feat: add p-rec-annot Plant component geometry adapter
This commit is contained in:
@@ -87,6 +87,20 @@ public sealed record GeometrySnapshot
|
|||||||
public double? Elevation { get; init; }
|
public double? Elevation { get; init; }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Platform-neutral result of the legacy GetComponentGeometry contract. The
|
||||||
|
// Plant adapter is responsible for reading Parts/Pipes and normalising units;
|
||||||
|
// consumers do not take a dependency on Autodesk managed assemblies.
|
||||||
|
public sealed record ComponentGeometryDefinition(
|
||||||
|
string Kind,
|
||||||
|
IReadOnlyDictionary<string, string> Parameters,
|
||||||
|
Bounds3Dto Bounds,
|
||||||
|
bool ImperialDimensions);
|
||||||
|
public sealed record ComponentGeometryReadResult(bool Success, ComponentGeometryDefinition? Geometry, string? FailureCode = null);
|
||||||
|
public interface IPlantComponentGeometryReader
|
||||||
|
{
|
||||||
|
ComponentGeometryReadResult Read(string stableEntityId, bool convertToMetric);
|
||||||
|
}
|
||||||
|
|
||||||
public sealed record OrthoViewportInfo
|
public sealed record OrthoViewportInfo
|
||||||
{
|
{
|
||||||
public required string Id { get; init; }
|
public required string Id { get; init; }
|
||||||
|
|||||||
@@ -14,6 +14,7 @@
|
|||||||
<Reference Include="PnPDataObjects"><HintPath>$(AutoCAD2026ManagedDir)\PnPDataObjects.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
<Reference Include="PnPDataObjects"><HintPath>$(AutoCAD2026ManagedDir)\PnPDataObjects.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||||
<Reference Include="PnPProjectManagerMgd"><HintPath>$(Plant3D2026ManagedDir)\PnPProjectManagerMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
<Reference Include="PnPProjectManagerMgd"><HintPath>$(Plant3D2026ManagedDir)\PnPProjectManagerMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||||
<Reference Include="PnP3dObjectsMgd"><HintPath>$(Plant3D2026ManagedDir)\PnP3dObjectsMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
<Reference Include="PnP3dObjectsMgd"><HintPath>$(Plant3D2026ManagedDir)\PnP3dObjectsMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||||
|
<Reference Include="PnP3dPartsRepository"><HintPath>$(AutoCAD2026ManagedDir)\PnP3dPartsRepository.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||||
<Reference Include="PnP3dProjectPartsMgd"><HintPath>$(Plant3D2026ManagedDir)\PnP3dProjectPartsMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
<Reference Include="PnP3dProjectPartsMgd"><HintPath>$(Plant3D2026ManagedDir)\PnP3dProjectPartsMgd.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||||
<Reference Include="PnP3dOrthoProjectPart"><HintPath>$(Plant3D2026ManagedDir)\PnP3dOrthoProjectPart.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
<Reference Include="PnP3dOrthoProjectPart"><HintPath>$(Plant3D2026ManagedDir)\PnP3dOrthoProjectPart.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||||
<Reference Include="PnPDwg2dDef"><HintPath>$(Plant3D2026ManagedDir)\PnPDwg2dDef.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
<Reference Include="PnPDwg2dDef"><HintPath>$(Plant3D2026ManagedDir)\PnPDwg2dDef.dll</HintPath><Private>false</Private><CopyLocal>false</CopyLocal></Reference>
|
||||||
|
|||||||
@@ -0,0 +1,60 @@
|
|||||||
|
using Autodesk.AutoCAD.DatabaseServices;
|
||||||
|
using Autodesk.ProcessPower.PnP3dObjects;
|
||||||
|
using PRecAnnot.Abstractions;
|
||||||
|
|
||||||
|
namespace PRecAnnot.Plant3D2026;
|
||||||
|
|
||||||
|
// Modern Plant 2026 adapter for the production-useful part of legacy
|
||||||
|
// GetComponentGeometry.IsGeometryRead: Pipe dimensions and Part extents are
|
||||||
|
// read from the selected source object with explicit unit normalisation.
|
||||||
|
internal sealed class PlantComponentGeometryReader(Database database) : IPlantComponentGeometryReader
|
||||||
|
{
|
||||||
|
public ComponentGeometryReadResult Read(string stableEntityId, bool convertToMetric)
|
||||||
|
{
|
||||||
|
if (!TryObjectId(stableEntityId, out var id)) return new(false, null, "COMPONENT_GEOMETRY_STABLE_ID_INVALID");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
using var transaction = database.TransactionManager.StartOpenCloseTransaction();
|
||||||
|
if (transaction.GetObject(id, OpenMode.ForRead) is not Entity entity) return new(false, null, "COMPONENT_GEOMETRY_ENTITY_NOT_FOUND");
|
||||||
|
var extents = entity.GeometricExtents;
|
||||||
|
var bounds = new Bounds3Dto(new(extents.MinPoint.X, extents.MinPoint.Y, extents.MinPoint.Z), new(extents.MaxPoint.X, extents.MaxPoint.Y, extents.MaxPoint.Z));
|
||||||
|
if (entity is Pipe pipe)
|
||||||
|
{
|
||||||
|
var factor = Factor(convertToMetric, pipe.PartSizeProperties.LinearUnit);
|
||||||
|
var parameters = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
["D"] = Format(pipe.OuterDiameter * factor),
|
||||||
|
["L"] = Format(pipe.Length * factor)
|
||||||
|
};
|
||||||
|
return new(true, new("CPP", parameters, bounds, factor != 1));
|
||||||
|
}
|
||||||
|
if (entity is Part part)
|
||||||
|
{
|
||||||
|
var factor = Factor(convertToMetric, part.PartSizeProperties.LinearUnit);
|
||||||
|
var parameters = new Dictionary<string, string>(StringComparer.Ordinal)
|
||||||
|
{
|
||||||
|
["BoundsX"] = Format((extents.MaxPoint.X - extents.MinPoint.X) * factor),
|
||||||
|
["BoundsY"] = Format((extents.MaxPoint.Y - extents.MinPoint.Y) * factor),
|
||||||
|
["BoundsZ"] = Format((extents.MaxPoint.Z - extents.MinPoint.Z) * factor)
|
||||||
|
};
|
||||||
|
return new(true, new("PART", parameters, bounds, factor != 1));
|
||||||
|
}
|
||||||
|
return new(false, null, "COMPONENT_GEOMETRY_UNSUPPORTED_ENTITY");
|
||||||
|
}
|
||||||
|
catch (Autodesk.AutoCAD.Runtime.Exception) { return new(false, null, "COMPONENT_GEOMETRY_EXTENTS_UNAVAILABLE"); }
|
||||||
|
catch (Exception) { return new(false, null, "COMPONENT_GEOMETRY_READ_FAILED"); }
|
||||||
|
}
|
||||||
|
|
||||||
|
private bool TryObjectId(string stableEntityId, out ObjectId id)
|
||||||
|
{
|
||||||
|
id = ObjectId.Null;
|
||||||
|
const string prefix = "acad:";
|
||||||
|
if (string.IsNullOrWhiteSpace(stableEntityId) || !stableEntityId.StartsWith(prefix, StringComparison.OrdinalIgnoreCase)) return false;
|
||||||
|
if (!long.TryParse(stableEntityId[prefix.Length..], System.Globalization.NumberStyles.HexNumber, System.Globalization.CultureInfo.InvariantCulture, out var handle)) return false;
|
||||||
|
try { id = database.GetObjectId(false, new Handle(handle), 0); return !id.IsNull; }
|
||||||
|
catch { return false; }
|
||||||
|
}
|
||||||
|
|
||||||
|
private static double Factor(bool convertToMetric, object? unit) => convertToMetric && string.Equals(unit?.ToString(), "Inch", StringComparison.OrdinalIgnoreCase) ? 25.4 : 1d;
|
||||||
|
private static string Format(double value) => value.ToString("0.###############", System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
@@ -0,0 +1,15 @@
|
|||||||
|
using PRecAnnot.Abstractions;
|
||||||
|
|
||||||
|
namespace PRecAnnot.Core.Tests;
|
||||||
|
|
||||||
|
public sealed class ComponentGeometryContractTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void GeometryDefinition_PreservesStableParametersAndBounds()
|
||||||
|
{
|
||||||
|
var geometry = new ComponentGeometryDefinition("CPP", new Dictionary<string, string> { ["D"] = "25.4", ["L"] = "508" }, new(new(0, 0, 0), new(1, 2, 3)), true);
|
||||||
|
Assert.Equal("25.4", geometry.Parameters["D"]);
|
||||||
|
Assert.Equal(new Point3Dto(1, 2, 3), geometry.Bounds.Maximum);
|
||||||
|
Assert.True(geometry.ImperialDimensions);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user