Add hoisting layer height editing
This commit is contained in:
parent
9be5d250e8
commit
7058e5fd23
@ -2,6 +2,7 @@ using System;
|
||||
using System.ComponentModel;
|
||||
using System.Runtime.CompilerServices;
|
||||
using Autodesk.Navisworks.Api;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Models
|
||||
{
|
||||
@ -53,4 +54,47 @@ namespace NavisworksTransport.UI.WPF.Models
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 吊装路径层高编辑项
|
||||
/// </summary>
|
||||
public class HoistingLayerEditItem : INotifyPropertyChanged
|
||||
{
|
||||
private int _displayIndex;
|
||||
private double _heightInMeters;
|
||||
private string _pointSummary;
|
||||
|
||||
public int DisplayIndex
|
||||
{
|
||||
get => _displayIndex;
|
||||
set { _displayIndex = value; OnPropertyChanged(nameof(DisplayIndex)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 相对起吊点的绝对高度(米)
|
||||
/// </summary>
|
||||
public double HeightInMeters
|
||||
{
|
||||
get => _heightInMeters;
|
||||
set { _heightInMeters = value; OnPropertyChanged(nameof(HeightInMeters)); }
|
||||
}
|
||||
|
||||
public string PointSummary
|
||||
{
|
||||
get => _pointSummary;
|
||||
set { _pointSummary = value; OnPropertyChanged(nameof(PointSummary)); }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 当前层关联的路径点 Id 列表
|
||||
/// </summary>
|
||||
public List<string> PointIds { get; } = new List<string>();
|
||||
|
||||
public event PropertyChangedEventHandler PropertyChanged;
|
||||
|
||||
protected virtual void OnPropertyChanged(string propertyName)
|
||||
{
|
||||
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
using System;
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.ObjectModel;
|
||||
using System.Drawing;
|
||||
@ -332,6 +332,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 多层吊装模式
|
||||
private bool _isMultiLevelHoistingMode = false;
|
||||
private ObservableCollection<HoistingLevelItem> _multiLevelItems = new ObservableCollection<HoistingLevelItem>();
|
||||
private ObservableCollection<HoistingLayerEditItem> _hoistingLayerEditItems = new ObservableCollection<HoistingLayerEditItem>();
|
||||
private Point3D _multiLevelStartPoint;
|
||||
private Point3D _multiLevelEndPoint;
|
||||
private bool _hasMultiLevelStartPoint = false;
|
||||
@ -339,6 +340,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private string _multiLevelStartPointText = "未选择";
|
||||
private string _multiLevelEndPointText = "未选择";
|
||||
private string _multiLevelStatsText = "层级数: 0 | 预估路径点数: 0";
|
||||
private string _hoistingLayerEditHintText = "选择一条吊装路径后,可在这里按层编辑相对起点的绝对高度。";
|
||||
private bool _isSelectingMultiLevelStartPoint = false;
|
||||
private bool _isSelectingMultiLevelEndPoint = false;
|
||||
private HoistingLevelItem _currentSelectingLevelTarget;
|
||||
@ -431,9 +433,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
// 🔥 路径类型改变时,检查是否可以使用路径线
|
||||
OnPropertyChanged(nameof(CanUsePathLines));
|
||||
OnPropertyChanged(nameof(IsRailRouteSelected));
|
||||
OnPropertyChanged(nameof(IsHoistingRouteSelected));
|
||||
OnPropertyChanged(nameof(SelectedRailMountMode));
|
||||
OnPropertyChanged(nameof(SelectedRailNormalOffsetInMeters));
|
||||
OnPropertyChanged(nameof(CanRepositionRailStartPoint));
|
||||
OnPropertyChanged(nameof(CanApplyHoistingLayerHeights));
|
||||
NotifyRailAssemblyCommandStateChanged();
|
||||
if (!CanUsePathLines && ShowPathLines)
|
||||
{
|
||||
@ -442,6 +446,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
LogManager.Info("[路径可视化] 切换到吊装/空轨路径,自动关闭路径线");
|
||||
}
|
||||
|
||||
RefreshHoistingLayerEditItemsFromSelectedRoute();
|
||||
|
||||
// 实现路径选择时的可视化切换
|
||||
UpdatePathVisualization();
|
||||
}
|
||||
@ -493,6 +499,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
public bool IsRailRouteSelected => SelectedPathRoute?.PathType == PathType.Rail;
|
||||
public bool IsHoistingRouteSelected => SelectedPathRoute?.PathType == PathType.Hoisting;
|
||||
|
||||
public ObservableCollection<RailConfigOption<RailMountMode>> RailMountModeOptions
|
||||
{
|
||||
@ -906,6 +913,18 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
set => SetProperty(ref _multiLevelItems, value);
|
||||
}
|
||||
|
||||
public ObservableCollection<HoistingLayerEditItem> HoistingLayerEditItems
|
||||
{
|
||||
get => _hoistingLayerEditItems;
|
||||
set => SetProperty(ref _hoistingLayerEditItems, value);
|
||||
}
|
||||
|
||||
public string HoistingLayerEditHintText
|
||||
{
|
||||
get => _hoistingLayerEditHintText;
|
||||
set => SetProperty(ref _hoistingLayerEditHintText, value);
|
||||
}
|
||||
|
||||
public string MultiLevelStartPointText
|
||||
{
|
||||
get => _multiLevelStartPointText;
|
||||
@ -925,6 +944,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
public bool CanCreateMultiLevelPath => _hasMultiLevelStartPoint && _hasMultiLevelEndPoint && _multiLevelItems.Count > 0;
|
||||
public bool CanApplyHoistingLayerHeights => IsHoistingRouteSelected &&
|
||||
SelectedPathRoute != null &&
|
||||
HoistingLayerEditItems.Count > 0;
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1326,6 +1348,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
public ICommand PickMultiLevelTargetCommand { get; private set; }
|
||||
public ICommand CreateMultiLevelPathCommand { get; private set; }
|
||||
public ICommand CancelMultiLevelHoistingCommand { get; private set; }
|
||||
public ICommand ApplyHoistingLayerHeightsCommand { get; private set; }
|
||||
|
||||
#endregion
|
||||
|
||||
@ -1977,6 +2000,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
AnalyzeAssemblyTerminalFaceForEditCommand = new RelayCommand(async () => await ExecuteAnalyzeAssemblyTerminalFaceAsync(RailAssemblyWorkflowMode.EditSelectedRail), () => CanAnalyzeAssemblyTerminalFaceForEdit);
|
||||
DecreaseSelectedRailNormalOffsetCommand = new RelayCommand(() => AdjustSelectedRailNormalOffset(-RailNormalOffsetNudgeStepInMeters));
|
||||
IncreaseSelectedRailNormalOffsetCommand = new RelayCommand(() => AdjustSelectedRailNormalOffset(RailNormalOffsetNudgeStepInMeters));
|
||||
ApplyHoistingLayerHeightsCommand = new RelayCommand(async () => await ExecuteApplyHoistingLayerHeightsAsync(), () => CanApplyHoistingLayerHeights);
|
||||
}
|
||||
|
||||
#endregion
|
||||
@ -2010,22 +2034,58 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
HasAssemblyTerminalObject = true;
|
||||
AssemblyStartPointText = "未选择";
|
||||
AssemblyTerminalObjectName = ModelItemAnalysisHelper.GetSafeDisplayName(selectedItem);
|
||||
bool isEditMode = IsRailAssemblyEditMode(mode);
|
||||
|
||||
ResetAssemblyEndFaceAnalysisState();
|
||||
ResetAssemblyInstallationReferenceState();
|
||||
if (!isEditMode)
|
||||
{
|
||||
ResetAssemblyInstallationReferenceState();
|
||||
}
|
||||
RefreshAssemblyTerminalObjectInfo();
|
||||
ClearAssemblyAnchorMarker();
|
||||
ClearAssemblyEndFaceAnalysisVisuals();
|
||||
ClearAssemblyInstallationReferenceVisuals();
|
||||
if (!isEditMode)
|
||||
{
|
||||
ClearAssemblyInstallationReferenceVisuals();
|
||||
}
|
||||
NotifyRailAssemblyCommandStateChanged();
|
||||
|
||||
// 新建路径时隐藏其他路径的辅助线
|
||||
ClearAssemblyReferenceLineVisuals();
|
||||
if (isEditMode)
|
||||
{
|
||||
// 编辑态重选箱体后,只恢复可由当前 Rail 路径和终点箱体稳定重建的可视化。
|
||||
// 安装参考面依赖安装拾取点、安装面 span 等会话态数据,这些数据当前未持久化到 PathRoute,
|
||||
// 因此这里不尝试恢复安装参考面,避免后续误以为“少了一次刷新调用”是 bug。
|
||||
RefreshSelectedPathVisualizationForAssemblyEdit();
|
||||
RefreshAssemblyReferenceRodIfNeeded();
|
||||
}
|
||||
else
|
||||
{
|
||||
// 新建路径时隐藏其他路径的辅助线
|
||||
ClearAssemblyReferenceLineVisuals();
|
||||
}
|
||||
|
||||
UpdateMainStatus($"已捕获终点箱体: {AssemblyTerminalObjectName}");
|
||||
LogManager.Info($"[直线装配] 已捕获终点箱体: {AssemblyTerminalObjectName}");
|
||||
}, "捕获终点箱体");
|
||||
}
|
||||
|
||||
private void RefreshSelectedPathVisualizationForAssemblyEdit()
|
||||
{
|
||||
if (_selectedPathRoute == null || _selectedPathRoute.Points.Count == 0 || _pathPlanningManager == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var coreRoute = _pathPlanningManager.Routes?.FirstOrDefault(route => route.Id == _selectedPathRoute.Id)
|
||||
?? _pathPlanningManager.Routes?.FirstOrDefault(route => route.Name == _selectedPathRoute.Name);
|
||||
if (coreRoute == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
_pathPlanningManager.DrawRouteVisualization(coreRoute, isAutoPath: false);
|
||||
}
|
||||
|
||||
private void ClearNonGridPathVisualizations(string context)
|
||||
{
|
||||
if (PathPointRenderPlugin.Instance == null)
|
||||
@ -4572,6 +4632,163 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
return true;
|
||||
}
|
||||
|
||||
private void RefreshHoistingLayerEditItemsFromSelectedRoute()
|
||||
{
|
||||
HoistingLayerEditItems.Clear();
|
||||
|
||||
var coreRoute = GetSelectedCoreRoute();
|
||||
if (coreRoute == null || coreRoute.PathType != PathType.Hoisting || coreRoute.Points == null || coreRoute.Points.Count < 2)
|
||||
{
|
||||
HoistingLayerEditHintText = "选择一条吊装路径后,可在这里按层编辑相对起点的绝对高度。";
|
||||
OnPropertyChanged(nameof(CanApplyHoistingLayerHeights));
|
||||
return;
|
||||
}
|
||||
|
||||
HostCoordinateAdapter hostAdapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
PathPoint hostStartPoint = coreRoute.Points.FirstOrDefault(point => point.Type == PathPointType.StartPoint) ?? coreRoute.Points.First();
|
||||
double hostStartElevation = HoistingCoordinateHelper.GetElevation(hostStartPoint.Position, hostAdapter);
|
||||
const double relativeHeightToleranceInMeters = 0.001;
|
||||
|
||||
var layerItems = new List<HoistingLayerEditItem>();
|
||||
foreach (var point in coreRoute.Points)
|
||||
{
|
||||
if (point == null || point.Type == PathPointType.StartPoint || point.Type == PathPointType.EndPoint)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
double hostPointElevation = HoistingCoordinateHelper.GetElevation(point.Position, hostAdapter);
|
||||
double relativeHeightInMeters = UnitsConverter.ConvertToMeters(hostPointElevation - hostStartElevation);
|
||||
if (relativeHeightInMeters <= relativeHeightToleranceInMeters)
|
||||
{
|
||||
continue;
|
||||
}
|
||||
|
||||
HoistingLayerEditItem existingLayer = layerItems.FirstOrDefault(item =>
|
||||
Math.Abs(item.HeightInMeters - relativeHeightInMeters) <= relativeHeightToleranceInMeters);
|
||||
if (existingLayer == null)
|
||||
{
|
||||
existingLayer = new HoistingLayerEditItem
|
||||
{
|
||||
HeightInMeters = Math.Round(relativeHeightInMeters, 3)
|
||||
};
|
||||
layerItems.Add(existingLayer);
|
||||
}
|
||||
|
||||
if (!string.IsNullOrWhiteSpace(point.Id) && !existingLayer.PointIds.Contains(point.Id))
|
||||
{
|
||||
existingLayer.PointIds.Add(point.Id);
|
||||
}
|
||||
}
|
||||
|
||||
List<HoistingLayerEditItem> orderedLayers = layerItems
|
||||
.OrderByDescending(item => item.HeightInMeters)
|
||||
.ToList();
|
||||
|
||||
for (int i = 0; i < orderedLayers.Count; i++)
|
||||
{
|
||||
orderedLayers[i].DisplayIndex = i + 1;
|
||||
orderedLayers[i].PointSummary = $"关联点 {orderedLayers[i].PointIds.Count} 个";
|
||||
HoistingLayerEditItems.Add(orderedLayers[i]);
|
||||
}
|
||||
|
||||
HoistingLayerEditHintText = HoistingLayerEditItems.Count > 0
|
||||
? "层高按高到低显示;每层高度相对起吊点,且必须严格低于上一层、高于下一层。"
|
||||
: "当前吊装路径未解析出可编辑的空中层。";
|
||||
OnPropertyChanged(nameof(CanApplyHoistingLayerHeights));
|
||||
}
|
||||
|
||||
private async Task ExecuteApplyHoistingLayerHeightsAsync()
|
||||
{
|
||||
await SafeExecuteAsync(() =>
|
||||
{
|
||||
var coreRoute = GetSelectedCoreRoute();
|
||||
if (coreRoute == null || coreRoute.PathType != PathType.Hoisting)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
if (HoistingLayerEditItems.Count == 0)
|
||||
{
|
||||
UpdateMainStatus("当前吊装路径没有可编辑的空中层。");
|
||||
return;
|
||||
}
|
||||
|
||||
for (int i = 0; i < HoistingLayerEditItems.Count; i++)
|
||||
{
|
||||
HoistingLayerEditItem currentLayer = HoistingLayerEditItems[i];
|
||||
if (currentLayer.HeightInMeters <= 0)
|
||||
{
|
||||
HandleInvalidHoistingLayerHeightInput($"第{currentLayer.DisplayIndex}层高度必须大于0。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (i > 0 && currentLayer.HeightInMeters >= HoistingLayerEditItems[i - 1].HeightInMeters)
|
||||
{
|
||||
HandleInvalidHoistingLayerHeightInput($"第{currentLayer.DisplayIndex}层高度必须严格低于上一层。");
|
||||
return;
|
||||
}
|
||||
|
||||
if (i < HoistingLayerEditItems.Count - 1 && currentLayer.HeightInMeters <= HoistingLayerEditItems[i + 1].HeightInMeters)
|
||||
{
|
||||
HandleInvalidHoistingLayerHeightInput($"第{currentLayer.DisplayIndex}层高度必须严格高于下一层。");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
HostCoordinateAdapter hostAdapter = CoordinateSystemManager.Instance.CreateHostAdapter();
|
||||
PathPoint hostStartPoint = coreRoute.Points.FirstOrDefault(point => point.Type == PathPointType.StartPoint) ?? coreRoute.Points.First();
|
||||
double hostStartElevation = HoistingCoordinateHelper.GetElevation(hostStartPoint.Position, hostAdapter);
|
||||
|
||||
foreach (HoistingLayerEditItem layerItem in HoistingLayerEditItems)
|
||||
{
|
||||
double hostTargetElevation = hostStartElevation + UnitsConverter.ConvertFromMeters(layerItem.HeightInMeters);
|
||||
foreach (string pointId in layerItem.PointIds)
|
||||
{
|
||||
PathPoint targetPoint = coreRoute.Points.FirstOrDefault(point => string.Equals(point.Id, pointId, StringComparison.Ordinal));
|
||||
if (targetPoint != null)
|
||||
{
|
||||
targetPoint.Position = HoistingCoordinateHelper.SetElevation(targetPoint.Position, hostTargetElevation, hostAdapter);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
PathPoint firstAerialPoint = coreRoute.Points.FirstOrDefault(point =>
|
||||
point != null &&
|
||||
point.Type != PathPointType.StartPoint &&
|
||||
point.Type != PathPointType.EndPoint &&
|
||||
HoistingCoordinateHelper.GetElevation(point.Position, hostAdapter) > hostStartElevation + UnitsConverter.ConvertFromMeters(0.001));
|
||||
coreRoute.LiftHeight = firstAerialPoint != null
|
||||
? HoistingCoordinateHelper.GetElevation(firstAerialPoint.Position, hostAdapter) - hostStartElevation
|
||||
: 0.0;
|
||||
coreRoute.LastModified = DateTime.Now;
|
||||
coreRoute.RecalculateRoute("更新吊装层高");
|
||||
|
||||
SyncObjectParametersToRenderPlugin();
|
||||
_pathPlanningManager.DrawRouteVisualization(coreRoute, isAutoPath: false);
|
||||
_pathPlanningManager.SavePathToDatabase(coreRoute);
|
||||
_pathPlanningManager.RaiseVisualizationStateChanged();
|
||||
|
||||
if (SelectedPathRoute != null && SelectedPathRoute.Id == coreRoute.Id)
|
||||
{
|
||||
SyncPathViewModelFromCoreRoute(SelectedPathRoute, coreRoute, preserveSelection: true);
|
||||
}
|
||||
|
||||
RefreshHoistingLayerEditItemsFromSelectedRoute();
|
||||
UpdateMainStatus($"已应用 {HoistingLayerEditItems.Count} 个吊装层高。");
|
||||
LogManager.Info($"[吊装层高] {coreRoute.Name}: 已应用 {HoistingLayerEditItems.Count} 个层高编辑项");
|
||||
}, "应用吊装层高");
|
||||
}
|
||||
|
||||
private void HandleInvalidHoistingLayerHeightInput(string message)
|
||||
{
|
||||
const string validationMessage = "每层高度必须低于上一层、高于下一层,也不能相等。";
|
||||
LogManager.Warning($"[吊装层高] 用户输入无效: {message}");
|
||||
RefreshHoistingLayerEditItemsFromSelectedRoute();
|
||||
UpdateMainStatus(validationMessage);
|
||||
MessageBox.Show(validationMessage, "层高输入无效", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 更新吊装路径的关联点(UI层)
|
||||
/// </summary>
|
||||
@ -6579,6 +6796,11 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
LogManager.Info($"已更新路径点列表,当前点数: {pathViewModel.Points.Count}");
|
||||
|
||||
if (SelectedPathRoute == pathViewModel)
|
||||
{
|
||||
RefreshHoistingLayerEditItemsFromSelectedRoute();
|
||||
}
|
||||
|
||||
// 更新真实路径可视化
|
||||
UpdatePathVisualization();
|
||||
}
|
||||
|
||||
@ -371,6 +371,59 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
|
||||
<Expander Header="吊装参数" IsExpanded="True" Margin="0,8,0,0">
|
||||
<StackPanel Margin="10,6,0,0">
|
||||
<StackPanel Visibility="{Binding IsHoistingRouteSelected, Converter={StaticResource BoolToVisibilityConverter}, ConverterParameter=Inverse}">
|
||||
<TextBlock Text="选择一条吊装路径后,可在这里编辑各空中层的绝对高度。"
|
||||
Style="{StaticResource StatusTextStyle}"
|
||||
Foreground="#FF666666"/>
|
||||
</StackPanel>
|
||||
|
||||
<StackPanel Visibility="{Binding IsHoistingRouteSelected, Converter={StaticResource BoolToVisibilityConverter}}">
|
||||
<TextBlock Text="{Binding HoistingLayerEditHintText}"
|
||||
Style="{StaticResource StatusTextStyle}"
|
||||
TextWrapping="Wrap"
|
||||
Foreground="#FF666666"/>
|
||||
|
||||
<ListView ItemsSource="{Binding HoistingLayerEditItems}"
|
||||
Margin="0,6,0,0"
|
||||
Height="120"
|
||||
BorderBrush="#FFCCCCCC"
|
||||
BorderThickness="1">
|
||||
<ListView.View>
|
||||
<GridView>
|
||||
<GridViewColumn Header="层级" Width="48">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBlock Text="{Binding DisplayIndex}"
|
||||
HorizontalAlignment="Center"/>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn Header="高度(米)" Width="88">
|
||||
<GridViewColumn.CellTemplate>
|
||||
<DataTemplate>
|
||||
<TextBox Text="{Binding HeightInMeters, Mode=TwoWay, UpdateSourceTrigger=LostFocus, StringFormat=0.0###}"
|
||||
HorizontalContentAlignment="Center"
|
||||
Style="{StaticResource ParameterInputStyle}"/>
|
||||
</DataTemplate>
|
||||
</GridViewColumn.CellTemplate>
|
||||
</GridViewColumn>
|
||||
<GridViewColumn Header="关联点" DisplayMemberBinding="{Binding PointSummary}" Width="92"/>
|
||||
</GridView>
|
||||
</ListView.View>
|
||||
</ListView>
|
||||
|
||||
<WrapPanel Margin="0,8,0,0">
|
||||
<Button Content="应用层高"
|
||||
Command="{Binding ApplyHoistingLayerHeightsCommand}"
|
||||
IsEnabled="{Binding CanApplyHoistingLayerHeights}"
|
||||
Style="{StaticResource ActionButtonStyle}"/>
|
||||
</WrapPanel>
|
||||
</StackPanel>
|
||||
</StackPanel>
|
||||
</Expander>
|
||||
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
Loading…
Reference in New Issue
Block a user