feat: 过弯检测工具(纯解析几何公式)
This commit is contained in:
parent
70fee3cf3a
commit
4f9206da2a
@ -62,6 +62,7 @@
|
||||
<ItemGroup>
|
||||
<Compile Include="UnitTests\Core\AliasTreeTests.cs" />
|
||||
<Compile Include="UnitTests\Core\PathHelperTests.cs" />
|
||||
<Compile Include="UnitTests\Core\CornerTurnCheckHelperTests.cs" />
|
||||
<Compile Include="UnitTests\Core\SelectionClipBoxLockStateTests.cs" />
|
||||
<Compile Include="UnitTests\Core\PathPlanningManagerHoistingCompletionTests.cs" />
|
||||
<Compile Include="UnitTests\Core\PathPersistenceTests.cs" />
|
||||
|
||||
@ -278,6 +278,9 @@
|
||||
<Compile Include="src\UI\WPF\Views\EditCoordinatesWindow.xaml.cs">
|
||||
<DependentUpon>EditCoordinatesWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="src\UI\WPF\Views\CornerTurnCheckDialog.xaml.cs">
|
||||
<DependentUpon>CornerTurnCheckDialog.xaml</DependentUpon>
|
||||
</Compile>
|
||||
<Compile Include="src\UI\WPF\Views\ModelItemBoundsWindow.xaml.cs">
|
||||
<DependentUpon>ModelItemBoundsWindow.xaml</DependentUpon>
|
||||
</Compile>
|
||||
@ -342,6 +345,7 @@
|
||||
<Compile Include="src\Utils\NavisworksSelectionHelper.cs" />
|
||||
<Compile Include="src\Utils\NavisworksToDMesh3Converter.cs" />
|
||||
<Compile Include="src\Utils\UnitsConverter.cs" />
|
||||
<Compile Include="src\Utils\CornerTurnCheckHelper.cs" />
|
||||
<Compile Include="src\Utils\VersionInfo.cs" />
|
||||
<!-- Coordinate System -->
|
||||
<Compile Include="src\Utils\CoordinateSystem\AlignToGroundMinCrossSectionYawSearcher.cs" />
|
||||
@ -407,6 +411,10 @@
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="src\UI\WPF\Views\CornerTurnCheckDialog.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
</Page>
|
||||
<Page Include="src\UI\WPF\Views\ModelItemBoundsWindow.xaml">
|
||||
<SubType>Designer</SubType>
|
||||
<Generator>MSBuild:Compile</Generator>
|
||||
|
||||
99
UnitTests/Core/CornerTurnCheckHelperTests.cs
Normal file
99
UnitTests/Core/CornerTurnCheckHelperTests.cs
Normal file
@ -0,0 +1,99 @@
|
||||
using System;
|
||||
using Microsoft.VisualStudio.TestTools.UnitTesting;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.UnitTests.Core
|
||||
{
|
||||
[TestClass]
|
||||
public class CornerTurnCheckHelperTests
|
||||
{
|
||||
[TestMethod]
|
||||
public void EqualWidth_MaxLength_ShouldPass()
|
||||
{
|
||||
var L = 3.0 * Math.Sqrt(2) - 1.5 - 0.01;
|
||||
Assert.IsTrue(CornerTurnCheckHelper.CanPass(L, 1.5, 3.0, 3.0, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EqualWidth_TooLong_ShouldNotPass()
|
||||
{
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(2.5, 1.0, 2.0, 2.0, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EqualWidth_Short_ShouldPass()
|
||||
{
|
||||
Assert.IsTrue(CornerTurnCheckHelper.CanPass(1.0, 0.5, 2.0, 2.0, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void StraightPass_ShouldPass()
|
||||
{
|
||||
Assert.IsTrue(CornerTurnCheckHelper.CanPass(2.5, 0.3, 2.5, 3.0, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void WidthExceedsBoth_ShouldNotPass()
|
||||
{
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(3.0, 4.0, 2.0, 2.5, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void ZeroOrNegativeInput_ShouldReturnFalse()
|
||||
{
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(0, 1, 2, 2, out _, out _));
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(2, 0, 2, 2, out _, out _));
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(2, 1, 0, 2, out _, out _));
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(2, 1, 2, 0, out _, out _));
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(-1, 1, 2, 2, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UnequalWidth_Fits_ShouldPass()
|
||||
{
|
||||
Assert.IsTrue(CornerTurnCheckHelper.CanPass(0.8, 0.5, 3.0, 1.5, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void VeryDifferentWidths_ShouldUseNarrower()
|
||||
{
|
||||
Assert.IsTrue(CornerTurnCheckHelper.CanPass(0.1, 0.55, 10.0, 0.6, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void HugeModule_ShouldNotPass()
|
||||
{
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(20.0, 5.0, 2.0, 2.0, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UserCase_LongerThanEntry_ShouldNotPass()
|
||||
{
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(4.9, 2.0, 4.8, 2.75, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void UserCase_WiderThanExit_ShouldNotPass()
|
||||
{
|
||||
Assert.IsFalse(CornerTurnCheckHelper.CanPass(4.8, 2.8, 3.0, 2.75, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EqualWidth_Square_ShouldPass()
|
||||
{
|
||||
Assert.IsTrue(CornerTurnCheckHelper.CanPass(1.0, 1.0, 2.0, 2.0, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void EntryExact_ShouldPass()
|
||||
{
|
||||
Assert.IsTrue(CornerTurnCheckHelper.CanPass(2.75, 2.0, 5.0, 2.75, out _, out _));
|
||||
}
|
||||
|
||||
[TestMethod]
|
||||
public void Square_WidthEqualsCorridor_ShouldPass()
|
||||
{
|
||||
Assert.IsTrue(CornerTurnCheckHelper.CanPass(2.0, 2.0, 3.0, 2.75, out _, out _));
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -209,6 +209,9 @@ NavisworksTransport 共享样式资源字典 - Navisworks 2026风格
|
||||
M17.65,6.35C16.2,4.9 14.21,4 12,4C7.58,4 4.01,7.58 4.01,12C4.01,16.42 7.58,20 12,20C15.73,20 18.84,17.45 19.73,14H17.65C16.83,16.33 14.61,18 12,18C8.69,18 6,15.31 6,12C6,8.69 8.69,6 12,6C13.66,6 15.14,6.69 16.22,7.78L13,11H20V4L17.65,6.35Z
|
||||
</Geometry>
|
||||
|
||||
<!-- L形转角图标 (过弯检测) -->
|
||||
<Geometry x:Key="CornerCheckIconGeometry">M3,17 L17,17 L17,3</Geometry>
|
||||
|
||||
<!-- 靶心/准心图标 (测量工具) -->
|
||||
<Geometry x:Key="TargetIconGeometry">
|
||||
M12,2C6.48,2 2,6.48 2,12C2,17.52 6.48,22 12,22C17.52,22 22,17.52 22,12C22,6.48 17.52,2 12,2ZM12,20C7.59,20 4,16.41 4,12C4,7.59 7.59,4 12,4C16.41,4 20,7.59 20,12C20,16.41 16.41,20 12,20ZM12,6C8.69,6 6,8.69 6,12C6,15.31 8.69,18 12,18C15.31,18 18,15.31 18,12C18,8.69 15.31,6 12,6ZM12,16C9.79,16 8,14.21 8,12C8,9.79 9.79,8 12,8C14.21,8 16,9.79 16,12C16,14.21 14.21,16 12,16Z
|
||||
|
||||
123
src/UI/WPF/Views/CornerTurnCheckDialog.xaml
Normal file
123
src/UI/WPF/Views/CornerTurnCheckDialog.xaml
Normal file
@ -0,0 +1,123 @@
|
||||
<Window x:Class="NavisworksTransport.UI.WPF.Views.CornerTurnCheckDialog"
|
||||
xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
|
||||
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
|
||||
xmlns:converters="clr-namespace:NavisworksTransport.UI.WPF.Converters"
|
||||
Title="过弯检测" Height="520" Width="420"
|
||||
WindowStartupLocation="CenterScreen"
|
||||
ResizeMode="NoResize"
|
||||
ShowInTaskbar="False"
|
||||
Topmost="True"
|
||||
Background="White">
|
||||
<Window.Resources>
|
||||
<ResourceDictionary>
|
||||
<ResourceDictionary.MergedDictionaries>
|
||||
<ResourceDictionary Source="pack://application:,,,/TransportPlugin;component/src/UI/WPF/Resources/NavisworksStyles.xaml"/>
|
||||
</ResourceDictionary.MergedDictionaries>
|
||||
<converters:EditableNumberConverter x:Key="EditableNumberConverter"/>
|
||||
</ResourceDictionary>
|
||||
</Window.Resources>
|
||||
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="*"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- 标题栏 -->
|
||||
<Border Grid.Row="0" Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="0,0,0,1" Padding="20,15">
|
||||
<StackPanel>
|
||||
<TextBlock Text="过弯检测" FontWeight="SemiBold" FontSize="14" Foreground="#FF2B579A"/>
|
||||
<TextBlock Text="检查矩形模块能否通过 L 形通道" FontSize="10" Foreground="#FF666666" Margin="0,3,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 输入区域 -->
|
||||
<Border Grid.Row="1" Padding="20,15">
|
||||
<StackPanel>
|
||||
<Border Background="#FFF0F7FF" BorderBrush="#FFD4E7FF" BorderThickness="1" Padding="12,10" Margin="0,0,0,15">
|
||||
<TextBlock Text="模块在转角处的投影宽度不能超过通道宽度,输入尺寸后计算最优化角度下的通过性。"
|
||||
FontSize="10" Foreground="#FF666666" TextWrapping="Wrap"/>
|
||||
</Border>
|
||||
|
||||
<!-- 入口通道宽度 -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="入口通道宽度 C₁:" VerticalAlignment="Center" FontSize="11" Foreground="#FF333333"/>
|
||||
<TextBox Grid.Column="1" x:Name="EntryWidthBox" Height="28"
|
||||
VerticalContentAlignment="Center" Padding="8,0" FontSize="11"
|
||||
BorderBrush="#FFCCCCCC" BorderThickness="1"/>
|
||||
<TextBlock Grid.Column="2" Text="米" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666" Margin="5,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 出口通道宽度 -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="出口通道宽度 C₂:" VerticalAlignment="Center" FontSize="11" Foreground="#FF333333"/>
|
||||
<TextBox Grid.Column="1" x:Name="ExitWidthBox" Height="28"
|
||||
VerticalContentAlignment="Center" Padding="8,0" FontSize="11"
|
||||
BorderBrush="#FFCCCCCC" BorderThickness="1"/>
|
||||
<TextBlock Grid.Column="2" Text="米" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666" Margin="5,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 分隔线 -->
|
||||
<Border Height="1" Background="#FFEEEEEE" Margin="0,0,0,12"/>
|
||||
|
||||
<!-- 模块长度 -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="模块长度 L:" VerticalAlignment="Center" FontSize="11" Foreground="#FF333333"/>
|
||||
<TextBox Grid.Column="1" x:Name="LengthBox" Height="28"
|
||||
VerticalContentAlignment="Center" Padding="8,0" FontSize="11"
|
||||
BorderBrush="#FFCCCCCC" BorderThickness="1"/>
|
||||
<TextBlock Grid.Column="2" Text="米" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666" Margin="5,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 模块宽度 -->
|
||||
<Grid Margin="0,0,0,12">
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="100"/>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="30"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Text="模块宽度 W:" VerticalAlignment="Center" FontSize="11" Foreground="#FF333333"/>
|
||||
<TextBox Grid.Column="1" x:Name="WidthBox" Height="28"
|
||||
VerticalContentAlignment="Center" Padding="8,0" FontSize="11"
|
||||
BorderBrush="#FFCCCCCC" BorderThickness="1"/>
|
||||
<TextBlock Grid.Column="2" Text="米" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666" Margin="5,0,0,0"/>
|
||||
</Grid>
|
||||
|
||||
<!-- 结果区 -->
|
||||
<Border x:Name="ResultBorder" Background="#FFF0FFF0" BorderBrush="#FF4CAF50" BorderThickness="1" Padding="12,10" Margin="0,5,0,0"
|
||||
Visibility="Collapsed">
|
||||
<StackPanel>
|
||||
<TextBlock x:Name="ResultTitle" FontWeight="SemiBold" FontSize="12" Foreground="#FF2E7D32"/>
|
||||
<TextBlock x:Name="ResultDetail" FontSize="10" Foreground="#FF666666" Margin="0,3,0,0"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
|
||||
<!-- 按钮栏 -->
|
||||
<Border Grid.Row="2" Background="#FFF8FBFF" BorderBrush="#FFD4E7FF" BorderThickness="0,1,0,0" Padding="20,12">
|
||||
<StackPanel Orientation="Horizontal" HorizontalAlignment="Right">
|
||||
<Button Content="计算" Click="OnCalculateClick"
|
||||
Style="{StaticResource ActionButtonStyle}" Width="90" Height="32" Margin="0,0,10,0"/>
|
||||
<Button Content="关闭" Click="OnCloseClick"
|
||||
Style="{StaticResource SecondaryButtonStyle}" Width="90" Height="32"/>
|
||||
</StackPanel>
|
||||
</Border>
|
||||
</Grid>
|
||||
</Window>
|
||||
80
src/UI/WPF/Views/CornerTurnCheckDialog.xaml.cs
Normal file
80
src/UI/WPF/Views/CornerTurnCheckDialog.xaml.cs
Normal file
@ -0,0 +1,80 @@
|
||||
using System;
|
||||
using System.Windows;
|
||||
using System.Windows.Media;
|
||||
using NavisworksTransport.Utils;
|
||||
|
||||
namespace NavisworksTransport.UI.WPF.Views
|
||||
{
|
||||
public partial class CornerTurnCheckDialog : Window
|
||||
{
|
||||
public CornerTurnCheckDialog()
|
||||
{
|
||||
InitializeComponent();
|
||||
}
|
||||
|
||||
private void OnCalculateClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
if (!TryParseInputs(out var c1, out var c2, out var length, out var width))
|
||||
return;
|
||||
|
||||
var pass = CornerTurnCheckHelper.CanPass(length, width, c1, c2,
|
||||
out var margin, out var maxLength);
|
||||
|
||||
ResultBorder.Visibility = Visibility.Visible;
|
||||
|
||||
if (pass)
|
||||
{
|
||||
ResultBorder.Background = new SolidColorBrush(Color.FromRgb(240, 255, 240));
|
||||
ResultBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(76, 175, 80));
|
||||
ResultTitle.Text = "✅ 可通过";
|
||||
ResultTitle.Foreground = new SolidColorBrush(Color.FromRgb(46, 125, 50));
|
||||
ResultDetail.Text = $"余量: {margin:F2} 米\n最大可通过长度: {maxLength:F2} 米";
|
||||
}
|
||||
else
|
||||
{
|
||||
ResultBorder.Background = new SolidColorBrush(Color.FromRgb(255, 240, 240));
|
||||
ResultBorder.BorderBrush = new SolidColorBrush(Color.FromRgb(244, 67, 54));
|
||||
ResultTitle.Text = "❌ 不可通过";
|
||||
ResultTitle.Foreground = new SolidColorBrush(Color.FromRgb(183, 28, 28));
|
||||
ResultDetail.Text = $"超出: {-margin:F2} 米\n当前条件下最大可通过长度: {maxLength:F2} 米";
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCloseClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
Close();
|
||||
}
|
||||
|
||||
private bool TryParseInputs(out double c1, out double c2, out double length, out double width)
|
||||
{
|
||||
c1 = c2 = length = width = 0;
|
||||
|
||||
if (!TryParseTextBox(EntryWidthBox, out c1, "入口通道宽度")) return false;
|
||||
if (!TryParseTextBox(ExitWidthBox, out c2, "出口通道宽度")) return false;
|
||||
if (!TryParseTextBox(LengthBox, out length, "模块长度")) return false;
|
||||
if (!TryParseTextBox(WidthBox, out width, "模块宽度")) return false;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
private static bool TryParseTextBox(System.Windows.Controls.TextBox box, out double value, string name)
|
||||
{
|
||||
value = 0;
|
||||
var text = box.Text.Trim();
|
||||
if (string.IsNullOrEmpty(text))
|
||||
{
|
||||
MessageBox.Show($"请输入{name}。", "输入错误", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
box.Focus();
|
||||
return false;
|
||||
}
|
||||
if (!double.TryParse(text, out value) || value <= 0)
|
||||
{
|
||||
MessageBox.Show($"请输入有效的{name}(正数)。", "输入错误", MessageBoxButton.OK, MessageBoxImage.Warning);
|
||||
box.Focus();
|
||||
box.SelectAll();
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -311,6 +311,7 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
<Grid.ColumnDefinitions>
|
||||
<ColumnDefinition Width="*"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
<ColumnDefinition Width="Auto"/>
|
||||
</Grid.ColumnDefinitions>
|
||||
<TextBlock Grid.Column="0" Text="路径点列表" VerticalAlignment="Center" FontSize="11" Foreground="#FF666666"/>
|
||||
<Button Grid.Column="1"
|
||||
@ -318,11 +319,22 @@ NavisworksTransport 路径编辑页签视图 - 采用与动画控制和分层管
|
||||
Click="OnMeasureBoundsButtonClick"
|
||||
Style="{StaticResource IconButtonStyle}"
|
||||
Width="24"
|
||||
Height="24">
|
||||
Height="24"
|
||||
Margin="0,0,2,0">
|
||||
<Path Data="{StaticResource TargetIconGeometry}"
|
||||
Fill="{StaticResource NavisworksPrimaryBrush}"
|
||||
Width="16" Height="16" Stretch="Uniform"/>
|
||||
</Button>
|
||||
<Button Grid.Column="2"
|
||||
ToolTip="过弯检测 — 检查模块能否通过 L 形通道"
|
||||
Click="OnCornerTurnCheckClick"
|
||||
Style="{StaticResource IconButtonStyle}"
|
||||
Width="24"
|
||||
Height="24">
|
||||
<Path Data="{StaticResource CornerCheckIconGeometry}"
|
||||
Fill="{StaticResource NavisworksPrimaryBrush}"
|
||||
Width="16" Height="16" Stretch="Uniform"/>
|
||||
</Button>
|
||||
</Grid>
|
||||
|
||||
<!-- 路径点列表 -->
|
||||
|
||||
@ -286,6 +286,19 @@ namespace NavisworksTransport.UI.WPF.Views
|
||||
}
|
||||
}
|
||||
|
||||
private void OnCornerTurnCheckClick(object sender, RoutedEventArgs e)
|
||||
{
|
||||
try
|
||||
{
|
||||
var dialog = new CornerTurnCheckDialog();
|
||||
dialog.ShowDialog();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"打开过弯检测对话框失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 包围盒信息窗口实例(保持引用以防止被GC回收)
|
||||
/// </summary>
|
||||
|
||||
98
src/Utils/CornerTurnCheckHelper.cs
Normal file
98
src/Utils/CornerTurnCheckHelper.cs
Normal file
@ -0,0 +1,98 @@
|
||||
using System;
|
||||
|
||||
namespace NavisworksTransport.Utils
|
||||
{
|
||||
public static class CornerTurnCheckHelper
|
||||
{
|
||||
public static bool CanPass(double length, double width, double entryWidth, double exitWidth,
|
||||
out double margin, out double maxLength)
|
||||
{
|
||||
margin = 0;
|
||||
maxLength = 0;
|
||||
|
||||
if (length <= 0 || width <= 0 || entryWidth <= 0 || exitWidth <= 0)
|
||||
return false;
|
||||
|
||||
// 模块最短边必须能同时通过两条通道
|
||||
var minDim = Math.Min(length, width);
|
||||
if (minDim > entryWidth || minDim > exitWidth)
|
||||
return false;
|
||||
|
||||
// 转角通过性解析判断
|
||||
var r = Math.Sqrt(length * length + width * width);
|
||||
var phi = Math.Atan2(width, length);
|
||||
|
||||
var sinEntryRatio = entryWidth / r;
|
||||
var cosExitRatio = exitWidth / r;
|
||||
|
||||
// 入口侧:最大 θ 满足 Y_span ≤ C₁
|
||||
var thetaYmax = double.NaN;
|
||||
if (sinEntryRatio >= 1)
|
||||
thetaYmax = Math.PI / 2; // 从不超出
|
||||
else
|
||||
{
|
||||
thetaYmax = Math.Asin(sinEntryRatio) - phi;
|
||||
if (thetaYmax < 0) thetaYmax = 0;
|
||||
if (thetaYmax > Math.PI / 2) thetaYmax = Math.PI / 2;
|
||||
}
|
||||
|
||||
// 出口侧:最小 θ 满足 X_span ≤ C₂
|
||||
var thetaXmin = double.NaN;
|
||||
if (cosExitRatio >= 1)
|
||||
thetaXmin = 0; // 从不超出
|
||||
else
|
||||
{
|
||||
thetaXmin = Math.Acos(cosExitRatio) + phi;
|
||||
if (thetaXmin < 0) thetaXmin = 0;
|
||||
if (thetaXmin > Math.PI / 2) thetaXmin = Math.PI / 2;
|
||||
}
|
||||
|
||||
// 需要同时满足两条约束的区间有交集
|
||||
var canPass = thetaYmax >= thetaXmin;
|
||||
|
||||
if (canPass)
|
||||
{
|
||||
// 余量 = 在交集中心处两个约束的最小剩余量
|
||||
var thetaMid = (thetaYmax + thetaXmin) / 2;
|
||||
var cos = Math.Cos(thetaMid);
|
||||
var sin = Math.Sin(thetaMid);
|
||||
var xSpan = length * cos + width * sin;
|
||||
var ySpan = length * sin + width * cos;
|
||||
margin = Math.Min(exitWidth - xSpan, entryWidth - ySpan);
|
||||
|
||||
maxLength = FindMaxLength(width, entryWidth, exitWidth);
|
||||
}
|
||||
|
||||
return canPass;
|
||||
}
|
||||
|
||||
/// <summary> 二分法求最大可通过长度,使用相同的解析条件 </summary>
|
||||
private static double FindMaxLength(double width, double c1, double c2)
|
||||
{
|
||||
double lo = 0, hi = Math.Max(c1, c2) * 4;
|
||||
for (int i = 0; i < 50; i++)
|
||||
{
|
||||
var mid = (lo + hi) / 2;
|
||||
if (CheckLengthFeasible(mid, width, c1, c2))
|
||||
lo = mid;
|
||||
else
|
||||
hi = mid;
|
||||
}
|
||||
return (lo + hi) / 2;
|
||||
}
|
||||
|
||||
private static bool CheckLengthFeasible(double length, double width, double c1, double c2)
|
||||
{
|
||||
var r = Math.Sqrt(length * length + width * width);
|
||||
var phi = Math.Atan2(width, length);
|
||||
|
||||
var thetaYmax = c1 >= r ? Math.PI / 2
|
||||
: Math.Min(Math.Max(Math.Asin(c1 / r) - phi, 0), Math.PI / 2);
|
||||
|
||||
var thetaXmin = c2 >= r ? 0
|
||||
: Math.Min(Math.Max(Math.Acos(c2 / r) + phi, 0), Math.PI / 2);
|
||||
|
||||
return thetaYmax >= thetaXmin;
|
||||
}
|
||||
}
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user