Add linear assembly reference path and rod alignment
This commit is contained in:
parent
0c83c73917
commit
a55ef42f71
@ -470,13 +470,6 @@
|
||||
</Page>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildExtensionsPath)\Microsoft\WinFX\3.0\Microsoft.WinFX.targets" Condition="Exists('$(MSBuildExtensionsPath)\Microsoft\WinFX\3.0\Microsoft.WinFX.targets')" />
|
||||
<ItemGroup>
|
||||
<!-- Localization Files -->
|
||||
<None Include="resources\TransportPlugin.name.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>TransportPlugin.name.txt</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
|
||||
<!-- Import NETStandard.Library targets to support .NET Standard 2.0 libraries -->
|
||||
<Import Project="packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets" Condition="Exists('packages\NETStandard.Library.2.0.3\build\netstandard2.0\NETStandard.Library.targets')" />
|
||||
@ -486,6 +479,11 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>resources\default_config.toml</Link>
|
||||
</None>
|
||||
<!-- SQLite native x64 runtime dependency -->
|
||||
<None Include="packages\Stub.System.Data.SQLite.Core.NetFramework.1.0.118.0\build\net46\x64\SQLite.Interop.dll">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>SQLite.Interop.dll</Link>
|
||||
</None>
|
||||
<!-- Plugin Name File (in resources folder) -->
|
||||
<None Include="resources\TransportPlugin.name.txt">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
@ -496,6 +494,11 @@
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>resources\unit_cube.nwc</Link>
|
||||
</None>
|
||||
<!-- Unit Cylinder NWC Model File -->
|
||||
<None Include="resources\unit_cylinder.nwc">
|
||||
<CopyToOutputDirectory>PreserveNewest</CopyToOutputDirectory>
|
||||
<Link>resources\unit_cylinder.nwc</Link>
|
||||
</None>
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
||||
@ -1,6 +1,7 @@
|
||||
@echo off
|
||||
setlocal
|
||||
|
||||
:: 如果 Navisworks 正在运行,关闭它以释放 DLL 锁定
|
||||
:: Stop Navisworks to release locked plugin DLLs.
|
||||
taskkill /F /IM Roamer.exe 2>nul
|
||||
if %errorlevel% == 0 (
|
||||
echo Navisworks process terminated.
|
||||
@ -8,15 +9,17 @@ if %errorlevel% == 0 (
|
||||
)
|
||||
|
||||
set "TARGET_DIR=%PROGRAMDATA%\Autodesk\Navisworks Manage 2026\plugins\TransportPlugin"
|
||||
set "BUILD_DIR=bin\x64\Release"
|
||||
|
||||
if not exist "%TARGET_DIR%" mkdir "%TARGET_DIR%"
|
||||
|
||||
copy "bin\x64\Release\TransportPlugin.dll" "%TARGET_DIR%\" >nul
|
||||
copy "%BUILD_DIR%\*.dll" "%TARGET_DIR%\" >nul
|
||||
echo Plugin DLLs deployed.
|
||||
|
||||
:: 复制resources文件夹(包含chart.js等本地资源)
|
||||
if exist "bin\x64\Release\resources" (
|
||||
:: Copy bundled resources.
|
||||
if exist "%BUILD_DIR%\resources" (
|
||||
if not exist "%TARGET_DIR%\resources" mkdir "%TARGET_DIR%\resources"
|
||||
copy "bin\x64\Release\resources\*" "%TARGET_DIR%\resources\" >nul
|
||||
xcopy "%BUILD_DIR%\resources\*" "%TARGET_DIR%\resources\" /E /I /Y /Q >nul
|
||||
echo Resources folder deployed.
|
||||
)
|
||||
|
||||
|
||||
@ -703,6 +703,131 @@ var transform = components.Combine();
|
||||
// 结果:物体"公转"到错误位置
|
||||
```
|
||||
|
||||
#### 11.7.2.1 Rotation3D 构造函数的真实语义(2026-03-19 新增,重要)
|
||||
|
||||
这次在“直线装配参考杆”功能中,验证了一个很容易误用的点:
|
||||
|
||||
- `Rotation3D(UnitVector3D, UnitVector3D, UnitVector3D)` **不是**“传入本地 X/Y/Z 三个轴来直接构造三维姿态”
|
||||
- 之前如果把它当成“三轴姿态构造器”使用,会得到完全错误的旋转结果
|
||||
|
||||
根据 Navisworks .NET API HTML 文档:
|
||||
|
||||
- `Rotation3D(UnitVector3D vector1, UnitVector3D vector2)`
|
||||
- 文档摘要:`Creates rotation that rotates vector1 to same direction as vector2`
|
||||
- 正确语义:**将向量1旋转到与向量2同方向**
|
||||
- `Rotation3D(UnitVector3D axis, double angle)`
|
||||
- 文档摘要:`Creates rotation about given axis by angle in radians`
|
||||
- 正确语义:**绕指定轴按弧度旋转**
|
||||
- `Rotation3D(UnitVector3D axis, UnitVector3D vector1, UnitVector3D vector2)`
|
||||
- 文档摘要:`Creates rotation about axis by angle between v1 and v2 projected onto plane normal to axis.`
|
||||
- 正确语义:**绕 axis 旋转,旋转角由 vector1 和 vector2 在垂直于 axis 的平面上的投影夹角决定**
|
||||
|
||||
**结论:**
|
||||
|
||||
- 3 参数版本是“轴 + 两个参考向量”的构造,不是“三个基向量/三个坐标轴”
|
||||
- 如果需求是“让物体本地某个轴对齐任意空间方向”,优先使用 **2 参数版本**
|
||||
|
||||
#### 11.7.2.2 三维参考杆/任意向量对齐的正确用法
|
||||
|
||||
适用场景:
|
||||
|
||||
- 参考杆、箭头、圆柱体等“有明确本地长度轴”的资源
|
||||
- 需要将资源的本地某个轴,对齐到一个任意空间向量
|
||||
|
||||
例如:
|
||||
|
||||
- 资源中心在原点
|
||||
- 长轴沿本地 `+X`
|
||||
- 目标方向是参考线方向 `referenceDirection`
|
||||
|
||||
那么旋转应直接写成:
|
||||
|
||||
```csharp
|
||||
var rotation = new Rotation3D(
|
||||
new UnitVector3D(1, 0, 0),
|
||||
new UnitVector3D(referenceDirection));
|
||||
```
|
||||
|
||||
其含义非常直接:
|
||||
|
||||
- 把资源的本地 `+X` 轴
|
||||
- 旋转到 `referenceDirection`
|
||||
|
||||
这正是“直线装配参考杆”最终验证通过的做法。
|
||||
|
||||
**不要写成下面这样:**
|
||||
|
||||
```csharp
|
||||
// ❌ 错误:把三个向量误当成 X/Y/Z 三个姿态轴
|
||||
var rotation = new Rotation3D(
|
||||
new UnitVector3D(tangent),
|
||||
new UnitVector3D(lateral),
|
||||
new UnitVector3D(normal));
|
||||
```
|
||||
|
||||
这段代码的真实语义并不是“建立一个由 tangent/lateral/normal 组成的姿态”,
|
||||
而是“绕 tangent 这个轴,根据 lateral 和 normal 的投影夹角去旋转”,
|
||||
所以结果通常会完全错误。
|
||||
|
||||
#### 11.7.2.3 三维旋转与平移补偿必须分开理解
|
||||
|
||||
即使 `Rotation3D(vector1, vector2)` 构造正确,仍然不能忽略 Navisworks 的旋转中心限制:
|
||||
|
||||
- `OverridePermanentTransform()` 仍然是**增量变换**
|
||||
- 旋转仍然是**绕世界原点**
|
||||
- 因此三维姿态正确后,位置仍需要像 yaw 场景一样做补偿
|
||||
|
||||
对于“资源中心在原点、目标中心为 `targetCenter`”的对象,推荐模式是:
|
||||
|
||||
```csharp
|
||||
// 1. 从资源原始状态出发
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
// 2. 计算旋转:本地 X -> 目标方向
|
||||
var rotation = new Rotation3D(
|
||||
new UnitVector3D(1, 0, 0),
|
||||
new UnitVector3D(referenceDirection));
|
||||
|
||||
// 3. 计算原始中心绕世界原点旋转后的位置
|
||||
var originalCenter = item.BoundingBox().Center;
|
||||
var rotationTransform = new Transform3D(rotation);
|
||||
var linear = rotationTransform.Linear;
|
||||
var rotatedCenter = new Point3D(
|
||||
linear.Get(0, 0) * originalCenter.X + linear.Get(0, 1) * originalCenter.Y + linear.Get(0, 2) * originalCenter.Z,
|
||||
linear.Get(1, 0) * originalCenter.X + linear.Get(1, 1) * originalCenter.Y + linear.Get(1, 2) * originalCenter.Z,
|
||||
linear.Get(2, 0) * originalCenter.X + linear.Get(2, 1) * originalCenter.Y + linear.Get(2, 2) * originalCenter.Z);
|
||||
|
||||
// 4. 用补偿平移把旋转后的中心送到目标中心
|
||||
var translation = new Vector3D(
|
||||
targetCenter.X - rotatedCenter.X,
|
||||
targetCenter.Y - rotatedCenter.Y,
|
||||
targetCenter.Z - rotatedCenter.Z);
|
||||
|
||||
// 5. 组合并应用增量变换
|
||||
var transform = new Transform3D(rotation, translation);
|
||||
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
||||
```
|
||||
|
||||
**关键原则:**
|
||||
|
||||
- 先把“旋转方向”算对
|
||||
- 再按“绕原点旋转后的实际位置”计算平移补偿
|
||||
- 不要把“姿态构造错误”和“旋转中心补偿缺失”混成一个问题
|
||||
|
||||
#### 11.7.2.4 这类问题的调试建议
|
||||
|
||||
当三维物体“位置大概对,但方向明显错”时,优先检查:
|
||||
|
||||
1. 是否误用了 `Rotation3D(UnitVector3D, UnitVector3D, UnitVector3D)`
|
||||
2. 资源文件的本地长度轴是否真的与你的代码约定一致
|
||||
3. 是否把 `ModelItem.Transform` 错当成 override 后的真实世界姿态
|
||||
|
||||
推荐观察项:
|
||||
|
||||
- 参考线/目标向量本身是否正确
|
||||
- 资源本地长度轴约定(例如是否沿本地 `+X`)
|
||||
- 当前实际显示位置,优先看 `BoundingBox()`,不要只看 `ModelItem.Transform`
|
||||
|
||||
#### 11.7.3 正确实现"绕物体中心旋转"
|
||||
|
||||
**解决方案:手动计算旋转导致的位置偏移并补偿**
|
||||
|
||||
@ -2,6 +2,10 @@
|
||||
|
||||
## 功能点
|
||||
|
||||
### [2026/3/18]
|
||||
|
||||
1. [x](功能)增加轨上路径(角度可倾斜)
|
||||
|
||||
### [2026/3/11]
|
||||
|
||||
1. [x] (功能)给“导出剖面盒”增加导出nwd文件的选项
|
||||
|
||||
BIN
resources/unit_cylinder.nwc
Normal file
BIN
resources/unit_cylinder.nwc
Normal file
Binary file not shown.
@ -9,8 +9,8 @@ namespace NavisworksTransport.Core
|
||||
{
|
||||
/// <summary>
|
||||
/// 直线装配参考路径管理器。
|
||||
/// 优先使用单位圆柱体资源生成一根可点击的临时参考杆,供用户在 3D 视图中选择起点。
|
||||
/// 如果圆柱资源缺失,则退回到单位立方体资源。
|
||||
/// 使用单位立方体资源生成一根可点击的临时参考杆,供用户在 3D 视图中选择起点。
|
||||
/// 参考杆本质上表达的是一段 start->end 参考轴线,而不是终点处的独立构件。
|
||||
/// </summary>
|
||||
public class AssemblyReferencePathManager
|
||||
{
|
||||
@ -27,7 +27,6 @@ namespace NavisworksTransport.Core
|
||||
private Point3D _referenceLineEnd = Point3D.Origin;
|
||||
private double _referenceRodDiameterInMeters;
|
||||
private string _referenceResourceName;
|
||||
|
||||
public static AssemblyReferencePathManager Instance
|
||||
{
|
||||
get
|
||||
@ -50,7 +49,10 @@ namespace NavisworksTransport.Core
|
||||
public ModelItem CurrentReferenceRod => _referenceRodModelItem;
|
||||
public bool IsReferenceRodVisible => _isReferenceRodVisible;
|
||||
public bool IsUpdatingReferenceRod => _isUpdatingReferenceRod;
|
||||
public bool HasReferenceLine => _referenceRodModelItem != null && _isReferenceRodVisible;
|
||||
public bool HasReferenceLine => VectorLengthSquared(new Vector3D(
|
||||
_referenceLineEnd.X - _referenceLineStart.X,
|
||||
_referenceLineEnd.Y - _referenceLineStart.Y,
|
||||
_referenceLineEnd.Z - _referenceLineStart.Z)) > 1e-9;
|
||||
public Point3D ReferenceLineStart => _referenceLineStart;
|
||||
public Point3D ReferenceLineEnd => _referenceLineEnd;
|
||||
public string ReferenceResourceName => _referenceResourceName;
|
||||
@ -62,6 +64,7 @@ namespace NavisworksTransport.Core
|
||||
|
||||
/// <summary>
|
||||
/// 创建或更新参考杆。
|
||||
/// 先记录参考线主数据,再基于参考线更新可点击实体外壳。
|
||||
/// </summary>
|
||||
public ModelItem CreateOrUpdateReferenceRod(Point3D startPoint, Point3D endPoint, double diameterInMeters)
|
||||
{
|
||||
@ -80,19 +83,19 @@ namespace NavisworksTransport.Core
|
||||
_isUpdatingReferenceRod = true;
|
||||
try
|
||||
{
|
||||
EnsureReferenceRodModelLoaded();
|
||||
|
||||
double lengthInMeters = UnitsConverter.ConvertToMeters(referenceLength);
|
||||
ScaleReferenceRod(lengthInMeters, diameterInMeters);
|
||||
PositionReferenceRod(startPoint, endPoint);
|
||||
|
||||
ShowReferenceRod();
|
||||
|
||||
_referenceLineStart = startPoint;
|
||||
_referenceLineEnd = endPoint;
|
||||
_referenceRodDiameterInMeters = diameterInMeters;
|
||||
|
||||
LogManager.Info($"[装配参考杆] 已创建/更新,长度={lengthInMeters:F3}m,直径={diameterInMeters:F3}m");
|
||||
EnsureReferenceRodModelLoaded();
|
||||
|
||||
ScaleReferenceRod(referenceLength, diameterInMeters);
|
||||
PositionReferenceRod();
|
||||
|
||||
ShowReferenceRod();
|
||||
|
||||
double referenceLengthInMeters = UnitsConverter.ConvertToMeters(referenceLength);
|
||||
LogManager.Info($"[装配参考杆] 已创建/更新,长度={referenceLengthInMeters:F3}m,直径={diameterInMeters:F3}m");
|
||||
return _referenceRodModelItem;
|
||||
}
|
||||
finally
|
||||
@ -108,7 +111,7 @@ namespace NavisworksTransport.Core
|
||||
return;
|
||||
}
|
||||
|
||||
var items = new ModelItemCollection { _referenceRodModelItem };
|
||||
var items = new ModelItemCollection { _referenceRodModel.RootItem };
|
||||
Application.ActiveDocument.Models.SetHidden(items, false);
|
||||
_isReferenceRodVisible = true;
|
||||
}
|
||||
@ -120,7 +123,7 @@ namespace NavisworksTransport.Core
|
||||
return;
|
||||
}
|
||||
|
||||
var items = new ModelItemCollection { _referenceRodModelItem };
|
||||
var items = new ModelItemCollection { _referenceRodModel.RootItem };
|
||||
Application.ActiveDocument.Models.SetHidden(items, true);
|
||||
_isReferenceRodVisible = false;
|
||||
}
|
||||
@ -199,19 +202,22 @@ namespace NavisworksTransport.Core
|
||||
}
|
||||
|
||||
_referenceRodModel = doc.Models.Last();
|
||||
_referenceRodModelItem = FindFirstGeometryItem(_referenceRodModel.RootItem);
|
||||
_referenceRodModelItem = _referenceRodModel.RootItem;
|
||||
_isReferenceRodVisible = true;
|
||||
_referenceResourceName = resourceName;
|
||||
LogManager.Info($"[装配参考杆] 已加载资源: {resourceName}");
|
||||
}
|
||||
|
||||
private void ScaleReferenceRod(double lengthInMeters, double diameterInMeters)
|
||||
private void ScaleReferenceRod(double length, double diameterInMeters)
|
||||
{
|
||||
if (_referenceRodModel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var doc = Application.ActiveDocument;
|
||||
double lengthInMeters = UnitsConverter.ConvertToMeters(length);
|
||||
|
||||
var currentTransform = _referenceRodModel.Transform;
|
||||
var components = currentTransform.Factor();
|
||||
components.Scale = new Vector3D(
|
||||
@ -220,54 +226,36 @@ namespace NavisworksTransport.Core
|
||||
diameterInMeters / ReferenceRodBaseSizeInMeters);
|
||||
|
||||
Transform3D scaledTransform = components.Combine();
|
||||
Application.ActiveDocument.Models.SetModelUnitsAndTransform(
|
||||
doc.Models.SetModelUnitsAndTransform(
|
||||
_referenceRodModel,
|
||||
_referenceRodModel.Units,
|
||||
scaledTransform,
|
||||
false);
|
||||
|
||||
LogManager.Info(
|
||||
$"[装配参考杆] 缩放完成,目标长度={lengthInMeters:F3}m,目标直径={diameterInMeters:F3}m,模型单位={_referenceRodModel.Units}");
|
||||
}
|
||||
|
||||
private void PositionReferenceRod(Point3D startPoint, Point3D endPoint)
|
||||
private void PositionReferenceRod()
|
||||
{
|
||||
if (_referenceRodModelItem == null)
|
||||
if (_referenceRodModelItem == null || _referenceRodModel == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { _referenceRodModelItem };
|
||||
var currentScale = _referenceRodModelItem.Transform.Factor().Scale;
|
||||
Point3D targetCenter = new Point3D(
|
||||
(_referenceLineStart.X + _referenceLineEnd.X) / 2.0,
|
||||
(_referenceLineStart.Y + _referenceLineEnd.Y) / 2.0,
|
||||
(_referenceLineStart.Z + _referenceLineEnd.Z) / 2.0);
|
||||
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
Rotation3D rotation = CreateLineRotation(_referenceLineStart, _referenceLineEnd);
|
||||
ModelItemTransformHelper.MoveItemToCenterAndRotation(
|
||||
_referenceRodModelItem,
|
||||
targetCenter,
|
||||
rotation);
|
||||
|
||||
var originalBounds = _referenceRodModelItem.BoundingBox();
|
||||
var originalCenter = originalBounds.Center;
|
||||
var targetCenter = new Point3D(
|
||||
(startPoint.X + endPoint.X) / 2.0,
|
||||
(startPoint.Y + endPoint.Y) / 2.0,
|
||||
(startPoint.Z + endPoint.Z) / 2.0);
|
||||
|
||||
Rotation3D rotation = CreateLineRotation(startPoint, endPoint);
|
||||
|
||||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||||
var components = identity.Factor();
|
||||
components.Scale = currentScale;
|
||||
components.Rotation = rotation;
|
||||
|
||||
var preTransform = components.Combine();
|
||||
var linear = preTransform.Linear;
|
||||
var transformedCenter = new Point3D(
|
||||
linear.Get(0, 0) * originalCenter.X + linear.Get(0, 1) * originalCenter.Y + linear.Get(0, 2) * originalCenter.Z,
|
||||
linear.Get(1, 0) * originalCenter.X + linear.Get(1, 1) * originalCenter.Y + linear.Get(1, 2) * originalCenter.Z,
|
||||
linear.Get(2, 0) * originalCenter.X + linear.Get(2, 1) * originalCenter.Y + linear.Get(2, 2) * originalCenter.Z);
|
||||
|
||||
components.Translation = new Vector3D(
|
||||
targetCenter.X - transformedCenter.X,
|
||||
targetCenter.Y - transformedCenter.Y,
|
||||
targetCenter.Z - transformedCenter.Z);
|
||||
|
||||
Transform3D transform = components.Combine();
|
||||
doc.Models.OverridePermanentTransform(modelItems, transform, false);
|
||||
LogManager.Info(
|
||||
$"[装配参考杆] 定位完成,中心=({targetCenter.X:F3}, {targetCenter.Y:F3}, {targetCenter.Z:F3}),起点=({_referenceLineStart.X:F3}, {_referenceLineStart.Y:F3}, {_referenceLineStart.Z:F3}),终点=({_referenceLineEnd.X:F3}, {_referenceLineEnd.Y:F3}, {_referenceLineEnd.Z:F3})");
|
||||
}
|
||||
|
||||
private static Rotation3D CreateLineRotation(Point3D startPoint, Point3D endPoint)
|
||||
@ -277,28 +265,12 @@ namespace NavisworksTransport.Core
|
||||
endPoint.Y - startPoint.Y,
|
||||
endPoint.Z - startPoint.Z));
|
||||
|
||||
var worldUp = new Vector3D(0, 0, 1);
|
||||
var projectedNormal = new Vector3D(
|
||||
worldUp.X - Dot(worldUp, tangent) * tangent.X,
|
||||
worldUp.Y - Dot(worldUp, tangent) * tangent.Y,
|
||||
worldUp.Z - Dot(worldUp, tangent) * tangent.Z);
|
||||
|
||||
if (VectorLengthSquared(projectedNormal) < 1e-9)
|
||||
{
|
||||
var fallbackUp = new Vector3D(0, 1, 0);
|
||||
projectedNormal = new Vector3D(
|
||||
fallbackUp.X - Dot(fallbackUp, tangent) * tangent.X,
|
||||
fallbackUp.Y - Dot(fallbackUp, tangent) * tangent.Y,
|
||||
fallbackUp.Z - Dot(fallbackUp, tangent) * tangent.Z);
|
||||
}
|
||||
|
||||
var normal = Normalize(projectedNormal);
|
||||
var lateral = Normalize(Cross(normal, tangent));
|
||||
|
||||
// 参考杆资源约定:几何中心在原点,长度轴沿局部 +X。
|
||||
// Navisworks API 文档中 Rotation3D(vector1, vector2) 的语义
|
||||
// 是“将 vector1 旋转到与 vector2 同方向”,这里直接将本地 X 轴对齐参考线方向。
|
||||
return new Rotation3D(
|
||||
new UnitVector3D(tangent),
|
||||
new UnitVector3D(lateral),
|
||||
new UnitVector3D(normal));
|
||||
new UnitVector3D(1, 0, 0),
|
||||
new UnitVector3D(tangent));
|
||||
}
|
||||
|
||||
private static Point3D ProjectPointToSegment(Point3D point, Point3D segmentStart, Point3D segmentEnd)
|
||||
@ -325,19 +297,6 @@ namespace NavisworksTransport.Core
|
||||
segmentStart.Z + projection * segmentZ);
|
||||
}
|
||||
|
||||
private static Vector3D Cross(Vector3D a, Vector3D b)
|
||||
{
|
||||
return new Vector3D(
|
||||
a.Y * b.Z - a.Z * b.Y,
|
||||
a.Z * b.X - a.X * b.Z,
|
||||
a.X * b.Y - a.Y * b.X);
|
||||
}
|
||||
|
||||
private static double Dot(Vector3D a, Vector3D b)
|
||||
{
|
||||
return a.X * b.X + a.Y * b.Y + a.Z * b.Z;
|
||||
}
|
||||
|
||||
private static Vector3D Normalize(Vector3D vector)
|
||||
{
|
||||
double lengthSquared = VectorLengthSquared(vector);
|
||||
@ -355,30 +314,6 @@ namespace NavisworksTransport.Core
|
||||
return vector.X * vector.X + vector.Y * vector.Y + vector.Z * vector.Z;
|
||||
}
|
||||
|
||||
private static ModelItem FindFirstGeometryItem(ModelItem item)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
return null;
|
||||
}
|
||||
|
||||
if (item.HasGeometry)
|
||||
{
|
||||
return item;
|
||||
}
|
||||
|
||||
foreach (var child in item.Children)
|
||||
{
|
||||
var geometryItem = FindFirstGeometryItem(child);
|
||||
if (geometryItem != null)
|
||||
{
|
||||
return geometryItem;
|
||||
}
|
||||
}
|
||||
|
||||
return item;
|
||||
}
|
||||
|
||||
private static string GetReferenceRodResourcePath(out string resourceName)
|
||||
{
|
||||
string pluginDir = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
|
||||
|
||||
@ -439,7 +439,6 @@ namespace NavisworksTransport
|
||||
var edge = BuildStraightEdge(sortedPoints[0], sortedPoints[1], samplingStep);
|
||||
route.Edges.Add(edge);
|
||||
route.IsCurved = true;
|
||||
RecalculateRouteLength(route);
|
||||
return;
|
||||
}
|
||||
|
||||
@ -493,7 +492,6 @@ namespace NavisworksTransport
|
||||
route.Edges.Add(lastEdge);
|
||||
|
||||
route.IsCurved = true;
|
||||
RecalculateRouteLength(route);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
||||
@ -127,6 +127,11 @@ namespace NavisworksTransport
|
||||
/// </summary>
|
||||
RailBaseline,
|
||||
|
||||
/// <summary>
|
||||
/// 直线装配基准向量样式(绿色)
|
||||
/// </summary>
|
||||
AssemblyGuideLine,
|
||||
|
||||
/// <summary>
|
||||
/// 吊装路径样式(紫色)
|
||||
/// </summary>
|
||||
@ -1126,6 +1131,14 @@ namespace NavisworksTransport
|
||||
/// <param name="railModelId">空轨模型ID</param>
|
||||
/// <param name="baselinePoints">基准路径点列表</param>
|
||||
public void RenderRailBaseline(string railModelId, List<Point3D> baselinePoints)
|
||||
{
|
||||
RenderRailBaseline(railModelId, baselinePoints, RenderStyleName.RailBaseline);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 按指定样式渲染基准路径。
|
||||
/// </summary>
|
||||
public void RenderRailBaseline(string railModelId, List<Point3D> baselinePoints, RenderStyleName renderStyleName)
|
||||
{
|
||||
if (string.IsNullOrEmpty(railModelId) || baselinePoints == null || baselinePoints.Count < 2)
|
||||
{
|
||||
@ -1146,7 +1159,7 @@ namespace NavisworksTransport
|
||||
LastUpdated = DateTime.Now
|
||||
};
|
||||
|
||||
var renderStyle = GetRenderStyle(RenderStyleName.RailBaseline);
|
||||
var renderStyle = GetRenderStyle(renderStyleName);
|
||||
|
||||
// 创建连线标记
|
||||
for (int i = 0; i < baselinePoints.Count - 1; i++)
|
||||
@ -2328,6 +2341,9 @@ namespace NavisworksTransport
|
||||
case RenderStyleName.RailBaseline:
|
||||
return new RenderStyle(Color.FromByteRGB(255, 138, 128), 0.7); // 浅红色,30%透明
|
||||
|
||||
case RenderStyleName.AssemblyGuideLine:
|
||||
return new RenderStyle(Color.FromByteRGB(76, 175, 80), 0.85); // Material Green装配基准向量
|
||||
|
||||
case RenderStyleName.HoistingLine:
|
||||
return new RenderStyle(Color.FromByteRGB(156, 39, 176), 0.8); // Material Purple吊装路径,20%透明
|
||||
|
||||
|
||||
@ -121,7 +121,7 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private ObservableCollection<RailConfigOption<RailMountMode>> _railMountModeOptions;
|
||||
private ObservableCollection<RailConfigOption<RailPayloadAnchorMode>> _railPayloadAnchorModeOptions;
|
||||
private const double DefaultAssemblyReferenceRodLengthInMeters = 20.0;
|
||||
private const double DefaultAssemblyReferenceRodDiameterInMeters = 0.2;
|
||||
private static readonly double DefaultAssemblyReferenceRodDiameterInMeters = CalculateDefaultAssemblyReferenceRodDiameterInMeters();
|
||||
private string _assemblyTerminalObjectName = "未选择";
|
||||
private string _assemblyTerminalObjectInfo = "请选择终点处已安装箱体";
|
||||
private double _assemblyReferenceRodLengthInMeters = DefaultAssemblyReferenceRodLengthInMeters;
|
||||
@ -134,6 +134,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
private ModelItem _assemblyTerminalObject;
|
||||
private Point3D _assemblyStartPoint;
|
||||
private const string AssemblyAnchorMarkerPathId = "assembly_anchor_marker";
|
||||
private const string AssemblyCenterGuideLinePathId = "assembly_center_guide_line";
|
||||
private const string AssemblyReferenceLinePathId = "assembly_reference_line";
|
||||
|
||||
// 自动路径起点和终点的路径对象引用(用于正确的ID管理)
|
||||
private PathRoute _autoPathStartPointRoute = null;
|
||||
@ -1320,27 +1322,29 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
throw new InvalidOperationException("终点箱体未设置或已失效,请重新捕获终点箱体");
|
||||
}
|
||||
|
||||
var forward = ModelItemTransformHelper.GetForwardDirectionFromTransform(_assemblyTerminalObject.Transform);
|
||||
var endPoint = GetAssemblyTerminalAnchorPoint();
|
||||
double rodLength = UnitsConverter.ConvertFromMeters(AssemblyReferenceRodLengthInMeters);
|
||||
var startPoint = new Point3D(
|
||||
endPoint.X - forward.X * rodLength,
|
||||
endPoint.Y - forward.Y * rodLength,
|
||||
endPoint.Z - forward.Z * rodLength);
|
||||
AssemblyReferenceLine referenceLine = BuildAssemblyReferenceLine();
|
||||
|
||||
AssemblyReferencePathManager.Instance.CreateOrUpdateReferenceRod(
|
||||
startPoint,
|
||||
endPoint,
|
||||
referenceLine.StartPoint,
|
||||
referenceLine.EndPoint,
|
||||
AssemblyReferenceRodDiameterInMeters);
|
||||
|
||||
_assemblyStartPoint = Point3D.Origin;
|
||||
AssemblyStartPointText = "未选择";
|
||||
RefreshAssemblyTerminalObjectInfo(startPoint, endPoint);
|
||||
RefreshAssemblyTerminalObjectInfo(referenceLine.StartPoint, referenceLine.EndPoint);
|
||||
RenderAssemblyAnchorMarker();
|
||||
RenderAssemblyCenterGuideLine();
|
||||
RenderAssemblyReferenceLine(referenceLine);
|
||||
FocusOnAssemblyReferenceArea(referenceLine.StartPoint, referenceLine.EndPoint);
|
||||
|
||||
UpdateMainStatus("已生成直线装配参考杆,请在三维视图中确认位置");
|
||||
UpdateMainStatus("已生成直线装配参考杆,请在三维视图中确认终点锚点、方向、外端位置和杆体贴合情况");
|
||||
OnPropertyChanged(nameof(CanSelectAssemblyStartPoint));
|
||||
LogManager.Info($"[直线装配] 已生成参考杆: {AssemblyTerminalObjectName}");
|
||||
LogManager.Info(
|
||||
$"[直线装配] 已生成参考杆: {AssemblyTerminalObjectName}, " +
|
||||
$"终点锚点=({referenceLine.EndPoint.X:F2}, {referenceLine.EndPoint.Y:F2}, {referenceLine.EndPoint.Z:F2}), " +
|
||||
$"参考线外端=({referenceLine.StartPoint.X:F2}, {referenceLine.StartPoint.Y:F2}, {referenceLine.StartPoint.Z:F2}), " +
|
||||
$"方向=({referenceLine.Direction.X:F3}, {referenceLine.Direction.Y:F3}, {referenceLine.Direction.Z:F3}), " +
|
||||
$"资源={AssemblyReferencePathManager.Instance.ReferenceResourceName}");
|
||||
}, "生成装配参考杆");
|
||||
}
|
||||
|
||||
@ -1391,14 +1395,16 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
CleanupAssemblyReferenceSelection();
|
||||
AssemblyReferencePathManager.Instance.HideReferenceRod();
|
||||
ClearAssemblyAnchorMarker();
|
||||
ClearAssemblyCenterGuideLine();
|
||||
ClearAssemblyReferenceLine();
|
||||
AssemblyStartPointText = "未选择";
|
||||
OnPropertyChanged(nameof(CanSelectAssemblyStartPoint));
|
||||
UpdateMainStatus("已隐藏直线装配参考杆");
|
||||
LogManager.Info("[直线装配] 已隐藏参考杆");
|
||||
UpdateMainStatus("已隐藏直线装配参考线");
|
||||
LogManager.Info("[直线装配] 已隐藏参考线");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Error($"[直线装配] 隐藏参考杆失败: {ex.Message}");
|
||||
LogManager.Error($"[直线装配] 隐藏参考线失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
@ -1494,6 +1500,39 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
center.Z + up.Z * halfHeight * direction);
|
||||
}
|
||||
|
||||
private AssemblyReferenceLine BuildAssemblyReferenceLine()
|
||||
{
|
||||
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
|
||||
{
|
||||
throw new InvalidOperationException("终点箱体未设置或已失效,无法生成装配参考线");
|
||||
}
|
||||
|
||||
BoundingBox3D bounds = _assemblyTerminalObject.BoundingBox();
|
||||
Point3D centerPoint = bounds.Center;
|
||||
Point3D endPoint = GetAssemblyTerminalAnchorPoint();
|
||||
Vector3D direction = new Vector3D(centerPoint.X, centerPoint.Y, centerPoint.Z);
|
||||
double directionLengthSquared = direction.X * direction.X + direction.Y * direction.Y + direction.Z * direction.Z;
|
||||
if (directionLengthSquared < 1e-9)
|
||||
{
|
||||
throw new InvalidOperationException("箱体中心与世界原点重合,无法生成装配参考线方向");
|
||||
}
|
||||
|
||||
direction = direction.Normalize();
|
||||
double rodLength = UnitsConverter.ConvertFromMeters(AssemblyReferenceRodLengthInMeters);
|
||||
Point3D startPoint = new Point3D(
|
||||
endPoint.X + direction.X * rodLength,
|
||||
endPoint.Y + direction.Y * rodLength,
|
||||
endPoint.Z + direction.Z * rodLength);
|
||||
|
||||
LogManager.Info(
|
||||
$"[直线装配] 参考线已计算,终点锚点=({endPoint.X:F2}, {endPoint.Y:F2}, {endPoint.Z:F2}), " +
|
||||
$"箱体中心=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2}), " +
|
||||
$"参考线外端=({startPoint.X:F2}, {startPoint.Y:F2}, {startPoint.Z:F2}), " +
|
||||
$"原点到箱体中心方向=({direction.X:F3}, {direction.Y:F3}, {direction.Z:F3})");
|
||||
|
||||
return new AssemblyReferenceLine(startPoint, endPoint, direction);
|
||||
}
|
||||
|
||||
private void RefreshAssemblyTerminalObjectInfo(Point3D referenceStartPoint = null, Point3D referenceEndPoint = null)
|
||||
{
|
||||
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
|
||||
@ -1551,21 +1590,17 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
|
||||
try
|
||||
{
|
||||
Point3D endPoint = GetAssemblyTerminalAnchorPoint();
|
||||
Vector3D forward = ModelItemTransformHelper.GetForwardDirectionFromTransform(_assemblyTerminalObject.Transform);
|
||||
double rodLength = UnitsConverter.ConvertFromMeters(AssemblyReferenceRodLengthInMeters);
|
||||
Point3D startPoint = new Point3D(
|
||||
endPoint.X - forward.X * rodLength,
|
||||
endPoint.Y - forward.Y * rodLength,
|
||||
endPoint.Z - forward.Z * rodLength);
|
||||
AssemblyReferenceLine referenceLine = BuildAssemblyReferenceLine();
|
||||
|
||||
AssemblyReferencePathManager.Instance.CreateOrUpdateReferenceRod(
|
||||
startPoint,
|
||||
endPoint,
|
||||
referenceLine.StartPoint,
|
||||
referenceLine.EndPoint,
|
||||
AssemblyReferenceRodDiameterInMeters);
|
||||
|
||||
RefreshAssemblyTerminalObjectInfo(startPoint, endPoint);
|
||||
RefreshAssemblyTerminalObjectInfo(referenceLine.StartPoint, referenceLine.EndPoint);
|
||||
RenderAssemblyAnchorMarker();
|
||||
RenderAssemblyCenterGuideLine();
|
||||
RenderAssemblyReferenceLine(referenceLine);
|
||||
OnPropertyChanged(nameof(CanSelectAssemblyStartPoint));
|
||||
}
|
||||
catch (Exception ex)
|
||||
@ -1574,6 +1609,82 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
}
|
||||
|
||||
private sealed class AssemblyReferenceLine
|
||||
{
|
||||
public AssemblyReferenceLine(Point3D startPoint, Point3D endPoint, Vector3D direction)
|
||||
{
|
||||
StartPoint = startPoint;
|
||||
EndPoint = endPoint;
|
||||
Direction = direction;
|
||||
}
|
||||
|
||||
public Point3D StartPoint { get; }
|
||||
public Point3D EndPoint { get; }
|
||||
public Vector3D Direction { get; }
|
||||
}
|
||||
|
||||
private static double CalculateDefaultAssemblyReferenceRodDiameterInMeters()
|
||||
{
|
||||
double standardRadiusInMeters = ConfigManager.Instance.Current.PathEditing.CellSizeMeters;
|
||||
standardRadiusInMeters = Math.Max(0.1, Math.Min(0.5, standardRadiusInMeters));
|
||||
return standardRadiusInMeters * 0.8;
|
||||
}
|
||||
|
||||
private void FocusOnAssemblyReferenceArea(Point3D startPoint, Point3D endPoint)
|
||||
{
|
||||
try
|
||||
{
|
||||
double minX = Math.Min(startPoint.X, endPoint.X);
|
||||
double minY = Math.Min(startPoint.Y, endPoint.Y);
|
||||
double minZ = Math.Min(startPoint.Z, endPoint.Z);
|
||||
double maxX = Math.Max(startPoint.X, endPoint.X);
|
||||
double maxY = Math.Max(startPoint.Y, endPoint.Y);
|
||||
double maxZ = Math.Max(startPoint.Z, endPoint.Z);
|
||||
|
||||
if (_assemblyTerminalObject != null && ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
|
||||
{
|
||||
var terminalBounds = _assemblyTerminalObject.BoundingBox();
|
||||
minX = Math.Min(minX, terminalBounds.Min.X);
|
||||
minY = Math.Min(minY, terminalBounds.Min.Y);
|
||||
minZ = Math.Min(minZ, terminalBounds.Min.Z);
|
||||
maxX = Math.Max(maxX, terminalBounds.Max.X);
|
||||
maxY = Math.Max(maxY, terminalBounds.Max.Y);
|
||||
maxZ = Math.Max(maxZ, terminalBounds.Max.Z);
|
||||
}
|
||||
|
||||
Point3D center = new Point3D(
|
||||
(minX + maxX) / 2.0,
|
||||
(minY + maxY) / 2.0,
|
||||
(minZ + maxZ) / 2.0);
|
||||
|
||||
double targetSize = Math.Max(
|
||||
maxX - minX,
|
||||
Math.Max(maxY - minY, maxZ - minZ));
|
||||
|
||||
targetSize = Math.Max(targetSize, UnitsConverter.ConvertFromMeters(1.0));
|
||||
|
||||
ViewpointHelper.FocusOnPosition(center, targetSize, 45.0, 0.35);
|
||||
|
||||
var selection = new List<ModelItem>();
|
||||
if (_assemblyTerminalObject != null && ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
|
||||
{
|
||||
selection.Add(_assemblyTerminalObject);
|
||||
}
|
||||
|
||||
var referenceRod = AssemblyReferencePathManager.Instance.CurrentReferenceRod;
|
||||
if (referenceRod != null)
|
||||
{
|
||||
selection.Add(referenceRod);
|
||||
}
|
||||
|
||||
NavisworksSelectionHelper.SetModelSelection(selection);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"[直线装配] 聚焦参考杆区域失败: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private void RenderAssemblyAnchorMarker()
|
||||
{
|
||||
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
|
||||
@ -1588,6 +1699,8 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
}
|
||||
|
||||
Point3D anchorPoint = GetAssemblyTerminalAnchorPoint();
|
||||
LogManager.Info(
|
||||
$"[直线装配] 已渲染终点锚点标记: ({anchorPoint.X:F2}, {anchorPoint.Y:F2}, {anchorPoint.Z:F2})");
|
||||
var markerRoute = new PathRoute("装配对接点")
|
||||
{
|
||||
Id = AssemblyAnchorMarkerPathId,
|
||||
@ -1597,6 +1710,47 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
renderPlugin.RenderPointOnly(markerRoute);
|
||||
}
|
||||
|
||||
private void RenderAssemblyReferenceLine(AssemblyReferenceLine referenceLine)
|
||||
{
|
||||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||||
if (renderPlugin == null || referenceLine == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderPlugin.RenderRailBaseline(
|
||||
AssemblyReferenceLinePathId,
|
||||
new List<Point3D> { referenceLine.EndPoint, referenceLine.StartPoint });
|
||||
|
||||
LogManager.Info(
|
||||
$"[直线装配] 已渲染参考线: 终点锚点=({referenceLine.EndPoint.X:F2}, {referenceLine.EndPoint.Y:F2}, {referenceLine.EndPoint.Z:F2}), " +
|
||||
$"参考线外端=({referenceLine.StartPoint.X:F2}, {referenceLine.StartPoint.Y:F2}, {referenceLine.StartPoint.Z:F2})");
|
||||
}
|
||||
|
||||
private void RenderAssemblyCenterGuideLine()
|
||||
{
|
||||
if (_assemblyTerminalObject == null || !ModelItemAnalysisHelper.IsModelItemValid(_assemblyTerminalObject))
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||||
if (renderPlugin == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
Point3D centerPoint = _assemblyTerminalObject.BoundingBox().Center;
|
||||
renderPlugin.RenderRailBaseline(
|
||||
AssemblyCenterGuideLinePathId,
|
||||
new List<Point3D> { Point3D.Origin, centerPoint },
|
||||
RenderStyleName.AssemblyGuideLine);
|
||||
|
||||
LogManager.Info(
|
||||
$"[直线装配] 已渲染原点到箱体中心基准线: 原点=(0.00, 0.00, 0.00), " +
|
||||
$"箱体中心=({centerPoint.X:F2}, {centerPoint.Y:F2}, {centerPoint.Z:F2})");
|
||||
}
|
||||
|
||||
private void ClearAssemblyAnchorMarker()
|
||||
{
|
||||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||||
@ -1608,6 +1762,28 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
renderPlugin.RemovePath(AssemblyAnchorMarkerPathId);
|
||||
}
|
||||
|
||||
private void ClearAssemblyReferenceLine()
|
||||
{
|
||||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||||
if (renderPlugin == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderPlugin.ClearRailBaseline(AssemblyReferenceLinePathId);
|
||||
}
|
||||
|
||||
private void ClearAssemblyCenterGuideLine()
|
||||
{
|
||||
var renderPlugin = PathPointRenderPlugin.Instance;
|
||||
if (renderPlugin == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
renderPlugin.ClearRailBaseline(AssemblyCenterGuideLinePathId);
|
||||
}
|
||||
|
||||
private void CleanupAssemblyReferenceSelection()
|
||||
{
|
||||
try
|
||||
@ -5071,6 +5247,15 @@ namespace NavisworksTransport.UI.WPF.ViewModels
|
||||
LogManager.Warning($"清理直线装配对接点标记时发生异常: {ex.Message}");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
ClearAssemblyReferenceLine();
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
LogManager.Warning($"清理直线装配参考线时发生异常: {ex.Message}");
|
||||
}
|
||||
|
||||
// 确保停止任何活动的点击工具
|
||||
try
|
||||
{
|
||||
|
||||
@ -152,6 +152,50 @@ namespace NavisworksTransport.Utils
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 根据箱体局部尺寸和世界原点方向,推导“背离球心”的真实长轴方向。
|
||||
/// 先选局部最长轴,再用箱体中心相对世界原点的方向决定正负。
|
||||
/// </summary>
|
||||
public static Vector3D GetLongestAxisDirectionAwayFromOrigin(BoundingBox3D bounds, Transform3D transform)
|
||||
{
|
||||
Vector3D localSize = EstimateLocalBoxSize(bounds, transform);
|
||||
|
||||
int longestAxisIndex = 0;
|
||||
double longestAxisLength = localSize.X;
|
||||
if (localSize.Y > longestAxisLength)
|
||||
{
|
||||
longestAxisIndex = 1;
|
||||
longestAxisLength = localSize.Y;
|
||||
}
|
||||
|
||||
if (localSize.Z > longestAxisLength)
|
||||
{
|
||||
longestAxisIndex = 2;
|
||||
}
|
||||
|
||||
Vector3D axisDirection = GetAxisDirectionFromTransform(
|
||||
transform,
|
||||
longestAxisIndex,
|
||||
longestAxisIndex == 0 ? new Vector3D(1, 0, 0) :
|
||||
longestAxisIndex == 1 ? new Vector3D(0, 1, 0) :
|
||||
new Vector3D(0, 0, 1));
|
||||
|
||||
Vector3D awayFromOrigin = new Vector3D(bounds.Center.X, bounds.Center.Y, bounds.Center.Z);
|
||||
double awayLengthSquared = awayFromOrigin.X * awayFromOrigin.X + awayFromOrigin.Y * awayFromOrigin.Y + awayFromOrigin.Z * awayFromOrigin.Z;
|
||||
if (awayLengthSquared < 1e-9)
|
||||
{
|
||||
return axisDirection;
|
||||
}
|
||||
|
||||
double dot = axisDirection.X * awayFromOrigin.X + axisDirection.Y * awayFromOrigin.Y + axisDirection.Z * awayFromOrigin.Z;
|
||||
if (dot < 0.0)
|
||||
{
|
||||
return new Vector3D(-axisDirection.X, -axisDirection.Y, -axisDirection.Z);
|
||||
}
|
||||
|
||||
return axisDirection;
|
||||
}
|
||||
|
||||
private static Vector3D GetAxisDirectionFromTransform(Transform3D transform, int axisIndex, Vector3D fallback)
|
||||
{
|
||||
if (transform == null)
|
||||
@ -317,6 +361,168 @@ namespace NavisworksTransport.Utils
|
||||
ApplyAbsoluteTransform(item, targetPosition, targetRotation, preserveCurrentScale: true);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将物体移动到指定中心点和完整三维朝向,同时保留当前缩放。
|
||||
/// 适用于参考杆、辅助几何等以几何中心作为定位基准的场景。
|
||||
/// </summary>
|
||||
public static void MoveItemToCenterAndRotationWithCurrentScale(ModelItem item, Point3D targetCenter, Rotation3D targetRotation)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { item };
|
||||
|
||||
var currentComponents = item.Transform.Factor();
|
||||
Vector3D currentScale = currentComponents.Scale;
|
||||
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
var originalBounds = item.BoundingBox();
|
||||
Point3D originalCenter = originalBounds.Center;
|
||||
|
||||
var rotationTransform = new Transform3D(targetRotation);
|
||||
var linear = rotationTransform.Linear;
|
||||
|
||||
var rotatedCenter = new Point3D(
|
||||
linear.Get(0, 0) * originalCenter.X + linear.Get(0, 1) * originalCenter.Y + linear.Get(0, 2) * originalCenter.Z,
|
||||
linear.Get(1, 0) * originalCenter.X + linear.Get(1, 1) * originalCenter.Y + linear.Get(1, 2) * originalCenter.Z,
|
||||
linear.Get(2, 0) * originalCenter.X + linear.Get(2, 1) * originalCenter.Y + linear.Get(2, 2) * originalCenter.Z);
|
||||
|
||||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||||
var components = identity.Factor();
|
||||
components.Scale = currentScale;
|
||||
components.Rotation = targetRotation;
|
||||
components.Translation = new Vector3D(
|
||||
targetCenter.X - rotatedCenter.X,
|
||||
targetCenter.Y - rotatedCenter.Y,
|
||||
targetCenter.Z - rotatedCenter.Z);
|
||||
|
||||
doc.Models.OverridePermanentTransform(modelItems, components.Combine(), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将物体移动到指定中心点和完整三维朝向。
|
||||
/// 仅应用位置和旋转,不保留当前缩放。
|
||||
/// 适用于缩放已经在 Model 层完成的参考杆等场景。
|
||||
/// </summary>
|
||||
public static void MoveItemToCenterAndRotation(ModelItem item, Point3D targetCenter, Rotation3D targetRotation)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { item };
|
||||
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
var originalBounds = item.BoundingBox();
|
||||
Point3D originalCenter = originalBounds.Center;
|
||||
|
||||
var rotationTransform = new Transform3D(targetRotation);
|
||||
var linear = rotationTransform.Linear;
|
||||
|
||||
var rotatedCenter = new Point3D(
|
||||
linear.Get(0, 0) * originalCenter.X + linear.Get(0, 1) * originalCenter.Y + linear.Get(0, 2) * originalCenter.Z,
|
||||
linear.Get(1, 0) * originalCenter.X + linear.Get(1, 1) * originalCenter.Y + linear.Get(1, 2) * originalCenter.Z,
|
||||
linear.Get(2, 0) * originalCenter.X + linear.Get(2, 1) * originalCenter.Y + linear.Get(2, 2) * originalCenter.Z);
|
||||
|
||||
var translation = new Vector3D(
|
||||
targetCenter.X - rotatedCenter.X,
|
||||
targetCenter.Y - rotatedCenter.Y,
|
||||
targetCenter.Z - rotatedCenter.Z);
|
||||
|
||||
doc.Models.OverridePermanentTransform(modelItems, new Transform3D(targetRotation, translation), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将物体的局部正 X 端面中心对齐到目标点,并保留当前缩放。
|
||||
/// 适用于单位立方体/单位圆柱体沿 +X 定义长度方向的参考杆资源。
|
||||
/// </summary>
|
||||
public static void MoveItemPositiveXEndToPointAndRotationWithCurrentScale(ModelItem item, Point3D targetPoint, Rotation3D targetRotation)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { item };
|
||||
|
||||
var currentComponents = item.Transform.Factor();
|
||||
Vector3D currentScale = currentComponents.Scale;
|
||||
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
var originalBounds = item.BoundingBox();
|
||||
Point3D originalPositiveXEndCenter = new Point3D(
|
||||
originalBounds.Max.X,
|
||||
originalBounds.Center.Y,
|
||||
originalBounds.Center.Z);
|
||||
|
||||
var rotationTransform = new Transform3D(targetRotation);
|
||||
var linear = rotationTransform.Linear;
|
||||
|
||||
var rotatedPositiveXEndCenter = new Point3D(
|
||||
linear.Get(0, 0) * originalPositiveXEndCenter.X + linear.Get(0, 1) * originalPositiveXEndCenter.Y + linear.Get(0, 2) * originalPositiveXEndCenter.Z,
|
||||
linear.Get(1, 0) * originalPositiveXEndCenter.X + linear.Get(1, 1) * originalPositiveXEndCenter.Y + linear.Get(1, 2) * originalPositiveXEndCenter.Z,
|
||||
linear.Get(2, 0) * originalPositiveXEndCenter.X + linear.Get(2, 1) * originalPositiveXEndCenter.Y + linear.Get(2, 2) * originalPositiveXEndCenter.Z);
|
||||
|
||||
var identity = Transform3D.CreateTranslation(new Vector3D(0, 0, 0));
|
||||
var components = identity.Factor();
|
||||
components.Scale = currentScale;
|
||||
components.Rotation = targetRotation;
|
||||
components.Translation = new Vector3D(
|
||||
targetPoint.X - rotatedPositiveXEndCenter.X,
|
||||
targetPoint.Y - rotatedPositiveXEndCenter.Y,
|
||||
targetPoint.Z - rotatedPositiveXEndCenter.Z);
|
||||
|
||||
doc.Models.OverridePermanentTransform(modelItems, components.Combine(), false);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 将物体的局部正 X 端面中心对齐到目标点。
|
||||
/// 仅应用位置和旋转,不保留当前缩放。
|
||||
/// 适用于缩放已经在 Model 层完成、Item 层只负责定位的场景。
|
||||
/// </summary>
|
||||
public static void MoveItemPositiveXEndToPointAndRotation(ModelItem item, Point3D targetPoint, Rotation3D targetRotation)
|
||||
{
|
||||
if (item == null)
|
||||
{
|
||||
throw new ArgumentNullException(nameof(item));
|
||||
}
|
||||
|
||||
var doc = Application.ActiveDocument;
|
||||
var modelItems = new ModelItemCollection { item };
|
||||
|
||||
doc.Models.ResetPermanentTransform(modelItems);
|
||||
|
||||
var originalBounds = item.BoundingBox();
|
||||
Point3D originalPositiveXEndCenter = new Point3D(
|
||||
originalBounds.Max.X,
|
||||
originalBounds.Center.Y,
|
||||
originalBounds.Center.Z);
|
||||
|
||||
var rotationTransform = new Transform3D(targetRotation);
|
||||
var linear = rotationTransform.Linear;
|
||||
|
||||
var rotatedPositiveXEndCenter = new Point3D(
|
||||
linear.Get(0, 0) * originalPositiveXEndCenter.X + linear.Get(0, 1) * originalPositiveXEndCenter.Y + linear.Get(0, 2) * originalPositiveXEndCenter.Z,
|
||||
linear.Get(1, 0) * originalPositiveXEndCenter.X + linear.Get(1, 1) * originalPositiveXEndCenter.Y + linear.Get(1, 2) * originalPositiveXEndCenter.Z,
|
||||
linear.Get(2, 0) * originalPositiveXEndCenter.X + linear.Get(2, 1) * originalPositiveXEndCenter.Y + linear.Get(2, 2) * originalPositiveXEndCenter.Z);
|
||||
|
||||
var translation = new Vector3D(
|
||||
targetPoint.X - rotatedPositiveXEndCenter.X,
|
||||
targetPoint.Y - rotatedPositiveXEndCenter.Y,
|
||||
targetPoint.Z - rotatedPositiveXEndCenter.Z);
|
||||
|
||||
doc.Models.OverridePermanentTransform(modelItems, new Transform3D(targetRotation, translation), false);
|
||||
}
|
||||
|
||||
private static void ApplyAbsoluteTransform(ModelItem item, Point3D targetPosition, Rotation3D targetRotation, bool preserveCurrentScale)
|
||||
{
|
||||
var doc = Application.ActiveDocument;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user