feat: port p-rec-annot elevation subsystem

This commit is contained in:
codex
2026-08-07 11:58:13 +03:00
parent 532c9dc1f2
commit b69a858f8e
12 changed files with 327 additions and 10 deletions
@@ -0,0 +1,104 @@
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using PRecAnnot.Abstractions;
namespace PRecAnnot.Plant3D2026;
internal sealed class ElevationAnnotationWriter
{
private const string ApplicationName = "PXP-AE";
public IReadOnlyList<ExistingElevationAnnotation> ReadExisting(Document document)
{
var result = new List<ExistingElevationAnnotation>();
using var transaction = document.Database.TransactionManager.StartOpenCloseTransaction();
var blocks = (BlockTable)transaction.GetObject(document.Database.BlockTableId, OpenMode.ForRead);
var paper = (BlockTableRecord)transaction.GetObject(blocks[BlockTableRecord.PaperSpace], OpenMode.ForRead);
foreach (ObjectId id in paper)
{
if (transaction.GetObject(id, OpenMode.ForRead) is not BlockReference reference) continue;
var data = reference.GetXDataForApplication(ApplicationName)?.AsArray();
if (data is null) continue;
var strings = data.Where(x => x.TypeCode == (int)DxfCode.ExtendedDataAsciiString).Select(x => x.Value?.ToString() ?? string.Empty).ToArray();
var source = Value(strings, "SOURCE"); var text = Value(strings, "ANNOTATIONTEXT");
if (source is null || text is null) continue;
result.Add(new ExistingElevationAnnotation(reference.Handle.ToString(), source, text, new AnnotationPoint(reference.Position.X, reference.Position.Y, reference.Position.Z)));
}
return result.OrderBy(x => x.SourceStableId, StringComparer.Ordinal).ToArray();
}
public ElevationApplyResult Apply(Document document, IReadOnlyList<ElevationAnnotationOperation> operations, ElevationAssets assets, bool manual = false)
{
using var documentLock = document.LockDocument();
using var transaction = document.Database.TransactionManager.StartTransaction();
var blocks = (BlockTable)transaction.GetObject(document.Database.BlockTableId, OpenMode.ForRead);
var layers = (LayerTable)transaction.GetObject(document.Database.LayerTableId, OpenMode.ForRead);
var styles = (TextStyleTable)transaction.GetObject(document.Database.TextStyleTableId, OpenMode.ForRead);
if (!blocks.Has(assets.BlockName)) throw new InvalidOperationException("ELEVATION_BLOCK_MISSING:" + assets.BlockName);
if (!layers.Has(assets.Layer)) throw new InvalidOperationException("ELEVATION_LAYER_MISSING:" + assets.Layer);
if (!styles.Has(assets.TextStyle)) throw new InvalidOperationException("ELEVATION_TEXT_STYLE_MISSING:" + assets.TextStyle);
EnsureRegApp(transaction, document.Database);
var paper = (BlockTableRecord)transaction.GetObject(blocks[BlockTableRecord.PaperSpace], OpenMode.ForWrite);
var definition = (BlockTableRecord)transaction.GetObject(blocks[assets.BlockName], OpenMode.ForRead);
var owned = paper.Cast<ObjectId>().Select(id => transaction.GetObject(id, OpenMode.ForRead)).OfType<BlockReference>()
.Select(reference => (reference, source: Source(reference))).Where(x => x.source is not null)
.GroupBy(x => x.source!, StringComparer.OrdinalIgnoreCase).ToDictionary(x => x.Key, x => x.ToArray(), StringComparer.OrdinalIgnoreCase);
var created = 0; var updated = 0; var unchanged = 0;
foreach (var operation in operations.Where(x => x.Kind is AnnotationOperationKind.Create or AnnotationOperationKind.Update))
{
if (operation.Text is null) continue;
var matches = owned.GetValueOrDefault(operation.StableId, []);
BlockReference reference;
if (matches.Length == 0)
{
reference = new BlockReference(ToPoint(operation.Position), definition.ObjectId) { LayerId = layers[assets.Layer], Rotation = operation.Rotation };
paper.AppendEntity(reference); transaction.AddNewlyCreatedDBObject(reference, true); created++;
AddAttributes(transaction, definition, reference, operation.Text, styles[assets.TextStyle]);
}
else
{
reference = matches[0].reference; reference.UpgradeOpen();
reference.Position = ToPoint(operation.Position); reference.Rotation = operation.Rotation; reference.LayerId = layers[assets.Layer]; updated++;
foreach (ObjectId attributeId in reference.AttributeCollection)
{
var attribute = (AttributeReference)transaction.GetObject(attributeId, OpenMode.ForWrite);
attribute.TextString = operation.Text + (attribute.Tag.Contains('*') ? "*" : string.Empty); attribute.TextStyleId = styles[assets.TextStyle]; attribute.UpdateMTextAttribute();
}
}
reference.XData = Ownership(operation.StableId, operation.Text, manual);
ApplyLegacyDynamicProperties(reference);
}
unchanged += operations.Count(x => x.Kind == AnnotationOperationKind.Skip && x.Reasons.Contains("ALREADY_CURRENT"));
transaction.Commit();
return new(created, updated, unchanged);
}
private static void AddAttributes(Transaction transaction, BlockTableRecord definition, BlockReference reference, string text, ObjectId style)
{
foreach (ObjectId id in definition)
{
if (transaction.GetObject(id, OpenMode.ForRead) is not AttributeDefinition source || source.Constant) continue;
using var attribute = new AttributeReference(); attribute.SetAttributeFromBlock(source, reference.BlockTransform);
attribute.TextString = text + (attribute.Tag.Contains('*') ? "*" : string.Empty); attribute.TextStyleId = style;
attribute.UpdateMTextAttribute(); reference.AttributeCollection.AppendAttribute(attribute); transaction.AddNewlyCreatedDBObject(attribute, true);
}
}
private static void ApplyLegacyDynamicProperties(BlockReference reference)
{
if (!reference.IsDynamicBlock) return;
foreach (DynamicBlockReferenceProperty property in reference.DynamicBlockReferencePropertyCollection)
if (string.Equals(property.PropertyName, "V1", StringComparison.OrdinalIgnoreCase) && !property.ReadOnly) property.Value = "От объекта";
}
private static string? Source(BlockReference reference)
{
var strings = reference.GetXDataForApplication(ApplicationName)?.AsArray().Where(x => x.TypeCode == (int)DxfCode.ExtendedDataAsciiString).Select(x => x.Value?.ToString() ?? string.Empty).ToArray();
return strings is null ? null : Value(strings, "SOURCE");
}
private static string? Value(string[] values, string key) { var index = Array.FindIndex(values, x => string.Equals(x, key, StringComparison.Ordinal)); return index >= 0 && index + 1 < values.Length ? values[index + 1] : null; }
private static Point3d ToPoint(AnnotationPoint point) => new(point.X, point.Y, point.Z);
private static ResultBuffer Ownership(string source, string text, bool manual) => new(new TypedValue((int)DxfCode.ExtendedDataRegAppName, ApplicationName), new TypedValue((int)DxfCode.ExtendedDataAsciiString, "SOURCE"), new TypedValue((int)DxfCode.ExtendedDataAsciiString, source), new TypedValue((int)DxfCode.ExtendedDataAsciiString, "ANNOTATIONTEXT"), new TypedValue((int)DxfCode.ExtendedDataAsciiString, text), new TypedValue((int)DxfCode.ExtendedDataAsciiString, manual ? "MANUAL" : "AUTO"));
private static void EnsureRegApp(Transaction transaction, Database database) { var table = (RegAppTable)transaction.GetObject(database.RegAppTableId, OpenMode.ForRead); if (table.Has(ApplicationName)) return; table.UpgradeOpen(); using var record = new RegAppTableRecord { Name = ApplicationName }; table.Add(record); transaction.AddNewlyCreatedDBObject(record, true); }
}