Add rail preferred normal repair script
This commit is contained in:
parent
7058e5fd23
commit
e21e934705
209
scripts/Fix-RailPreferredNormals.ps1
Normal file
209
scripts/Fix-RailPreferredNormals.ps1
Normal file
@ -0,0 +1,209 @@
|
||||
<#
|
||||
用法示例
|
||||
|
||||
1. 批量修复当前目录下所有 XML 路径文件
|
||||
powershell -ExecutionPolicy Bypass -File "C:\Users\Tellme\apps\NavisworksTransport-rail-mount-modes\scripts\Fix-RailPreferredNormals.ps1" -Path "C:\Users\Tellme\Documents\NavisworksTransport\模型"
|
||||
|
||||
2. 递归修复目录及其子目录中的所有 XML 路径文件
|
||||
powershell -ExecutionPolicy Bypass -File "C:\Users\Tellme\apps\NavisworksTransport-rail-mount-modes\scripts\Fix-RailPreferredNormals.ps1" -Path "C:\Users\Tellme\Documents\NavisworksTransport\模型" -Recurse
|
||||
|
||||
3. 只修复单个 XML 路径文件
|
||||
powershell -ExecutionPolicy Bypass -File "C:\Users\Tellme\apps\NavisworksTransport-rail-mount-modes\scripts\Fix-RailPreferredNormals.ps1" -Path "C:\Users\Tellme\Documents\NavisworksTransport\模型\副本_人工_0331_133243.xml"
|
||||
|
||||
脚本规则
|
||||
|
||||
- 只处理 pathType="Rail" 且 railMountMode="OverRail" 的路径
|
||||
- 只修复 railPreferredNormalY < 0 的路径
|
||||
- 修复方式为将 railPreferredNormalX / Y / Z 三个分量一起取反
|
||||
- 原文件会被直接覆盖,同时自动保留一个同名 .bak 备份文件
|
||||
#>
|
||||
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Path,
|
||||
|
||||
[switch]$Recurse
|
||||
)
|
||||
|
||||
Set-StrictMode -Version Latest
|
||||
$ErrorActionPreference = 'Stop'
|
||||
|
||||
function Format-NormalValue {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[double]$Value
|
||||
)
|
||||
|
||||
return $Value.ToString("0.###", [System.Globalization.CultureInfo]::InvariantCulture)
|
||||
}
|
||||
|
||||
function Test-ParseableDoubleText {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$Text,
|
||||
|
||||
[ref]$Value
|
||||
)
|
||||
|
||||
return [double]::TryParse(
|
||||
$Text,
|
||||
[System.Globalization.NumberStyles]::Float -bor [System.Globalization.NumberStyles]::AllowLeadingSign,
|
||||
[System.Globalization.CultureInfo]::InvariantCulture,
|
||||
$Value)
|
||||
}
|
||||
|
||||
function Update-RouteNode {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[System.Xml.XmlElement]$RouteNode,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$SourcePath
|
||||
)
|
||||
|
||||
if ($RouteNode.GetAttribute("pathType") -ne "Rail") {
|
||||
return $false
|
||||
}
|
||||
|
||||
if ($RouteNode.GetAttribute("railMountMode") -ne "OverRail") {
|
||||
return $false
|
||||
}
|
||||
|
||||
$xText = $RouteNode.GetAttribute("railPreferredNormalX")
|
||||
$yText = $RouteNode.GetAttribute("railPreferredNormalY")
|
||||
$zText = $RouteNode.GetAttribute("railPreferredNormalZ")
|
||||
|
||||
if ([string]::IsNullOrWhiteSpace($xText) -or
|
||||
[string]::IsNullOrWhiteSpace($yText) -or
|
||||
[string]::IsNullOrWhiteSpace($zText)) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$x = 0.0
|
||||
$y = 0.0
|
||||
$z = 0.0
|
||||
if (-not (Test-ParseableDoubleText -Text $xText -Value ([ref]$x)) -or
|
||||
-not (Test-ParseableDoubleText -Text $yText -Value ([ref]$y)) -or
|
||||
-not (Test-ParseableDoubleText -Text $zText -Value ([ref]$z))) {
|
||||
throw "文件 '$SourcePath' 中存在无法解析的 railPreferredNormal 值。"
|
||||
}
|
||||
|
||||
if ($y -ge 0) {
|
||||
return $false
|
||||
}
|
||||
|
||||
$RouteNode.SetAttribute("railPreferredNormalX", (Format-NormalValue -Value (-$x)))
|
||||
$RouteNode.SetAttribute("railPreferredNormalY", (Format-NormalValue -Value (-$y)))
|
||||
$RouteNode.SetAttribute("railPreferredNormalZ", (Format-NormalValue -Value (-$z)))
|
||||
return $true
|
||||
}
|
||||
|
||||
function Save-XmlDocument {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[xml]$Document,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$TargetPath
|
||||
)
|
||||
|
||||
$settings = New-Object System.Xml.XmlWriterSettings
|
||||
$settings.Indent = $true
|
||||
$settings.IndentChars = " "
|
||||
$settings.Encoding = New-Object System.Text.UTF8Encoding($false)
|
||||
$settings.NewLineChars = "`r`n"
|
||||
$settings.NewLineHandling = [System.Xml.NewLineHandling]::Replace
|
||||
|
||||
$writer = [System.Xml.XmlWriter]::Create($TargetPath, $settings)
|
||||
try {
|
||||
$Document.Save($writer)
|
||||
}
|
||||
finally {
|
||||
$writer.Dispose()
|
||||
}
|
||||
}
|
||||
|
||||
function Update-XmlFile {
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$FilePath
|
||||
)
|
||||
|
||||
[xml]$xml = Get-Content -LiteralPath $FilePath -Raw
|
||||
$routeNodes = $xml.SelectNodes("//*[local-name()='Route']")
|
||||
if ($null -eq $routeNodes -or $routeNodes.Count -eq 0) {
|
||||
return [pscustomobject]@{
|
||||
FilePath = $FilePath
|
||||
Modified = $false
|
||||
RouteCount = 0
|
||||
}
|
||||
}
|
||||
|
||||
$modifiedRouteCount = 0
|
||||
foreach ($routeNode in $routeNodes) {
|
||||
if (Update-RouteNode -RouteNode $routeNode -SourcePath $FilePath) {
|
||||
$modifiedRouteCount++
|
||||
}
|
||||
}
|
||||
|
||||
if ($modifiedRouteCount -gt 0) {
|
||||
$backupPath = "$FilePath.bak"
|
||||
if (-not (Test-Path -LiteralPath $backupPath)) {
|
||||
Copy-Item -LiteralPath $FilePath -Destination $backupPath
|
||||
}
|
||||
|
||||
Save-XmlDocument -Document $xml -TargetPath $FilePath
|
||||
}
|
||||
|
||||
return [pscustomobject]@{
|
||||
FilePath = $FilePath
|
||||
Modified = ($modifiedRouteCount -gt 0)
|
||||
RouteCount = $modifiedRouteCount
|
||||
}
|
||||
}
|
||||
|
||||
$resolvedPath = Resolve-Path -LiteralPath $Path
|
||||
$item = Get-Item -LiteralPath $resolvedPath
|
||||
|
||||
$files = @()
|
||||
if ($item.PSIsContainer) {
|
||||
$childParams = @{
|
||||
LiteralPath = $item.FullName
|
||||
File = $true
|
||||
}
|
||||
if ($Recurse) {
|
||||
$childParams["Recurse"] = $true
|
||||
}
|
||||
|
||||
$files = Get-ChildItem @childParams | Where-Object { $_.Extension -ieq ".xml" }
|
||||
}
|
||||
else {
|
||||
$files = @($item)
|
||||
}
|
||||
|
||||
if ($files.Count -eq 0) {
|
||||
Write-Host "未找到可处理的 XML 文件。"
|
||||
exit 0
|
||||
}
|
||||
|
||||
$results = @()
|
||||
foreach ($file in $files) {
|
||||
$results += Update-XmlFile -FilePath $file.FullName
|
||||
}
|
||||
|
||||
$modifiedFiles = @($results | Where-Object { $_.Modified })
|
||||
$modifiedRoutes = 0
|
||||
if ($modifiedFiles.Count -gt 0) {
|
||||
$measure = $modifiedFiles | Measure-Object -Property RouteCount -Sum
|
||||
if ($null -ne $measure -and $null -ne $measure.Sum) {
|
||||
$modifiedRoutes = [int]$measure.Sum
|
||||
}
|
||||
}
|
||||
|
||||
Write-Host ("扫描文件: {0}" -f $results.Count)
|
||||
Write-Host ("修改文件: {0}" -f $modifiedFiles.Count)
|
||||
Write-Host ("修复路径: {0}" -f $modifiedRoutes)
|
||||
|
||||
foreach ($result in $modifiedFiles) {
|
||||
Write-Host ("已修复: {0} (路径数={1})" -f $result.FilePath, $result.RouteCount)
|
||||
}
|
||||
@ -770,7 +770,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
else
|
||||
{
|
||||
startPosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, startPosition, previousPoint, nextPoint, objectHeight);
|
||||
LogManager.Debug($"[移动到起点] Rail路径调整: 参考点=({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2}), 物体中心=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2}), 物体高度={objectHeight:F2}, 安装={_route.RailMountMode}, 对接={(PathRoute.IsTopPayloadAnchorForMountMode(_route.RailMountMode) ? "顶面对接" : "底面对接")}");
|
||||
LogManager.Debug($"[移动到起点] Rail路径调整: 参考点=({_pathPoints[0].X:F2},{_pathPoints[0].Y:F2},{_pathPoints[0].Z:F2}), 物体中心=({startPosition.X:F2},{startPosition.Y:F2},{startPosition.Z:F2}), 物体高度={objectHeight:F2}, 安装={_route.RailMountMode}, 参考面={(PathRoute.IsTopReferenceFaceForMountMode(_route.RailMountMode) ? "顶面参考点" : "底面参考点")}");
|
||||
}
|
||||
|
||||
if (TryCreateRailPathRotation(
|
||||
@ -1278,7 +1278,7 @@ namespace NavisworksTransport.Core.Animation
|
||||
else
|
||||
{
|
||||
framePosition = RailPathPoseHelper.ResolveObjectSpaceCenterPosition(_route, framePosition, p1, p2, objectHeight);
|
||||
LogManager.Debug($"[Rail路径] 调整物体位置: 参考点=({p1.X:F2},{p1.Y:F2},{p1.Z:F2})->({p2.X:F2},{p2.Y:F2},{p2.Z:F2}), 物体中心=({framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2}), 安装={_route.RailMountMode}, 对接={(PathRoute.IsTopPayloadAnchorForMountMode(_route.RailMountMode) ? "顶面对接" : "底面对接")}");
|
||||
LogManager.Debug($"[Rail路径] 调整物体位置: 参考点=({p1.X:F2},{p1.Y:F2},{p1.Z:F2})->({p2.X:F2},{p2.Y:F2},{p2.Z:F2}), 物体中心=({framePosition.X:F2},{framePosition.Y:F2},{framePosition.Z:F2}), 安装={_route.RailMountMode}, 参考面={(PathRoute.IsTopReferenceFaceForMountMode(_route.RailMountMode) ? "顶面参考点" : "底面参考点")}");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -566,7 +566,7 @@ namespace NavisworksTransport
|
||||
[Serializable]
|
||||
public class PathRoute
|
||||
{
|
||||
public static bool IsTopPayloadAnchorForMountMode(RailMountMode railMountMode)
|
||||
public static bool IsTopReferenceFaceForMountMode(RailMountMode railMountMode)
|
||||
{
|
||||
return railMountMode == RailMountMode.UnderRail;
|
||||
}
|
||||
|
||||
@ -2943,16 +2943,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
private double GetAssemblyAnchorDirection()
|
||||
{
|
||||
return PathRoute.IsTopPayloadAnchorForMountMode(AssemblyMountMode)
|
||||
return PathRoute.IsTopReferenceFaceForMountMode(AssemblyMountMode)
|
||||
? 1.0
|
||||
: -1.0;
|
||||
}
|
||||
|
||||
private string GetAssemblyAnchorText()
|
||||
{
|
||||
return PathRoute.IsTopPayloadAnchorForMountMode(AssemblyMountMode)
|
||||
? "顶面对接"
|
||||
: "底面对接";
|
||||
return PathRoute.IsTopReferenceFaceForMountMode(AssemblyMountMode)
|
||||
? "顶面参考点"
|
||||
: "底面参考点";
|
||||
}
|
||||
|
||||
private sealed class AssemblyReferenceLine
|
||||
|
||||
@ -15,7 +15,7 @@ namespace NavisworksTransport.Utils.CoordinateSystem
|
||||
// 路径点本身已经是安装参考点(终点锚点)语义。
|
||||
// 动画跟踪中心只需要相对安装参考点沿轨道法向偏移半个高度,
|
||||
// 不能叠加任何“参考线 -> 锚点”旧语义偏移,否则会把终点/逐帧参考点重复平移。
|
||||
double halfHeightOffset = PathRoute.IsTopPayloadAnchorForMountMode(route.RailMountMode)
|
||||
double halfHeightOffset = PathRoute.IsTopReferenceFaceForMountMode(route.RailMountMode)
|
||||
? -objectHeight / 2.0
|
||||
: objectHeight / 2.0;
|
||||
return halfHeightOffset + route.RailNormalOffset;
|
||||
|
||||
@ -50,7 +50,7 @@ namespace NavisworksTransport.Utils
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (PathRoute.IsTopPayloadAnchorForMountMode(route.RailMountMode))
|
||||
if (PathRoute.IsTopReferenceFaceForMountMode(route.RailMountMode))
|
||||
{
|
||||
return -objectHeight;
|
||||
}
|
||||
@ -60,8 +60,8 @@ namespace NavisworksTransport.Utils
|
||||
|
||||
/// <summary>
|
||||
/// 计算通行空间中心相对于 Rail 参考点的 Z 偏移(模型单位)。
|
||||
/// 顶面对接时,通行空间中心位于对接点下方半高;
|
||||
/// 底面对接时,通行空间中心位于对接点上方半高。
|
||||
/// 路径参考点位于构件顶面时,通行空间中心位于参考点下方半高;
|
||||
/// 路径参考点位于构件底面时,通行空间中心位于参考点上方半高。
|
||||
/// </summary>
|
||||
public static double GetObjectSpaceCenterZOffset(PathRoute route, double objectSpaceHeight)
|
||||
{
|
||||
@ -70,7 +70,7 @@ namespace NavisworksTransport.Utils
|
||||
return 0.0;
|
||||
}
|
||||
|
||||
if (PathRoute.IsTopPayloadAnchorForMountMode(route.RailMountMode))
|
||||
if (PathRoute.IsTopReferenceFaceForMountMode(route.RailMountMode))
|
||||
{
|
||||
return -objectSpaceHeight / 2.0;
|
||||
}
|
||||
@ -605,7 +605,7 @@ namespace NavisworksTransport.Utils
|
||||
|
||||
private static double GetBottomOffsetMagnitude(PathRoute route, double objectHeight)
|
||||
{
|
||||
double baseOffset = PathRoute.IsTopPayloadAnchorForMountMode(route.RailMountMode)
|
||||
double baseOffset = PathRoute.IsTopReferenceFaceForMountMode(route.RailMountMode)
|
||||
? -objectHeight
|
||||
: 0.0;
|
||||
return baseOffset + route.RailNormalOffset;
|
||||
@ -613,7 +613,7 @@ namespace NavisworksTransport.Utils
|
||||
|
||||
private static double GetObjectSpaceCenterOffsetMagnitude(PathRoute route, double objectSpaceHeight)
|
||||
{
|
||||
double baseOffset = PathRoute.IsTopPayloadAnchorForMountMode(route.RailMountMode)
|
||||
double baseOffset = PathRoute.IsTopReferenceFaceForMountMode(route.RailMountMode)
|
||||
? -objectSpaceHeight / 2.0
|
||||
: objectSpaceHeight / 2.0;
|
||||
return baseOffset + route.RailNormalOffset;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user