diff --git a/CHANGELOG.md b/CHANGELOG.md index c79a372..f4a7640 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,24 @@ # Changelog +## [0.4.2] - 2026-09-06 + +### Added + +- Added a native CAD compatibility-report export action at the bottom of the existing right-side Properties pane. +- Added a privacy-safe JSON report generated from the loaded reader-independent `CadDocument`, including app/kernel/adapter versions, safe aggregate reader metadata, structural custom-object groups, proxy primitive coverage and raw proxy-command diagnostics when available. +- Reports are saved to `Desktop/SpatialViewer Diagnostics` with a LocalAppData fallback, and the resulting path is copied to the clipboard. +- Added Simplified Chinese, Japanese and English UI feedback for report availability, success and failure states. + +### Privacy and integration + +- The report uses an explicit allow-list and does not export the drawing filename/path, entity or target handles, layer names, coordinates, raw property values, colors, text content, raw DWG/DXF bytes or complete entity metadata. +- SpatialViewer v0.4.2 is released against the latest stable compatible SpatialViewer.CadCore; v0.12.10 supplies the raw ObjectARX proxy-command inventory used by the new report. + +### Preserved + +- CAD rendering behavior, toolbar geometry, layer/properties pane layout, title bar, tabs, NavigationView, Projects, Favorites, Rhino 3DM rendering and the v0.4.1 responsiveness fixes remain unchanged. + + ## [0.4.1] - 2026-09-06 ### Fixed diff --git a/docs/RELEASE-NOTES-v0.4.2.en.md b/docs/RELEASE-NOTES-v0.4.2.en.md new file mode 100644 index 0000000..81870eb --- /dev/null +++ b/docs/RELEASE-NOTES-v0.4.2.en.md @@ -0,0 +1,26 @@ +# SpatialViewer v0.4.2 + +[简体中文](RELEASE-NOTES-v0.4.2.md) | [日本語](RELEASE-NOTES-v0.4.2.ja.md) | English + +v0.4.2 completes the formal CAD-kernel diagnostics integration and connects the previously prepared CAD compatibility-report exporter to the existing viewer UI. + +## CAD compatibility report + +- Adds **Export CAD compatibility report** at the bottom of the existing right-side Properties pane, preserving the current WinUI 3 controls, pane widths, toolbar geometry and canvas layout. +- Generates privacy-safe JSON directly from the loaded reader-independent `CadDocument`, including application/CadCore/adapter versions, allow-listed aggregate document metadata, structural custom-entity grouping, Proxy Graphics coverage, and the raw proxy-command structural diagnostics supplied by CadCore v0.12.10. +- Saves to `Desktop/SpatialViewer Diagnostics` by default with a LocalAppData fallback. +- Copies the exported path to the clipboard and reuses the existing InfoBar for success/failure feedback. + +## Privacy boundary + +The exporter uses an explicit allow-list. It does not serialize the drawing filename/path, entity or target handles, layer names, coordinates, raw property values, colors, text content, raw DWG/DXF bytes, or complete entity metadata. + +Unknown raw proxy commands remain structural evidence only and are not promoted to Xiangyuan or any other proprietary semantics. + +## Kernel + +The v0.4.2 release pipeline continues to resolve and embed the latest stable compatible CadCore through the independent Host Contract. The corresponding formal kernel release is SpatialViewer.CadCore v0.12.10, with CLR ABI `1.0.0.0` unchanged. + +## Preserved + +Existing CAD rendering, CAD toolbar geometry, responsive left/right pane behavior, title bar, tabs, NavigationView, Projects/Favorites, and the Rhino 3DM rendering/responsiveness fixes from v0.4.1 remain unchanged. diff --git a/docs/RELEASE-NOTES-v0.4.2.ja.md b/docs/RELEASE-NOTES-v0.4.2.ja.md new file mode 100644 index 0000000..67b743f --- /dev/null +++ b/docs/RELEASE-NOTES-v0.4.2.ja.md @@ -0,0 +1,26 @@ +# SpatialViewer v0.4.2 + +[简体中文](RELEASE-NOTES-v0.4.2.md) | 日本語 | [English](RELEASE-NOTES-v0.4.2.en.md) + +v0.4.2 では CAD カーネル診断チェーンを正式リリースに接続し、既に準備していた CAD 互換性レポート出力を既存のビューア UI に統合しました。 + +## CAD 互換性レポート + +- CAD ページ右側の「プロパティ」サイドバー下部に「CAD 互換性レポートを出力」を追加しました。既存の WinUI 3 コントロール、サイドバー幅、レイアウト規則を維持し、ツールバーやキャンバス配置は変更しません。 +- 現在読み込み済みの reader-independent `CadDocument` から JSON を生成し、アプリ/CadCore/adapter のバージョン、安全な集計 metadata、custom entity の構造分類、Proxy Graphics coverage、v0.12.10 の raw proxy-command structural diagnostics を含めます。 +- 既定ではデスクトップの `SpatialViewer Diagnostics` に保存し、利用できない場合は LocalAppData にフォールバックします。 +- 成功時は出力パスをクリップボードへコピーし、既存の InfoBar で結果を通知します。 + +## プライバシー境界 + +明示的な allow-list を使用し、図面ファイル名/パス、entity/target handle、layer 名、座標、raw property value、色、テキスト、raw DWG/DXF bytes、完全な entity metadata は出力しません。 + +unknown proxy command は構造証拠としてのみ扱い、湘源などの proprietary semantic を推測しません。 + +## カーネル + +v0.4.2 の正式リリースは独立 Host Contract に基づいて最新の安定 CadCore を解決・同梱します。対応する正式カーネルは SpatialViewer.CadCore v0.12.10、CLR ABI は引き続き `1.0.0.0` です。 + +## 維持事項 + +既存の CAD レンダリング、CAD ツールバー形状、左右サイドバーのレスポンシブ規則、タイトルバー、タブ、NavigationView、Projects/Favorites、および v0.4.1 の Rhino 3DM 表示・応答性修正は変更しません。 diff --git a/docs/RELEASE-NOTES-v0.4.2.md b/docs/RELEASE-NOTES-v0.4.2.md new file mode 100644 index 0000000..c3ef15e --- /dev/null +++ b/docs/RELEASE-NOTES-v0.4.2.md @@ -0,0 +1,31 @@ +# SpatialViewer v0.4.2 + +简体中文 | [日本語](RELEASE-NOTES-v0.4.2.ja.md) | [English](RELEASE-NOTES-v0.4.2.en.md) + +v0.4.2 完成 CAD 内核诊断链的正式发布接入,并把已经起草的 CAD 兼容性报告导出能力接到现有看图 UI。 + +## CAD 兼容性报告 + +- 在 CAD 页面右侧“属性”侧栏底部新增“导出 CAD 兼容性报告”入口,沿用现有 WinUI 3 控件、侧栏宽度和交互层级,不改变工具栏及画布布局。 +- 报告直接从当前已载入的 reader-independent `CadDocument` 生成 JSON,包含: + - SpatialViewer / CadCore / CAD adapter 版本; + - 安全的文档级汇总元数据; + - custom entity 的结构类别、vendor 与 representation; + - Proxy Graphics primitive 覆盖与 translated / unsupported 汇总; + - CadCore v0.12.10 提供的 raw proxy-command structural signature、unknown type IDs 与 malformed / truncated 汇总。 +- 默认保存到桌面的 `SpatialViewer Diagnostics` 文件夹;桌面路径不可用时回退到 LocalAppData。 +- 导出成功后自动复制报告路径,并通过现有 InfoBar 显示成功/失败状态。 + +## 隐私边界 + +兼容性报告采用显式 allow-list,不导出源图纸文件名/路径、entity handle、target handle、图层名、坐标、原始属性值、颜色、文字内容、raw DWG/DXF bytes 或完整 entity metadata。 + +raw proxy-command 仅输出结构证据,不把 unknown command 猜测为湘源或其他 proprietary semantic。 + +## 内核 + +v0.4.2 的正式发布流程继续按照独立 Host Contract 解析并嵌入最新稳定 CadCore。对应正式内核为 SpatialViewer.CadCore v0.12.10,CLR ABI 保持 `1.0.0.0`。 + +## 保持不变 + +本次不修改既有 CAD 渲染结果、CAD 工具栏几何、左右侧栏响应式规则、标题栏、标签页、NavigationView、项目/收藏,也不回退 v0.4.1 已完成的 Rhino 3DM 黑屏和响应性修复。 diff --git a/external/SpatialViewer.CadCore b/external/SpatialViewer.CadCore index a62bf36..873c631 160000 --- a/external/SpatialViewer.CadCore +++ b/external/SpatialViewer.CadCore @@ -1 +1 @@ -Subproject commit a62bf36a716c26cd184376a3733d745fb869e840 +Subproject commit 873c63143c55c63f23f33ab5a7cd1e22b5abdd75 diff --git a/release/release.json b/release/release.json index 582df16..5e5cc5f 100644 --- a/release/release.json +++ b/release/release.json @@ -2,8 +2,8 @@ "schemaVersion": 1, "product": { "name": "SpatialViewer", - "version": "0.4.1", - "packageVersion": "0.4.1.0" + "version": "0.4.2", + "packageVersion": "0.4.2.0" }, "channels": { "github": { @@ -11,8 +11,8 @@ } }, "title": { - "zh-CN": "SpatialViewer v0.4.1 Rhino 显示与响应性热修", - "ja-JP": "SpatialViewer v0.4.1 Rhino 表示・応答性ホットフィックス", - "en-US": "SpatialViewer v0.4.1 Rhino Rendering and Responsiveness Hotfix" + "zh-CN": "SpatialViewer v0.4.2 CAD 兼容性报告与内核诊断", + "ja-JP": "SpatialViewer v0.4.2 CAD 互換性レポートとカーネル診断", + "en-US": "SpatialViewer v0.4.2 CAD Compatibility Report and Kernel Diagnostics" } } diff --git a/src/SpatialViewer.App/AppVersionProvider.cs b/src/SpatialViewer.App/AppVersionProvider.cs index a980db5..f839361 100644 --- a/src/SpatialViewer.App/AppVersionProvider.cs +++ b/src/SpatialViewer.App/AppVersionProvider.cs @@ -4,8 +4,8 @@ namespace SpatialViewer.Product; internal static class AppVersionProvider { - public const string Version = "0.4.1"; - public const string DisplayVersion = "v0.4.1"; + public const string Version = "0.4.2"; + public const string DisplayVersion = "v0.4.2"; public static Version GetCurrentVersion() { @@ -16,7 +16,7 @@ public static Version GetCurrentVersion() } catch (Exception) when (OperatingSystem.IsWindows()) { - var assemblyVersion = typeof(AppVersionProvider).Assembly.GetName().Version ?? new Version(0, 4, 1, 0); + var assemblyVersion = typeof(AppVersionProvider).Assembly.GetName().Version ?? new Version(0, 4, 2, 0); return new Version(assemblyVersion.Major, assemblyVersion.Minor, Math.Max(0, assemblyVersion.Build), Math.Max(0, assemblyVersion.Revision)); } } diff --git a/src/SpatialViewer.App/CadCompatibilityReportBuilder.cs b/src/SpatialViewer.App/CadCompatibilityReportBuilder.cs new file mode 100644 index 0000000..418d3e4 --- /dev/null +++ b/src/SpatialViewer.App/CadCompatibilityReportBuilder.cs @@ -0,0 +1,234 @@ +using System.Collections.ObjectModel; +using System.Globalization; +using System.Text.Json; +using SpatialViewer.Formats.Cad; +using SpatialViewer.Formats.Cad.ACadSharp; + +namespace SpatialViewer.Product; + +internal static class CadCompatibilityReportBuilder +{ + public const int SchemaVersion = 1; + + private static readonly string[] SafeDocumentMetadataKeys = + [ + "Reader", + "ReaderVersion", + "SourceFormat", + "CadVersion", + "Units", + "CustomClassCount", + "CustomEntityCount", + "CustomProxyGraphicEntityCount", + "XiangyuanDetected", + "XiangyuanClassCount", + "XiangyuanEntityCount", + "RawProxyCommandCaptureSupported", + "RawProxyCommandCaptureFailed", + "RawProxyCommandCapturedEntityCount", + "RawProxyCommandMalformedEntityCount", + "RawProxyUnknownCommandEntityCount", + "RawProxyUnknownCommandCount", + "RawProxyUnknownTypeIds" + ]; + + private static readonly JsonSerializerOptions JsonOptions = new(JsonSerializerDefaults.Web) + { + WriteIndented = true + }; + + public static string Build(CadDocument document) + { + ArgumentNullException.ThrowIfNull(document); + + var customEntities = EnumerateEntities(document) + .OfType() + .ToArray(); + + var groups = customEntities + .GroupBy(EntityGroupKey.From) + .OrderBy(group => group.Key.Vendor, StringComparer.Ordinal) + .ThenBy(group => group.Key.ApplicationName, StringComparer.OrdinalIgnoreCase) + .ThenBy(group => group.Key.CppClassName, StringComparer.OrdinalIgnoreCase) + .ThenBy(group => group.Key.DxfName, StringComparer.OrdinalIgnoreCase) + .ThenBy(group => group.Key.SourceEntityType, StringComparer.OrdinalIgnoreCase) + .Select(group => BuildGroup(group.Key, group.ToArray())) + .ToArray(); + + var report = new CadCompatibilityReport( + SchemaVersion, + DateTimeOffset.UtcNow, + AppVersionProvider.Version, + GetProductVersion(typeof(CadDocument)), + GetProductVersion(typeof(ACadSharpCadImporter)), + document.SourceFormat, + document.Version, + document.Units.ToString(), + document.CustomClasses.Count, + customEntities.Length, + FilterMetadata(document.Metadata, SafeDocumentMetadataKeys), + groups); + + return JsonSerializer.Serialize(report, JsonOptions); + } + + private static string GetProductVersion(Type type) + { + var fileVersion = System.Diagnostics.FileVersionInfo + .GetVersionInfo(type.Assembly.Location) + .FileVersion; + return string.IsNullOrWhiteSpace(fileVersion) + ? type.Assembly.GetName().Version?.ToString() ?? "unknown" + : fileVersion; + } + + private static CadCompatibilityCustomGroup BuildGroup( + EntityGroupKey key, + CadCustomEntity[] entities) + { + var primitiveKinds = entities + .SelectMany(entity => entity.ProxyGraphicKinds) + .Where(value => !string.IsNullOrWhiteSpace(value)) + .Distinct(StringComparer.Ordinal) + .OrderBy(value => value, StringComparer.Ordinal) + .ToArray(); + + var commandSignatures = DistinctMetadata(entities, "RawProxyCommandTypeSignature"); + var unknownTypeIds = entities + .SelectMany(entity => ParseIntegerList(Metadata(entity, "RawProxyUnknownTypeIds"))) + .Distinct() + .OrderBy(value => value) + .ToArray(); + + return new( + key.DxfName, + key.CppClassName, + key.ApplicationName, + key.SourceEntityType, + key.Vendor, + key.Representation, + entities.Length, + primitiveKinds, + SumMetadata(entities, "ProxyGraphicCount"), + SumMetadata(entities, "ProxyGraphicTranslatedCount"), + SumMetadata(entities, "ProxyGraphicUnsupportedCount"), + SumMetadata(entities, "RawProxyCommandDeclaredCount"), + SumMetadata(entities, "RawProxyCommandScannedCount"), + SumMetadata(entities, "RawProxyCommandKnownCount"), + SumMetadata(entities, "RawProxyCommandUnknownCount"), + entities.Count(entity => MetadataBoolean(entity, "RawProxyCommandMalformed")), + entities.Count(entity => MetadataBoolean(entity, "RawProxyCommandTruncated")), + unknownTypeIds, + commandSignatures); + } + + private static IEnumerable EnumerateEntities(CadDocument document) + { + foreach (var entity in document.ModelSpace) yield return entity; + foreach (var block in document.Blocks) + foreach (var entity in block.Entities) + yield return entity; + foreach (var layout in document.Layouts.Where(layout => layout.IsPaperSpace)) + foreach (var entity in layout.Entities) + yield return entity; + } + + private static ReadOnlyDictionary FilterMetadata( + IReadOnlyDictionary source, + IEnumerable allowList) + { + var safe = new SortedDictionary(StringComparer.Ordinal); + foreach (var key in allowList) + if (source.TryGetValue(key, out var value) && !string.IsNullOrWhiteSpace(value)) + safe[key] = value; + return new ReadOnlyDictionary(safe); + } + + private static string[] DistinctMetadata( + IEnumerable entities, + string key) + => entities + .Select(entity => Metadata(entity, key)) + .Where(value => !string.IsNullOrWhiteSpace(value)) + .Distinct(StringComparer.Ordinal) + .OrderBy(value => value, StringComparer.Ordinal) + .ToArray(); + + private static string Metadata(CadCustomEntity entity, string key) + => entity.Metadata.TryGetValue(key, out var value) ? value : string.Empty; + + private static bool MetadataBoolean(CadCustomEntity entity, string key) + => bool.TryParse(Metadata(entity, key), out var value) && value; + + private static long SumMetadata( + IEnumerable entities, + string key) + { + long sum = 0; + foreach (var entity in entities) + if (long.TryParse(Metadata(entity, key), NumberStyles.Integer, CultureInfo.InvariantCulture, out var value) && value > 0) + sum = checked(sum + value); + return sum; + } + + private static IEnumerable ParseIntegerList(string raw) + { + if (string.IsNullOrWhiteSpace(raw)) yield break; + foreach (var token in raw.Split(';', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries)) + if (int.TryParse(token, NumberStyles.Integer, CultureInfo.InvariantCulture, out var value)) + yield return value; + } + + private sealed record EntityGroupKey( + string DxfName, + string CppClassName, + string ApplicationName, + string SourceEntityType, + string Vendor, + string Representation) + { + public static EntityGroupKey From(CadCustomEntity entity) + => new( + entity.ClassDefinition?.DxfName ?? string.Empty, + entity.ClassDefinition?.CppClassName ?? string.Empty, + entity.ClassDefinition?.ApplicationName ?? string.Empty, + entity.SourceEntityType, + entity.Vendor.ToString(), + entity.Representation.ToString()); + } +} + +internal sealed record CadCompatibilityReport( + int SchemaVersion, + DateTimeOffset GeneratedUtc, + string AppVersion, + string CadCoreVersion, + string CadAdapterVersion, + string SourceFormat, + string CadVersion, + string Units, + int CustomClassCount, + int CustomEntityCount, + IReadOnlyDictionary AggregateMetadata, + IReadOnlyList CustomGroups); + +internal sealed record CadCompatibilityCustomGroup( + string DxfName, + string CppClassName, + string ApplicationName, + string SourceEntityType, + string Vendor, + string Representation, + int EntityCount, + IReadOnlyList ProxyGraphicKinds, + long ProxyGraphicCommandCount, + long ProxyGraphicTranslatedCount, + long ProxyGraphicUnsupportedCount, + long RawCommandDeclaredCount, + long RawCommandScannedCount, + long RawCommandKnownCount, + long RawCommandUnknownCount, + int RawCommandMalformedEntityCount, + int RawCommandTruncatedEntityCount, + IReadOnlyList RawUnknownTypeIds, + IReadOnlyList RawCommandTypeSignatures); diff --git a/src/SpatialViewer.App/Package.appxmanifest b/src/SpatialViewer.App/Package.appxmanifest index 4fbd110..3dd4e31 100644 --- a/src/SpatialViewer.App/Package.appxmanifest +++ b/src/SpatialViewer.App/Package.appxmanifest @@ -6,7 +6,7 @@ xmlns:uap5="http://schemas.microsoft.com/appx/manifest/uap/windows10/5" xmlns:rescap="http://schemas.microsoft.com/appx/manifest/foundation/windows10/restrictedcapabilities" IgnorableNamespaces="uap uap5 rescap"> - + Spatial Viewer diff --git a/src/SpatialViewer.App/SpatialViewer.App.csproj b/src/SpatialViewer.App/SpatialViewer.App.csproj index 07466c4..8b299b5 100644 --- a/src/SpatialViewer.App/SpatialViewer.App.csproj +++ b/src/SpatialViewer.App/SpatialViewer.App.csproj @@ -8,13 +8,13 @@ app.manifest Assets\AppIcon.ico x64 - 0.4.1 - 0.4.1.0 - 0.4.1.0 - 0.4.1 + 0.4.2 + 0.4.2.0 + 0.4.2.0 + 0.4.2 zh-CN Scale|DXFeatureLevel - 0.9.0 + 0.12.10 diff --git a/src/SpatialViewer.App/Strings/en-US/Resources.resw b/src/SpatialViewer.App/Strings/en-US/Resources.resw index 4387e68..b0ea4fb 100644 --- a/src/SpatialViewer.App/Strings/en-US/Resources.resw +++ b/src/SpatialViewer.App/Strings/en-US/Resources.resw @@ -192,4 +192,12 @@ Yes No Integrated + Export CAD compatibility report + Report is not available yet + The current drawing has not finished loading. + CAD compatibility report exported + Report saved to: {0}\nThe file path was copied to the clipboard. + Compatibility report path copied + CAD compatibility report export failed + The diagnostic report could not be written. Check local file permissions and try again. diff --git a/src/SpatialViewer.App/Strings/ja-JP/Resources.resw b/src/SpatialViewer.App/Strings/ja-JP/Resources.resw index ee6f815..e436f17 100644 --- a/src/SpatialViewer.App/Strings/ja-JP/Resources.resw +++ b/src/SpatialViewer.App/Strings/ja-JP/Resources.resw @@ -192,4 +192,12 @@ はい いいえ 統合済み + CAD 互換性レポートを出力 + レポートを出力できません + 現在の図面はまだ読み込みを完了していません。 + CAD 互換性レポートを出力しました + レポートを保存しました:{0}\nファイルパスをクリップボードにコピーしました。 + 互換性レポートのパスをコピーしました + CAD 互換性レポートの出力に失敗しました + 診断レポートを書き込めません。ローカルのファイル権限を確認して再試行してください。 diff --git a/src/SpatialViewer.App/Strings/zh-CN/Resources.resw b/src/SpatialViewer.App/Strings/zh-CN/Resources.resw index 3d71dee..a5d5039 100644 --- a/src/SpatialViewer.App/Strings/zh-CN/Resources.resw +++ b/src/SpatialViewer.App/Strings/zh-CN/Resources.resw @@ -192,4 +192,12 @@ 已集成 + 导出 CAD 兼容性报告 + 暂时无法导出报告 + 当前图纸尚未完成读取。 + CAD 兼容性报告已导出 + 报告已保存到:{0}\n文件路径已复制到剪贴板。 + 兼容性报告路径已复制 + CAD 兼容性报告导出失败 + 无法写入诊断报告,请检查本机文件权限后重试。 diff --git a/src/SpatialViewer.App/Views/CadViewerView.xaml b/src/SpatialViewer.App/Views/CadViewerView.xaml index 5ed2572..19af612 100644 --- a/src/SpatialViewer.App/Views/CadViewerView.xaml +++ b/src/SpatialViewer.App/Views/CadViewerView.xaml @@ -30,7 +30,13 @@ - + +