Add selection-based clip box controls
This commit is contained in:
parent
18b530ef5f
commit
bbd72345c9
@ -14,6 +14,13 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
/// </summary>
|
||||
public class LogisticsControlViewModel : ViewModelBase
|
||||
{
|
||||
private enum ClipBoxFocusMode
|
||||
{
|
||||
None,
|
||||
Path,
|
||||
Selection
|
||||
}
|
||||
|
||||
#region 私有字段
|
||||
|
||||
private string _statusText;
|
||||
@ -144,6 +151,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
private bool _isClipBoxEnabled;
|
||||
private bool _isSelectionClipBoxEnabled;
|
||||
private bool _isUpdatingClipBoxState;
|
||||
private ClipBoxFocusMode _clipBoxFocusMode = ClipBoxFocusMode.None;
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用剖面盒(聚焦当前路径)
|
||||
@ -155,14 +165,45 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
{
|
||||
if (SetProperty(ref _isClipBoxEnabled, value))
|
||||
{
|
||||
if (_isUpdatingClipBoxState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
EnableClipBoxForCurrentRoute();
|
||||
ActivatePathClipBox();
|
||||
}
|
||||
else
|
||||
else if (_clipBoxFocusMode == ClipBoxFocusMode.Path)
|
||||
{
|
||||
SectionClipHelper.ClearClipBox();
|
||||
LogManager.Info("[剖面盒] 已关闭");
|
||||
ClearActiveClipBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 是否启用剖面盒(聚焦当前选择对象)
|
||||
/// </summary>
|
||||
public bool IsSelectionClipBoxEnabled
|
||||
{
|
||||
get => _isSelectionClipBoxEnabled;
|
||||
set
|
||||
{
|
||||
if (SetProperty(ref _isSelectionClipBoxEnabled, value))
|
||||
{
|
||||
if (_isUpdatingClipBoxState)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (value)
|
||||
{
|
||||
ActivateSelectionClipBox();
|
||||
}
|
||||
else if (_clipBoxFocusMode == ClipBoxFocusMode.Selection)
|
||||
{
|
||||
ClearActiveClipBox();
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -238,7 +279,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 用于剖面盒自动跟随路径切换
|
||||
_pathPlanningManager.CurrentRouteSwitched += (s, e) =>
|
||||
{
|
||||
if (IsClipBoxEnabled)
|
||||
if (_clipBoxFocusMode == ClipBoxFocusMode.Path)
|
||||
{
|
||||
if (_pathPlanningManager.PathEditState == PathEditState.Creating)
|
||||
{
|
||||
@ -252,6 +293,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
};
|
||||
}
|
||||
|
||||
if (NavisApplication.ActiveDocument?.CurrentSelection != null)
|
||||
{
|
||||
NavisApplication.ActiveDocument.CurrentSelection.Changed += OnCurrentSelectionChanged;
|
||||
}
|
||||
|
||||
// 初始化状态
|
||||
StatusText = "插件已就绪";
|
||||
|
||||
@ -333,6 +379,63 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
#region 剖面盒功能
|
||||
|
||||
private void ActivatePathClipBox()
|
||||
{
|
||||
SetClipBoxState(ClipBoxFocusMode.Path);
|
||||
EnableClipBoxForCurrentRoute();
|
||||
}
|
||||
|
||||
private void ActivateSelectionClipBox()
|
||||
{
|
||||
SetClipBoxState(ClipBoxFocusMode.Selection);
|
||||
EnableClipBoxForCurrentSelection();
|
||||
}
|
||||
|
||||
private void ClearActiveClipBox()
|
||||
{
|
||||
SetClipBoxState(ClipBoxFocusMode.None);
|
||||
SectionClipHelper.ClearClipBox();
|
||||
LogManager.Info("[剖面盒] 已关闭");
|
||||
}
|
||||
|
||||
private void SetClipBoxState(ClipBoxFocusMode mode)
|
||||
{
|
||||
_clipBoxFocusMode = mode;
|
||||
bool pathEnabled = mode == ClipBoxFocusMode.Path;
|
||||
bool selectionEnabled = mode == ClipBoxFocusMode.Selection;
|
||||
|
||||
_isUpdatingClipBoxState = true;
|
||||
try
|
||||
{
|
||||
if (_isClipBoxEnabled != pathEnabled)
|
||||
{
|
||||
_isClipBoxEnabled = pathEnabled;
|
||||
OnPropertyChanged(nameof(IsClipBoxEnabled));
|
||||
}
|
||||
|
||||
if (_isSelectionClipBoxEnabled != selectionEnabled)
|
||||
{
|
||||
_isSelectionClipBoxEnabled = selectionEnabled;
|
||||
OnPropertyChanged(nameof(IsSelectionClipBoxEnabled));
|
||||
}
|
||||
}
|
||||
finally
|
||||
{
|
||||
_isUpdatingClipBoxState = false;
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCurrentSelectionChanged(object sender, EventArgs e)
|
||||
{
|
||||
if (_clipBoxFocusMode != ClipBoxFocusMode.Selection)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
LogManager.Info("[剖面盒] 选择已变化,重新聚焦到当前选择对象");
|
||||
EnableClipBoxForCurrentSelection();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用剖面盒并聚焦到当前路径
|
||||
/// </summary>
|
||||
@ -344,7 +447,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
if (currentRoute?.Points == null || currentRoute.Points.Count == 0)
|
||||
{
|
||||
LogManager.Warning("[剖面盒] 当前没有路径,无法设置剖面盒");
|
||||
IsClipBoxEnabled = false;
|
||||
SectionClipHelper.ClearClipBox();
|
||||
SetClipBoxState(ClipBoxFocusMode.None);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -390,13 +494,59 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
else
|
||||
{
|
||||
LogManager.Warning("[剖面盒] 设置失败");
|
||||
IsClipBoxEnabled = false;
|
||||
SectionClipHelper.ClearClipBox();
|
||||
SetClipBoxState(ClipBoxFocusMode.None);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[剖面盒] 启用失败: {ex.Message}");
|
||||
IsClipBoxEnabled = false;
|
||||
SectionClipHelper.ClearClipBox();
|
||||
SetClipBoxState(ClipBoxFocusMode.None);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 启用剖面盒并聚焦到当前选择对象
|
||||
/// </summary>
|
||||
private void EnableClipBoxForCurrentSelection()
|
||||
{
|
||||
try
|
||||
{
|
||||
var document = NavisApplication.ActiveDocument;
|
||||
var selectedItems = document?.CurrentSelection?.SelectedItems?.Cast<ModelItem>().Where(item => item != null).ToList();
|
||||
if (selectedItems == null || selectedItems.Count == 0)
|
||||
{
|
||||
LogManager.Warning("[剖面盒] 当前没有选中的模型对象,无法设置剖面盒");
|
||||
SectionClipHelper.ClearClipBox();
|
||||
SetClipBoxState(ClipBoxFocusMode.None);
|
||||
return;
|
||||
}
|
||||
|
||||
SectionClipHelper.ClearClipBox();
|
||||
|
||||
bool success = SectionClipHelper.SetClipBoxByModelItems(
|
||||
selectedItems,
|
||||
marginInMeters: 2.0,
|
||||
heightMarginInMeters: 2.0);
|
||||
|
||||
if (success)
|
||||
{
|
||||
ViewpointHelper.FocusOnModelItems(selectedItems, ViewpointHelper.ViewpointStrategy.ModelFocus);
|
||||
LogManager.Info($"[剖面盒] 已按当前选择对象启用,选择数量: {selectedItems.Count}");
|
||||
}
|
||||
else
|
||||
{
|
||||
LogManager.Warning("[剖面盒] 按选择对象设置失败");
|
||||
SectionClipHelper.ClearClipBox();
|
||||
SetClipBoxState(ClipBoxFocusMode.None);
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[剖面盒] 按选择对象启用失败: {ex.Message}");
|
||||
SectionClipHelper.ClearClipBox();
|
||||
SetClipBoxState(ClipBoxFocusMode.None);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -146,18 +146,48 @@ NavisworksTransport 主控制面板 - 采用与其他视图一致的Navisworks 2
|
||||
Margin="8,0,6,0"
|
||||
Foreground="#FFCCCCCC"
|
||||
FontSize="14"/>
|
||||
|
||||
<!-- 剖面盒开关 -->
|
||||
|
||||
<!-- 选择对象剖面盒开关 -->
|
||||
<ToggleButton IsChecked="{Binding IsSelectionClipBoxEnabled}"
|
||||
ToolTip="剖面盒(聚焦当前选择对象)"
|
||||
Width="28" Height="24"
|
||||
Margin="2,0">
|
||||
<Viewbox Width="18" Height="18" Stretch="Uniform">
|
||||
<Canvas Width="20" Height="20">
|
||||
<Path Data="M 2,6 L 14,6 L 14,18 L 2,18 Z M 2,6 L 6,2 L 18,2 L 14,6 M 14,18 L 18,14 L 18,2 L 14,6"
|
||||
Stroke="{Binding IsSelectionClipBoxEnabled, Converter={StaticResource BoolToBrushConverter}, ConverterParameter=Green}"
|
||||
StrokeThickness="1.3"
|
||||
Fill="Transparent"/>
|
||||
<Rectangle Canvas.Left="6"
|
||||
Canvas.Top="10"
|
||||
Width="4"
|
||||
Height="4"
|
||||
Stroke="{Binding IsSelectionClipBoxEnabled, Converter={StaticResource BoolToBrushConverter}, ConverterParameter=Green}"
|
||||
StrokeThickness="1.2"
|
||||
Fill="Transparent"/>
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</ToggleButton>
|
||||
|
||||
<!-- 路径剖面盒开关 -->
|
||||
<ToggleButton IsChecked="{Binding IsClipBoxEnabled}"
|
||||
ToolTip="剖面盒(聚焦当前路径)"
|
||||
Width="28" Height="24"
|
||||
Margin="2,0">
|
||||
<Path Data="M 2,6 L 14,6 L 14,18 L 2,18 Z M 2,6 L 6,2 L 18,2 L 14,6 M 14,18 L 18,14 L 18,2 L 14,6"
|
||||
Stroke="{Binding IsClipBoxEnabled, Converter={StaticResource BoolToBrushConverter}, ConverterParameter=Red}"
|
||||
StrokeThickness="1.5"
|
||||
Fill="Transparent"
|
||||
Stretch="Uniform"
|
||||
Margin="2"/>
|
||||
<Viewbox Width="18" Height="18" Stretch="Uniform">
|
||||
<Canvas Width="20" Height="20">
|
||||
<Path Data="M 2,6 L 14,6 L 14,18 L 2,18 Z M 2,6 L 6,2 L 18,2 L 14,6 M 14,18 L 18,14 L 18,2 L 14,6"
|
||||
Stroke="{Binding IsClipBoxEnabled, Converter={StaticResource BoolToBrushConverter}, ConverterParameter=Green}"
|
||||
StrokeThickness="1.3"
|
||||
Fill="Transparent"/>
|
||||
<Polyline Points="5.5,13 7.2,11 8.1,12 10.5,10"
|
||||
Stroke="{Binding IsClipBoxEnabled, Converter={StaticResource BoolToBrushConverter}, ConverterParameter=Green}"
|
||||
StrokeThickness="1.2"
|
||||
StrokeLineJoin="Round"
|
||||
StrokeStartLineCap="Round"
|
||||
StrokeEndLineCap="Round"/>
|
||||
</Canvas>
|
||||
</Viewbox>
|
||||
</ToggleButton>
|
||||
</StackPanel>
|
||||
|
||||
|
||||
@ -1,5 +1,6 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
using Autodesk.Navisworks.Api;
|
||||
|
||||
namespace NavisworksTransport.Utils
|
||||
@ -131,6 +132,78 @@ namespace NavisworksTransport.Utils
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据模型项集合设置剖面盒
|
||||
/// </summary>
|
||||
/// <param name="modelItems">模型项集合</param>
|
||||
/// <param name="marginInMeters">水平边距(米)</param>
|
||||
/// <param name="heightMarginInMeters">高度边距(米,上下各延伸)</param>
|
||||
/// <returns>是否成功设置</returns>
|
||||
public static bool SetClipBoxByModelItems(
|
||||
IEnumerable<ModelItem> modelItems,
|
||||
double marginInMeters = 2.0,
|
||||
double heightMarginInMeters = 2.0)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (modelItems == null)
|
||||
{
|
||||
LogManager.Warning("[剖面盒] 模型项集合为空");
|
||||
return false;
|
||||
}
|
||||
|
||||
var validItems = modelItems.Where(item => item != null).ToList();
|
||||
if (validItems.Count == 0)
|
||||
{
|
||||
LogManager.Warning("[剖面盒] 没有可用的模型项,无法设置剖面盒");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool hasBounds = false;
|
||||
double minX = double.MaxValue, minY = double.MaxValue, minZ = double.MaxValue;
|
||||
double maxX = double.MinValue, maxY = double.MinValue, maxZ = double.MinValue;
|
||||
|
||||
foreach (var item in validItems)
|
||||
{
|
||||
BoundingBox3D bounds = item.BoundingBox();
|
||||
minX = Math.Min(minX, bounds.Min.X);
|
||||
minY = Math.Min(minY, bounds.Min.Y);
|
||||
minZ = Math.Min(minZ, bounds.Min.Z);
|
||||
maxX = Math.Max(maxX, bounds.Max.X);
|
||||
maxY = Math.Max(maxY, bounds.Max.Y);
|
||||
maxZ = Math.Max(maxZ, bounds.Max.Z);
|
||||
hasBounds = true;
|
||||
}
|
||||
|
||||
if (!hasBounds)
|
||||
{
|
||||
LogManager.Warning("[剖面盒] 未能从模型项中计算有效包围盒");
|
||||
return false;
|
||||
}
|
||||
|
||||
double metersToUnits = UnitsConverter.GetMetersToUnitsConversionFactor(Application.ActiveDocument.Units);
|
||||
double margin = marginInMeters * metersToUnits;
|
||||
double heightMargin = heightMarginInMeters * metersToUnits;
|
||||
|
||||
var selectionBounds = new BoundingBox3D(
|
||||
new Point3D(minX, minY, minZ),
|
||||
new Point3D(maxX, maxY, maxZ));
|
||||
var clipBox = ExpandBoundingBox(selectionBounds, margin, heightMargin);
|
||||
|
||||
ApplyClipBox(clipBox);
|
||||
|
||||
LogManager.Info($"[剖面盒] 已按选择对象设置 - 对象数: {validItems.Count}, 水平边距: {marginInMeters}m, 高度边距: {heightMarginInMeters}m, " +
|
||||
$"范围: X[{clipBox.Min.X:F2}, {clipBox.Max.X:F2}], Y[{clipBox.Min.Y:F2}, {clipBox.Max.Y:F2}], Z[{clipBox.Min.Z:F2}, {clipBox.Max.Z:F2}]");
|
||||
|
||||
return true;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[剖面盒] 按选择对象设置失败: {ex.Message}");
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 清除剖面盒(显示全部模型)
|
||||
/// 使用 JSON 字符串方式设置 Enabled = false
|
||||
|
||||
@ -689,6 +689,36 @@ namespace NavisworksTransport.Utils
|
||||
FocusOnModelItem(modelItem, profile.ViewAngleDegrees, profile.TargetViewRatio);
|
||||
}
|
||||
|
||||
public static void FocusOnModelItems(IEnumerable<ModelItem> modelItems, ViewpointStrategy strategy)
|
||||
{
|
||||
if (modelItems == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(modelItems));
|
||||
}
|
||||
|
||||
List<ModelItem> items = modelItems.Where(item => item != null).ToList();
|
||||
if (items.Count == 0)
|
||||
{
|
||||
throw new ArgumentException("至少需要一个模型元素", nameof(modelItems));
|
||||
}
|
||||
|
||||
BoundingBox3D combinedBounds = items[0].BoundingBox();
|
||||
for (int i = 1; i < items.Count; i++)
|
||||
{
|
||||
BoundingBox3D itemBounds = items[i].BoundingBox();
|
||||
combinedBounds.Extend(itemBounds.Min);
|
||||
combinedBounds.Extend(itemBounds.Max);
|
||||
}
|
||||
|
||||
Point3D center = new Point3D(
|
||||
(combinedBounds.Min.X + combinedBounds.Max.X) / 2.0,
|
||||
(combinedBounds.Min.Y + combinedBounds.Max.Y) / 2.0,
|
||||
(combinedBounds.Min.Z + combinedBounds.Max.Z) / 2.0);
|
||||
double targetSize = GetMaxDimension(combinedBounds);
|
||||
|
||||
FocusOnPosition(center, targetSize, strategy);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 聚焦到多个模型元素(如碰撞中的两个对象)
|
||||
/// </summary>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user