增加添加路径点功能,还差智能插入位置和预览连线功能。

This commit is contained in:
tian 2025-08-30 20:53:10 +08:00
parent 5938c817a4
commit 1e046e1e4d
5 changed files with 454 additions and 37 deletions

View File

@ -6,6 +6,7 @@
1. [ ]性能优化用几何方法识别通道的坡度变化侧面上表面轮廓线给通道网格准确的z坐标
2. [ ]BUG动画中的包围盒检测和ClashDetective检测结果不一致是因为碰撞间隙不同造成的。Autodesk官方建议保守测试+0公差解决碰撞检测结果不准确的问题
3. [x] (功能)实现完整的路径点可视化编辑
### [2025/08/29]

View File

@ -46,6 +46,15 @@ namespace NavisworksTransport
// 路径点3D标记管理
private List<PathPointMarker> _pathPointMarkers;
// 预览点管理
private PathPoint _previewPoint = null;
private bool _isPreviewMode = false;
/// <summary>
/// 获取当前是否在预览模式
/// </summary>
public bool IsPreviewMode => _isPreviewMode;
// 静态引用用于处理ToolPlugin事件
private static PathPlanningManager _activePathManager;
@ -1371,6 +1380,189 @@ namespace NavisworksTransport
}
}
/// <summary>
/// 设置预览点位置(仅用于预览,不添加到路径中)
/// </summary>
/// <param name="worldPoint">3D世界坐标</param>
/// <param name="pointType">点类型</param>
/// <returns>预览点对象</returns>
public PathPoint SetPreviewPoint(Point3D worldPoint, PathPointType? pointType = null)
{
// 确保在编辑状态下才能设置预览点
if (!IsInEditableState)
{
RaiseErrorOccurred("不在编辑状态,无法设置预览点");
return null;
}
// 如果没有当前路径,则无法设置预览点
if (CurrentRoute == null)
{
RaiseErrorOccurred("内部错误:当前路径丢失");
return null;
}
try
{
// 确定路径点类型
PathPointType finalPointType;
if (pointType.HasValue)
{
finalPointType = pointType.Value;
}
else
{
// 自动判断类型:第一个点为起点,其余为路径点
finalPointType = (CurrentRoute.Points.Count == 0)
? PathPointType.StartPoint
: PathPointType.WayPoint;
}
// 创建预览点(不添加到路径中)
_previewPoint = new PathPoint
{
Name = $"预览-{GeneratePointName(finalPointType)}",
Position = worldPoint,
Type = finalPointType
};
_isPreviewMode = true;
LogManager.Info($"预览点已设置: {_previewPoint.Name}, 位置: ({worldPoint.X:F2}, {worldPoint.Y:F2}, {worldPoint.Z:F2})");
// 绘制预览点可视化(灰色)
try
{
DrawPreviewPointVisualization(_previewPoint);
LogManager.Info($"预览点3D可视化已更新: {_previewPoint.Name}");
}
catch (Exception renderEx)
{
LogManager.Error($"绘制预览点3D可视化失败: {renderEx.Message}");
}
return _previewPoint;
}
catch (Exception ex)
{
RaiseErrorOccurred($"设置预览点失败: {ex.Message}", ex);
return null;
}
}
/// <summary>
/// 确认预览点,将其添加到当前路径中
/// </summary>
/// <returns>添加的路径点如果失败返回null</returns>
public PathPoint ConfirmPreviewPoint()
{
if (!_isPreviewMode || _previewPoint == null)
{
RaiseErrorOccurred("没有预览点可确认");
return null;
}
if (CurrentRoute == null)
{
RaiseErrorOccurred("内部错误:当前路径丢失");
return null;
}
try
{
// 创建正式的路径点
var confirmPoint = new PathPoint
{
Name = GeneratePointName(_previewPoint.Type),
Position = _previewPoint.Position,
Type = _previewPoint.Type
};
// 添加到当前路径
CurrentRoute.Points.Add(confirmPoint);
LogManager.Info($"路径点已确认添加: {confirmPoint.Name}, 位置: ({confirmPoint.Position.X:F2}, {confirmPoint.Position.Y:F2}, {confirmPoint.Position.Z:F2})");
// 清除预览状态
ClearPreviewPoint();
// 绘制3D路径可视化
try
{
DrawRouteVisualization(CurrentRoute, isAutoPath: false);
LogManager.Info($"手工路径3D可视化已更新: {confirmPoint.Name}");
}
catch (Exception renderEx)
{
LogManager.Error($"绘制手工路径3D可视化失败: {renderEx.Message}");
}
// 触发路径点添加事件
RaisePathPointOperation(PathPointOperationType.Added, confirmPoint, CurrentRoute);
// 触发路径列表更新事件
RaisePathPointsListUpdated(CurrentRoute, "确认添加预览点");
return confirmPoint;
}
catch (Exception ex)
{
RaiseErrorOccurred($"确认预览点失败: {ex.Message}", ex);
return null;
}
}
/// <summary>
/// 清除预览点
/// </summary>
public void ClearPreviewPoint()
{
if (_isPreviewMode && _previewPoint != null)
{
LogManager.Info($"清除预览点: {_previewPoint.Name}");
// 清除预览点可视化
try
{
ClearPreviewPointVisualization();
}
catch (Exception ex)
{
LogManager.Error($"清除预览点可视化失败: {ex.Message}");
}
}
_previewPoint = null;
_isPreviewMode = false;
}
/// <summary>
/// 绘制预览点可视化(灰色)
/// </summary>
/// <param name="previewPoint">预览点</param>
private void DrawPreviewPointVisualization(PathPoint previewPoint)
{
if (_renderPlugin != null && previewPoint != null)
{
// 先清除之前的预览点
_renderPlugin.ClearPreviewPoint();
// 绘制新的灰色预览点
_renderPlugin.RenderPreviewPoint(previewPoint);
LogManager.Info($"预览点可视化已绘制: {previewPoint.Name}");
}
}
/// <summary>
/// 清除预览点可视化
/// </summary>
private void ClearPreviewPointVisualization()
{
if (_renderPlugin != null)
{
_renderPlugin.ClearPreviewPoint();
LogManager.Info("预览点可视化已清除");
}
}
/// <summary>
/// 获取路径统计信息
/// </summary>
@ -1856,7 +2048,7 @@ namespace NavisworksTransport
SearchAndSetTraversableChannels();
}
// 检查是否在可通行的物流模型内并添加路径点
// 检查是否在可通行的物流模型内并处理点击
if (_selectedChannels != null && _selectedChannels.Any())
{
bool isInTraversableLogisticsModel = IsItemInSelectedChannels(pickResult.ModelItem) ||
@ -1866,29 +2058,48 @@ namespace NavisworksTransport
if (isInTraversableLogisticsModel)
{
// 手动路径编辑 - 添加路径点并绘制
LogManager.WriteLog("[手动编辑] 调用AddPathPointIn3D添加路径点");
var pathPoint = AddPathPointIn3D(pickResult.Point);
if (pathPoint != null)
// 手动路径编辑 - 根据当前模式处理点击
if (PathEditState == PathEditState.AddingPoints)
{
LogManager.WriteLog($"[手动编辑] ✓ 路径点添加成功: {pathPoint.Name}");
// 添加路径点模式 - 使用预览点
LogManager.WriteLog("[手动编辑] 设置预览点位置");
var previewPoint = SetPreviewPoint(pickResult.Point);
if (previewPoint != null)
{
LogManager.WriteLog($"[手动编辑] ✓ 预览点已设置: {previewPoint.Name}");
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 预览点设置失败");
}
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 路径点添加失败");
// 其他编辑模式 - 保持原有逻辑
LogManager.WriteLog("[手动编辑] 调用AddPathPointIn3D添加路径点");
var pathPoint = AddPathPointIn3D(pickResult.Point);
if (pathPoint != null)
{
LogManager.WriteLog($"[手动编辑] ✓ 路径点添加成功: {pathPoint.Name}");
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 路径点添加失败");
}
}
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 点击位置不在可通行的物流模型内");
OnStatusChanged("点击位置不是可通行的物流模型,请在可通行的物流模型上点击");
RaiseErrorOccurred("点击位置不在物流通道内,请选择有效的物流路径位置");
}
}
else
{
LogManager.WriteLog("[手动编辑] ✗ 没有可通行的物流模型");
OnStatusChanged("没有找到任何可通行的物流模型,请先为模型设置可通行的物流属性");
LogManager.WriteLog("[手动编辑] ✗ 未找到可通行的物流模型");
RaiseErrorOccurred("未找到可通行的物流通道,请先选择或配置物流通道");
}
}

View File

@ -107,6 +107,9 @@ namespace NavisworksTransport
// 为向后兼容保留的旧字段
private List<CircleMarker> _circleMarkers = new List<CircleMarker>();
// 预览点标记
private CircleMarker _previewMarker = null;
// 静态实例,用于外部访问
private static PathPointRenderPlugin _instance;
@ -186,20 +189,23 @@ namespace NavisworksTransport
return; // 静默返回,避免日志泛滥
}
// 检查是否有路径需要渲染
// 检查是否有路径或预览点需要渲染
int pathCount;
bool hasPreviewPoint;
lock (_lockObject)
{
pathCount = _pathVisualizations.Count;
hasPreviewPoint = _previewMarker != null;
}
if (pathCount == 0) return;
if (pathCount == 0 && !hasPreviewPoint) return;
// 使用BeginModelContext确保正确的渲染上下文
graphics.BeginModelContext();
lock (_lockObject)
{
// 渲染所有正式路径
foreach (var visualization in _pathVisualizations.Values)
{
// 渲染所有点标记
@ -216,6 +222,13 @@ namespace NavisworksTransport
graphics.Cylinder(lineMarker.StartPoint, lineMarker.EndPoint, lineMarker.Radius);
}
}
// 渲染预览点(灰色)
if (_previewMarker != null && _previewMarker.IsVisible)
{
graphics.Color(_previewMarker.Color, 0.7); // 使用半透明效果
graphics.Sphere(_previewMarker.Position, _previewMarker.Radius);
}
}
graphics.EndModelContext();
@ -750,6 +763,82 @@ namespace NavisworksTransport
// 由于新系统没有直接的序号对应关系,这个方法不再有效
}
/// <summary>
/// 渲染预览点(灰色显示)
/// </summary>
/// <param name="previewPoint">预览点对象</param>
public void RenderPreviewPoint(PathPoint previewPoint)
{
try
{
if (previewPoint == null)
{
LogManager.WriteLog("[预览点渲染] 预览点为null跳过渲染");
return;
}
lock (_lockObject)
{
LogManager.WriteLog($"[预览点渲染] 开始渲染预览点: {previewPoint.Name}");
// 获取适当的半径
double radius = GetRadiusForPointType(previewPoint.Type);
// 使用白色作为预览点颜色灰色在Navisworks API中不可用
Color previewColor = Color.White;
// 创建预览点标记
_previewMarker = new CircleMarker
{
Position = previewPoint.Position,
Radius = radius,
Color = previewColor,
IsVisible = true,
PathPoint = previewPoint
};
LogManager.WriteLog($"[预览点渲染] 预览点标记已创建: 位置({previewPoint.Position.X:F2}, {previewPoint.Position.Y:F2}, {previewPoint.Position.Z:F2}), 半径: {radius:F2}, 颜色: 灰色");
// 请求刷新视图
RequestViewRefresh();
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[预览点渲染] 渲染预览点失败: {ex.Message}");
LogManager.WriteLog($"[预览点渲染] 堆栈跟踪: {ex.StackTrace}");
}
}
/// <summary>
/// 清除预览点
/// </summary>
public void ClearPreviewPoint()
{
try
{
lock (_lockObject)
{
if (_previewMarker != null)
{
LogManager.WriteLog($"[预览点渲染] 清除预览点: {_previewMarker.PathPoint?.Name ?? ""}");
_previewMarker = null;
// 请求刷新视图
RequestViewRefresh();
}
else
{
LogManager.WriteLog("[预览点渲染] 没有预览点需要清除");
}
}
}
catch (Exception ex)
{
LogManager.WriteLog($"[预览点渲染] 清除预览点失败: {ex.Message}");
}
}
#endregion
#region
@ -941,6 +1030,24 @@ namespace NavisworksTransport
/// 创建时间
/// </summary>
public DateTime CreatedTime { get; set; }
/// <summary>
/// 是否可见
/// </summary>
public bool IsVisible { get; set; }
/// <summary>
/// 位置Center的别名用于预览点兼容性
/// </summary>
public Point3D Position
{
get { return Center; }
set { Center = value; }
}
/// <summary>
/// 关联的路径点对象
/// </summary>
public PathPoint PathPoint { get; set; }
public override string ToString()
{

View File

@ -40,6 +40,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private string _autoPathStartPoint = "未选择";
private string _autoPathEndPoint = "未选择";
private string _autoPathStatus = "就绪";
private string _pathEditTipText = "提示:点击[添加路径点]后在3D视图中点击可通行的物流模型来添加路径点";
private Point3D _startPoint3D;
private Point3D _endPoint3D;
private bool _hasStartPoint = false;
@ -165,6 +166,14 @@ namespace NavisworksTransport.UI.WPF.ViewModels
get => _autoPathStatus;
set => SetProperty(ref _autoPathStatus, value);
}
/// <summary>
/// 路径编辑提示文本
/// </summary>
public string PathEditTipText
{
get => _pathEditTipText;
set => SetProperty(ref _pathEditTipText, value);
}
public double VehicleLength
{
@ -317,7 +326,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
(_pathPlanningManager?.PathEditState == PathEditState.Viewing);
public bool CanExecuteEndEdit => (_pathPlanningManager?.PathEditState == PathEditState.Creating ||
_pathPlanningManager?.PathEditState == PathEditState.Editing);
_pathPlanningManager?.PathEditState == PathEditState.Editing ||
_pathPlanningManager?.PathEditState == PathEditState.AddingPoints);
public bool CanExecuteClearPath => SelectedPathRoute != null && SelectedPathRoute.Points.Count > 0;
@ -370,21 +380,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
private void InitializeCommands()
{
// 使用原有的RelayCommand模式初始化命令
NewPathCommand = new RelayCommand(async () => await ExecuteNewPathAsync());
DeletePathCommand = new RelayCommand(async () => await ExecuteDeletePathAsync(), () => SelectedPathRoute != null);
RenamePathCommand = new RelayCommand(async () => await ExecuteRenamePathAsync(), () => SelectedPathRoute != null);
StartEditCommand = new RelayCommand(async () => await ExecuteStartEditAsync(), () => CanExecuteStartEdit);
DeletePathCommand = new RelayCommand(async () => await ExecuteDeletePathAsync());
RenamePathCommand = new RelayCommand(async () => await ExecuteRenamePathAsync());
StartEditCommand = new RelayCommand(async () => await ExecuteAddPathPointAsync(), () => CanExecuteStartEdit);
EndEditCommand = new RelayCommand(async () => await ExecuteEndEditAsync(), () => CanExecuteEndEdit);
ClearPathCommand = new RelayCommand(async () => await ExecuteClearPathAsync(), () => CanExecuteClearPath);
DeletePointCommand = new RelayCommand<PathPointViewModel>(async (point) => await ExecuteDeletePointAsync(point));
SelectStartPointCommand = new RelayCommand(async () => await ExecuteSelectStartPointAsync());
SelectEndPointCommand = new RelayCommand(async () => await ExecuteSelectEndPointAsync());
AutoPlanPathCommand = new RelayCommand(async () => await ExecuteAutoPlanPathAsync(), () => CanExecuteAutoPlanPath);
ClearAutoPathCommand = new RelayCommand(async () => await ExecuteClearAutoPathAsync());
ImportPathCommand = new RelayCommand(async () => await ExecuteImportPathAsync());
ExportPathCommand = new RelayCommand(async () => await ExecuteExportPathAsync(), () => CanExecuteExportPath);
SaveAsPathCommand = new RelayCommand(async () => await ExecuteSaveAsPathAsync(), () => CanExecuteSaveAsPath);
@ -849,7 +855,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
}, "重命名路径");
}
private async Task ExecuteStartEditAsync()
private async Task ExecuteAddPathPointAsync()
{
if (!CanExecuteStartEdit) return;
@ -861,10 +867,42 @@ namespace NavisworksTransport.UI.WPF.ViewModels
if (SelectedPathRoute != null)
{
SelectedPathRoute.IsActive = true;
AutoPathStatus = $"正在激活3D路径编辑模式: {SelectedPathRoute.Name}...";
// 启动PathPlanningManager的点击工具这会设置正确的编辑状态
if (_pathPlanningManager != null)
{
try
{
_pathPlanningManager.StartClickTool(PathPointType.WayPoint);
LogManager.Info($"已启动PathPlanningManager点击工具: {SelectedPathRoute.Name}");
}
catch (Exception ex)
{
LogManager.Error($"启动点击工具失败: {ex.Message}");
AutoPathStatus = "启动3D编辑工具失败请重试";
IsPathEditMode = false;
return;
}
}
else
{
AutoPathStatus = "路径规划管理器未初始化";
LogManager.Error("添加路径点PathPlanningManager未初始化");
IsPathEditMode = false;
return;
}
AutoPathStatus = $"已进入3D路径编辑模式: {SelectedPathRoute.Name}";
PathEditTipText = "✓ 预览模式已激活 - 在3D视图中点击设置灰色预览点满意后点击[确认]添加到路径,继续编辑或点击[结束]完成";
LogManager.Info($"开始添加路径点: {SelectedPathRoute.Name},已启动点击工具");
// 手动触发按钮状态更新
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
}
});
}, "开始编辑");
}, "添加路径点");
}
private async Task ExecuteEndEditAsync()
@ -873,33 +911,83 @@ namespace NavisworksTransport.UI.WPF.ViewModels
await SafeExecuteAsync(async () =>
{
// 调用PathPlanningManager的FinishEditing方法
// 这会自动将最后一个点设置为终点,并触发相关事件
bool success = false;
string operationMessage = "";
bool wasInPreviewMode = false;
if (_pathPlanningManager != null)
{
success = _pathPlanningManager.FinishEditing();
// 记录是否在预览模式(因为确认预览点后会退出预览模式)
wasInPreviewMode = _pathPlanningManager.IsPreviewMode;
if (wasInPreviewMode)
{
// 预览模式 - 先确认预览点,然后立即完成编辑
var confirmedPoint = _pathPlanningManager.ConfirmPreviewPoint();
if (confirmedPoint != null)
{
LogManager.Info($"[UI-预览模式] 预览点已确认: {confirmedPoint.Name},现在完成编辑");
// 立即完成编辑,不让用户继续添加点
success = _pathPlanningManager.FinishEditing();
operationMessage = success ? $"预览点已确认并完成编辑: {confirmedPoint.Name}" : "预览点确认后完成编辑失败";
}
else
{
LogManager.Error("[UI-预览模式] 预览点确认失败");
operationMessage = "预览点确认失败";
success = false;
}
LogManager.Info($"[UI-预览模式] {operationMessage}");
}
else
{
// 普通模式 - 直接完成编辑
success = _pathPlanningManager.FinishEditing();
operationMessage = success ? "路径编辑完成" : "路径编辑完成失败";
LogManager.Info($"[UI-编辑模式] {operationMessage}");
}
}
await _uiStateManager.ExecuteUIUpdateAsync(() =>
{
IsPathEditMode = false;
if (SelectedPathRoute != null)
if (success)
{
SelectedPathRoute.IsActive = false;
if (success)
// 编辑完成成功
if (SelectedPathRoute != null)
{
AutoPathStatus = $"✅ 路径编辑完成: {SelectedPathRoute.Name} - 最后一个点已自动设置为终点";
if (wasInPreviewMode)
{
AutoPathStatus = $"✅ 预览点已确认,路径编辑完成: {SelectedPathRoute.Name}";
}
else
{
AutoPathStatus = $"✅ 路径编辑完成: {SelectedPathRoute.Name} - 最后一个点已自动设置为终点";
}
PathFileStatus = "已完成";
}
else
LogManager.Info("[UI状态] 路径编辑完成,等待状态变更事件更新按钮");
}
else
{
// 编辑完成失败
if (SelectedPathRoute != null)
{
AutoPathStatus = $"❌ 路径编辑完成失败: {SelectedPathRoute.Name}";
AutoPathStatus = $"❌ 路径编辑失败: {SelectedPathRoute.Name}";
PathFileStatus = "编辑失败";
}
LogManager.Error("[UI状态] 路径编辑失败");
}
// 手动触发按钮状态更新 - 确保UI及时响应
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
});
}, "结束编辑");
}, "结束路径编辑");
}
private async Task ExecuteClearPathAsync()
@ -1885,7 +1973,12 @@ namespace NavisworksTransport.UI.WPF.ViewModels
{
LogManager.Info($"路径编辑状态变更: {e.PreviousState} -> {e.NewState}");
IsPathEditMode = (e.NewState == PathEditState.Creating || e.NewState == PathEditState.Editing);
// 修复:包含 AddingPoints 状态
IsPathEditMode = (e.NewState == PathEditState.Creating ||
e.NewState == PathEditState.Editing ||
e.NewState == PathEditState.AddingPoints);
LogManager.Info($"[UI状态更新] IsPathEditMode 设为: {IsPathEditMode}");
// 更新状态提示
switch (e.NewState)
@ -1896,6 +1989,9 @@ namespace NavisworksTransport.UI.WPF.ViewModels
case PathEditState.Editing:
AutoPathStatus = $"正在编辑路径: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点";
break;
case PathEditState.AddingPoints:
AutoPathStatus = $"正在添加路径点到: {e.EditingRoute?.Name} - 请在3D视图中点击设置路径点";
break;
case PathEditState.Viewing:
AutoPathStatus = "路径编辑已完成";
break;
@ -1907,6 +2003,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
// 通知命令状态更新
OnPropertyChanged(nameof(CanExecuteStartEdit));
OnPropertyChanged(nameof(CanExecuteEndEdit));
LogManager.Info($"[UI状态更新] 按钮状态已通知更新 - CanExecuteStartEdit: {CanExecuteStartEdit}, CanExecuteEndEdit: {CanExecuteEndEdit}");
}, "处理编辑状态变更事件");
}
catch (Exception ex)

View File

@ -241,7 +241,7 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
<!-- 编辑控制按钮 -->
<StackPanel Orientation="Horizontal" Margin="0,10,0,10">
<Button Content="开始"
<Button Content="添加路径点"
Command="{Binding StartEditCommand}"
Style="{StaticResource ActionButtonStyle}"
IsEnabled="{Binding CanExecuteStartEdit}"/>
@ -305,7 +305,7 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
</ListView>
<!-- 编辑提示 -->
<TextBlock Text="提示开始编辑后在3D视图中点击可通行的物流模型来添加路径点"
<TextBlock Text="{Binding PathEditTipText}"
Style="{StaticResource StatusTextStyle}"
Foreground="#FF666666"
Margin="0,5,0,0"/>