feat: port p-rec-annot ortho dimension workflow

This commit is contained in:
codex
2026-08-07 15:34:19 +03:00
parent 8510974d75
commit 64ace55338
4 changed files with 114 additions and 2 deletions
@@ -1,5 +1,5 @@
<?xml version="1.0" encoding="utf-8"?>
<ApplicationPackage SchemaVersion="1.0" Name="P_Rec_Annot" Description="P_Rec_Annot Plant 3D 2026" AppVersion="0.2.6" ProductCode="b455ee6eaed79d76f084846a60ce3284" Author="NBIM">
<ApplicationPackage SchemaVersion="1.0" Name="P_Rec_Annot" Description="P_Rec_Annot Plant 3D 2026" AppVersion="0.2.7" ProductCode="b455ee6eaed79d76f084846a60ce3284" Author="NBIM">
<Components>
<!-- SeriesMin/SeriesMax and platform token are pending official/runtime verification. -->
<RuntimeRequirements OS="Win64" />
@@ -55,6 +55,7 @@ public sealed class EntryPoint : IExtensionApplication
[CommandMethod("PXP-UOB")] public void UpdateOrthoViewBlocksCommand() => PRecAnnot.Plant3D2026.UpdateOrthoViewBlocksCommand.Run();
[CommandMethod("PXP-RA", CommandFlags.Session)] public void RemoveOrthoAnnotationsCommand() => OrthoAnnotationRemovalCommand.Run();
[CommandMethod("PXP-OSI", CommandFlags.Session)] public void ShowOrthoSourceInModelCommand() => OrthoSourceNavigationCommand.Run();
[CommandMethod("PXP-DJ")] public void DimensionOrthoAnnotationsCommand() => OrthoDimensionCommand.Run();
[CommandMethod("PXP-AC", CommandFlags.UsePickSet)] public void SwapAttributesCommand() => PRecAnnot.Plant3D2026.SwapAttributesCommand.Run();
[CommandMethod("PXP-IOD", CommandFlags.Session)] public void UpdateOrthoDrawingsCommand() => PRecAnnot.Plant3D2026.UpdateOrthoDrawingsCommand.Run();
[CommandMethod("mto-ma")] public void ShowManualAnnotationPaletteCommand() => ManualAnnotationPalette.Show();
@@ -0,0 +1,111 @@
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Application;
namespace PRecAnnot.Plant3D2026;
// Source-backed PXP-DJ mutation boundary. The legacy command dimensions the
// selected PXP-OA annotation blocks, rejects mixed viewport scales and keeps
// all dimension creation in one transaction so native UNDO restores the prior
// state. The Plant-specific candidate discovery remains deliberately typed
// through the PXP-OA ownership contract.
internal static class OrthoDimensionCommand
{
private const string AnnotationApp = "PXP-OA";
private const string DimensionApp = "PXP-DJ";
public static void Run()
{
var document = AcadApplication.DocumentManager.MdiActiveDocument;
if (document is null) return;
var selection = document.Editor.GetSelection(new SelectionFilter(new[] { new TypedValue((int)DxfCode.ExtendedDataRegAppName, AnnotationApp) }));
if (selection.Status != PromptStatus.OK || selection.Value.Count < 2)
{
document.Editor.WriteMessage("\nSelect at least two PXP-OA annotations.");
return;
}
using var read = document.Database.TransactionManager.StartOpenCloseTransaction();
var points = new List<(ObjectId Id, Point3d Position, string Viewport)>();
foreach (var id in selection.Value.GetObjectIds())
{
if (read.GetObject(id, OpenMode.ForRead) is not BlockReference reference) continue;
if (!TryReadViewport(reference, out var viewport)) continue;
points.Add((id, reference.Position, viewport));
}
read.Commit();
if (points.Count < 2 || points.Select(point => point.Viewport).Distinct(StringComparer.Ordinal).Count() != 1)
{
document.Editor.WriteMessage("\nSelected annotations must belong to one Ortho viewport.");
return;
}
points = points.OrderBy(point => point.Position.X).ThenBy(point => point.Position.Y).ThenBy(point => point.Id.Handle.Value).ToList();
var defaultAngle = Math.Abs(points[^1].Position.X - points[0].Position.X) >= Math.Abs(points[^1].Position.Y - points[0].Position.Y) ? 0d : Math.PI / 2d;
var angle = document.Editor.GetAngle(new PromptAngleOptions("\nDimension rotation <automatic>: ") { AllowNone = true });
if (angle.Status is not (PromptStatus.OK or PromptStatus.None)) return;
var rotation = angle.Status == PromptStatus.OK ? angle.Value : defaultAngle;
var offset = document.Editor.GetPoint("\nDimension line location: ");
if (offset.Status != PromptStatus.OK) return;
using var documentLock = document.LockDocument();
using var transaction = document.Database.TransactionManager.StartTransaction();
var table = (BlockTable)transaction.GetObject(document.Database.BlockTableId, OpenMode.ForRead);
var paper = (BlockTableRecord)transaction.GetObject(table[BlockTableRecord.PaperSpace], OpenMode.ForWrite);
EnsureRegApp(transaction, document.Database);
EraseOwnedDimensions(transaction, paper, points[0].Viewport, points.Select(point => point.Id.Handle.ToString()).ToHashSet(StringComparer.OrdinalIgnoreCase));
for (var index = 1; index < points.Count; index++)
{
var first = points[index - 1].Position;
var second = points[index].Position;
var dimension = new RotatedDimension(rotation, first, second, offset.Value, string.Empty, document.Database.Dimstyle);
dimension.LayerId = document.Database.Clayer;
dimension.XData = Ownership(points[0].Viewport, points.Select(point => point.Id.Handle.ToString()));
paper.AppendEntity(dimension);
transaction.AddNewlyCreatedDBObject(dimension, true);
}
transaction.Commit();
document.Editor.WriteMessage($"\nCreated {points.Count - 1} Ortho dimensions; native UNDO is available.");
}
private static bool TryReadViewport(BlockReference reference, out string viewport)
{
viewport = string.Empty;
using var data = reference.GetXDataForApplication(AnnotationApp);
var values = data?.AsArray().Where(value => value.TypeCode == (int)DxfCode.ExtendedDataAsciiString).Select(value => value.Value?.ToString() ?? string.Empty).ToArray();
if (values is null) return false;
for (var index = 0; index + 1 < values.Length; index++) if (string.Equals(values[index], "VIEWPORT", StringComparison.Ordinal)) { viewport = values[index + 1]; break; }
return !string.IsNullOrWhiteSpace(viewport);
}
private static void EraseOwnedDimensions(Transaction transaction, BlockTableRecord paper, string viewport, HashSet<string> handles)
{
foreach (ObjectId id in paper)
{
if (transaction.GetObject(id, OpenMode.ForRead) is not Entity entity) continue;
using var data = entity.GetXDataForApplication(DimensionApp);
var values = data?.AsArray().Where(value => value.TypeCode == (int)DxfCode.ExtendedDataAsciiString).Select(value => value.Value?.ToString() ?? string.Empty).ToArray();
if (values is null || !values.Contains(viewport, StringComparer.Ordinal)) continue;
if (!handles.Any(handle => values.Contains(handle, StringComparer.OrdinalIgnoreCase))) continue;
entity.UpgradeOpen();
entity.Erase();
}
}
private static ResultBuffer Ownership(string viewport, IEnumerable<string> handles) => new(
new TypedValue((int)DxfCode.ExtendedDataRegAppName, DimensionApp),
new TypedValue((int)DxfCode.ExtendedDataAsciiString, viewport),
new TypedValue((int)DxfCode.ExtendedDataAsciiString, "SOURCES"),
new TypedValue((int)DxfCode.ExtendedDataAsciiString, string.Join(",", handles)));
private static void EnsureRegApp(Transaction transaction, Database database)
{
var table = (RegAppTable)transaction.GetObject(database.RegAppTableId, OpenMode.ForRead);
if (table.Has(DimensionApp)) return;
table.UpgradeOpen();
using var record = new RegAppTableRecord { Name = DimensionApp };
table.Add(record);
transaction.AddNewlyCreatedDBObject(record, true);
}
}
@@ -16,6 +16,6 @@ $moduleManifestPath=Join-Path $bundleRoot 'Contents\module-manifest.json'
$files=Get-ChildItem $bundleRoot -Recurse -File | Where-Object { $_.FullName -ne $moduleManifestPath } | Sort-Object FullName | ForEach-Object {
[ordered]@{ relativePath=$_.FullName.Substring($bundleRoot.Length + 1).Replace('\','/'); sha256=(Get-FileHash $_.FullName -Algorithm SHA256).Hash.ToLowerInvariant(); required=$true }
}
$manifest=[ordered]@{ schemaVersion=2; package='P_Rec_Annot'; version='0.2.6'; modules=$modules; assemblies=$assemblies; files=$files }
$manifest=[ordered]@{ schemaVersion=2; package='P_Rec_Annot'; version='0.2.7'; modules=$modules; assemblies=$assemblies; files=$files }
$manifest | ConvertTo-Json -Depth 5 | Set-Content (Join-Path $bundleRoot 'Contents\module-manifest.json') -Encoding utf8
Write-Host 'Bundle assembly build completed; deployment is intentionally not performed.'