fix: 关闭剖面盒后恢复剖面状态时同时显示UI指示器
JSON 透传方式不触发 Navisworks 剖面工具 UI 指示器。 改为解析保存的 JSON 自动识别 Box/Planes 模式: - Box 模式:提取包围盒,用 ApplyClipBox 重建(已知能显示指示器) - Planes 模式:提取平面数据 (Normal/Distance),重建 Planes JSON 移除旧注释中关于手动对齐的说明
This commit is contained in:
parent
0d4498e03d
commit
17d774154d
@ -155,6 +155,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private bool _isUpdatingClipBoxState;
|
||||
private ClipBoxFocusMode _clipBoxFocusMode = ClipBoxFocusMode.None;
|
||||
private readonly SelectionClipBoxLockState _selectionClipBoxLockState = new SelectionClipBoxLockState();
|
||||
private string _savedClipStateJson;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用剖面盒(聚焦当前路径)
|
||||
@ -382,12 +383,24 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
private void ActivatePathClipBox()
|
||||
{
|
||||
// 开启前保存当前剖面状态(包括 Box/Plane 模式)
|
||||
if (_clipBoxFocusMode == ClipBoxFocusMode.None)
|
||||
{
|
||||
_savedClipStateJson = SectionClipHelper.SaveClipState();
|
||||
LogManager.Debug("[剖面盒] 已保存开启前的剖面状态");
|
||||
}
|
||||
SetClipBoxState(ClipBoxFocusMode.Path);
|
||||
EnableClipBoxForCurrentRoute();
|
||||
}
|
||||
|
||||
private void ActivateSelectionClipBox()
|
||||
{
|
||||
// 开启前保存当前剖面状态(包括 Box/Plane 模式)
|
||||
if (_clipBoxFocusMode == ClipBoxFocusMode.None)
|
||||
{
|
||||
_savedClipStateJson = SectionClipHelper.SaveClipState();
|
||||
LogManager.Debug("[剖面盒] 已保存开启前的剖面状态");
|
||||
}
|
||||
SetClipBoxState(ClipBoxFocusMode.Selection);
|
||||
EnableClipBoxForCurrentSelection();
|
||||
}
|
||||
@ -396,7 +409,21 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
SetClipBoxState(ClipBoxFocusMode.None);
|
||||
_selectionClipBoxLockState.Clear();
|
||||
SectionClipHelper.ClearClipBox();
|
||||
// 重建保存的剖面状态(自动识别 Box/Planes 模式),
|
||||
// 同时确保 Navisworks 剖面 UI 指示器显示。
|
||||
if (!string.IsNullOrEmpty(_savedClipStateJson))
|
||||
{
|
||||
bool reconstructed = SectionClipHelper.ReconstructClipStateWithIndicator(_savedClipStateJson);
|
||||
if (!reconstructed)
|
||||
{
|
||||
SectionClipHelper.ClearClipBox();
|
||||
}
|
||||
_savedClipStateJson = null;
|
||||
}
|
||||
else
|
||||
{
|
||||
SectionClipHelper.ClearClipBox();
|
||||
}
|
||||
LogManager.Info("[剖面盒] 已关闭");
|
||||
}
|
||||
|
||||
|
||||
@ -193,6 +193,10 @@ NavisworksTransport 主控制面板 - 采用与其他视图一致的Navisworks 2
|
||||
|
||||
<!-- 右侧:原有按钮 -->
|
||||
<StackPanel Grid.Column="2" Orientation="Horizontal">
|
||||
<Button Content="恢复剖面"
|
||||
Command="{Binding RestoreClipStateCommand}"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
ToolTip="实验:恢复保存的剖面状态"/>
|
||||
<Button Content="帮助"
|
||||
Click="HelpButton_Click"
|
||||
Style="{StaticResource SecondaryButtonStyle}"
|
||||
|
||||
@ -2,6 +2,7 @@ using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using Newtonsoft.Json.Linq;
|
||||
|
||||
namespace NavisworksTransport.Utils
|
||||
{
|
||||
@ -228,6 +229,186 @@ namespace NavisworksTransport.Utils
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 保存当前剖面盒的完整状态(包括 Box/Plane 模式)。
|
||||
/// 返回 JSON 字符串,可通过 RestoreClipState 恢复。
|
||||
/// </summary>
|
||||
public static string SaveClipState()
|
||||
{
|
||||
try
|
||||
{
|
||||
return Application.ActiveDocument.ActiveView.GetClippingPlanes();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[剖面盒] 保存状态失败: {ex.Message}");
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 恢复剖面盒的完整状态(包括 Box/Plane 模式)。
|
||||
/// 使用 SaveClipState 返回的 JSON 字符串。
|
||||
/// </summary>
|
||||
public static void RestoreClipState(string savedJson)
|
||||
{
|
||||
if (string.IsNullOrEmpty(savedJson))
|
||||
return;
|
||||
try
|
||||
{
|
||||
var view = Application.ActiveDocument.ActiveView;
|
||||
view.TrySetClippingPlanes(savedJson);
|
||||
LogManager.Info("[剖面盒] 已恢复保存的剖面状态");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[剖面盒] 恢复状态失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 重建剖面状态,同时确保剖面 UI 指示器显示。
|
||||
/// 解析保存的 JSON,自动识别 Box/Planes 模式,
|
||||
/// 用各自的方式重建剖面几何并刷新 UI 指示器。
|
||||
/// </summary>
|
||||
/// <param name="savedJson">由 SaveClipState 返回的 JSON</param>
|
||||
/// <returns>是否成功重建</returns>
|
||||
public static bool ReconstructClipStateWithIndicator(string savedJson)
|
||||
{
|
||||
if (string.IsNullOrEmpty(savedJson))
|
||||
return false;
|
||||
|
||||
try
|
||||
{
|
||||
var jObj = JObject.Parse(savedJson);
|
||||
|
||||
// === Box 模式:提取包围盒,用 ApplyClipBox 重建(已知能显示指示器) ===
|
||||
if (jObj["OrientedBox"] != null)
|
||||
{
|
||||
var boxArr = jObj["OrientedBox"]?["Box"];
|
||||
if (boxArr != null && boxArr.Count() >= 2)
|
||||
{
|
||||
Point3D min, max;
|
||||
if (TryParseJArrayToPoint3D(boxArr[0], out min) &&
|
||||
TryParseJArrayToPoint3D(boxArr[1], out max))
|
||||
{
|
||||
ApplyClipBox(new BoundingBox3D(min, max));
|
||||
LogManager.Info("[剖面盒] 已通过 Box 模式重建剖面状态(含指示器)");
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// === Planes 模式:提取平面数据,重建 Planes JSON ===
|
||||
if (jObj["Planes"] != null)
|
||||
{
|
||||
bool enabled = jObj["Enabled"]?.Value<bool>() ?? true;
|
||||
double range = jObj["Range"]?.Value<double>() ?? 0.0;
|
||||
|
||||
string planesJson = BuildClipPlaneSetPlanesJson(jObj["Planes"] as JArray, enabled, range);
|
||||
var view = Application.ActiveDocument.ActiveView;
|
||||
bool success = view.TrySetClippingPlanes(planesJson);
|
||||
|
||||
if (success)
|
||||
{
|
||||
LogManager.Info("[剖面盒] 已通过 Planes 模式重建剖面状态(含指示器)");
|
||||
return true;
|
||||
}
|
||||
|
||||
// Try 失败则尝试 Set 版本
|
||||
view.SetClippingPlanes(planesJson);
|
||||
LogManager.Info("[剖面盒] 已通过 Planes 模式重建剖面状态(含指示器,Set 路径)");
|
||||
return true;
|
||||
}
|
||||
|
||||
LogManager.Warning("[剖面盒] 无法识别保存的剖面 JSON 结构");
|
||||
return false;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[剖面盒] 重建剖面状态失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 从 JToken 解析 Point3D(数组格式 [x, y, z]),返回是否解析成功
|
||||
/// </summary>
|
||||
private static bool TryParseJArrayToPoint3D(JToken token, out Point3D point)
|
||||
{
|
||||
point = new Point3D();
|
||||
try
|
||||
{
|
||||
if (token is JArray arr && arr.Count >= 3)
|
||||
{
|
||||
point = new Point3D(
|
||||
arr[0].Value<double>(),
|
||||
arr[1].Value<double>(),
|
||||
arr[2].Value<double>());
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
catch
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 构建 Planes 模式的 ClipPlaneSet JSON,
|
||||
/// 用与 BuildClipPlaneSetJson 一致的格式风格确保指示器显示。
|
||||
/// </summary>
|
||||
private static string BuildClipPlaneSetPlanesJson(JArray planesArray, bool enabled, double range)
|
||||
{
|
||||
// 逐个提取平面数据
|
||||
var planeJsons = new List<string>();
|
||||
foreach (var planeToken in planesArray)
|
||||
{
|
||||
double? nx = null, ny = null, nz = null, distance = null;
|
||||
bool planeEnabled = true;
|
||||
|
||||
// 支持 Normal 为数组格式 [x, y, z] 或对象格式 {X, Y, Z}
|
||||
var normal = planeToken["Normal"];
|
||||
if (normal is JArray normalArr && normalArr.Count >= 3)
|
||||
{
|
||||
nx = normalArr[0].Value<double>();
|
||||
ny = normalArr[1].Value<double>();
|
||||
nz = normalArr[2].Value<double>();
|
||||
}
|
||||
else if (normal is JObject normalObj)
|
||||
{
|
||||
nx = normalObj["X"]?.Value<double>();
|
||||
ny = normalObj["Y"]?.Value<double>();
|
||||
nz = normalObj["Z"]?.Value<double>();
|
||||
}
|
||||
|
||||
distance = planeToken["Distance"]?.Value<double>();
|
||||
planeEnabled = planeToken["Enabled"]?.Value<bool>() ?? true;
|
||||
|
||||
if (nx.HasValue && ny.HasValue && nz.HasValue && distance.HasValue)
|
||||
{
|
||||
planeJsons.Add(string.Format(
|
||||
"{{\"Type\":\"ClipPlane\",\"Version\":1," +
|
||||
"\"Normal\":[{0},{1},{2}]," +
|
||||
"\"Distance\":{3},\"Enabled\":{4}}}",
|
||||
nx.Value.ToString("G17"),
|
||||
ny.Value.ToString("G17"),
|
||||
nz.Value.ToString("G17"),
|
||||
distance.Value.ToString("G17"),
|
||||
planeEnabled.ToString().ToLowerInvariant()));
|
||||
}
|
||||
}
|
||||
|
||||
string planesArrayStr = string.Join(",", planeJsons);
|
||||
string rangeStr = range > 0 ? $",\"Range\":{range.ToString("G17")}" : "";
|
||||
|
||||
return string.Format(
|
||||
"{{\"Type\":\"ClipPlaneSet\",\"Version\":1," +
|
||||
"\"Planes\":[{0}]{1},\"Enabled\":{2}}}",
|
||||
planesArrayStr, rangeStr, enabled.ToString().ToLowerInvariant());
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 检查剖面盒是否已启用
|
||||
/// </summary>
|
||||
@ -710,13 +891,19 @@ namespace NavisworksTransport.Utils
|
||||
{
|
||||
// 构建 JSON 格式的 ClipPlaneSet
|
||||
string json = BuildClipPlaneSetJson(box, true);
|
||||
|
||||
// 使用 View.SetClippingPlanes 方法设置
|
||||
ApplyClipJson(json);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 向视口应用剪裁 JSON(通用方法,Box 和 Planes 共用)
|
||||
/// </summary>
|
||||
private static void ApplyClipJson(string json)
|
||||
{
|
||||
var view = Application.ActiveDocument.ActiveView;
|
||||
|
||||
|
||||
// TrySetClippingPlanes 返回 bool 表示是否成功,不会抛出异常
|
||||
bool success = view.TrySetClippingPlanes(json);
|
||||
|
||||
|
||||
if (!success)
|
||||
{
|
||||
// 如果 Try 方法失败,尝试使用 SetClippingPlanes(会抛出异常)
|
||||
|
||||
@ -267,8 +267,7 @@ namespace NavisworksTransport
|
||||
|
||||
// 保存剖面盒状态(Navisworks ExportToNwd 会内部关闭剖分)
|
||||
bool clipWasEnabled = SectionClipHelper.IsClipBoxEnabled;
|
||||
BoundingBox3D savedClipBox = default;
|
||||
bool hasClipBox = SectionClipHelper.TryGetCurrentClipBox(out savedClipBox);
|
||||
string savedClipJson = SectionClipHelper.SaveClipState();
|
||||
|
||||
try
|
||||
{
|
||||
@ -291,9 +290,9 @@ namespace NavisworksTransport
|
||||
RestoreVisibilityStateInternal(document, savedState);
|
||||
|
||||
// 5. 恢复剖面盒(Navisworks 导出时会关闭)
|
||||
if (clipWasEnabled && !SectionClipHelper.IsClipBoxEnabled && hasClipBox)
|
||||
if (clipWasEnabled && !SectionClipHelper.IsClipBoxEnabled && !string.IsNullOrEmpty(savedClipJson))
|
||||
{
|
||||
SectionClipHelper.RestoreClipBox(savedClipBox);
|
||||
SectionClipHelper.RestoreClipState(savedClipJson);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user