feat: add p-rec-annot line-number list writer
This commit is contained in:
@@ -61,6 +61,7 @@ public sealed record LineNumberCommandOperation(string StableId, AnnotationOpera
|
||||
public sealed record LineNumberCommandPlan(IReadOnlyList<LineNumberCommandOperation> Operations);
|
||||
public sealed record LineNumberApplyPreflight(string StableId, int MarkerNumber, string Text, AnnotationPoint Position, double Rotation, string Layer, string TextStyle);
|
||||
public sealed record LineNumberApplyResult(int Created, int Updated, int Unchanged);
|
||||
public sealed record LineNumberListApplyPreflight(string StableId, AnnotationPoint Position, double Rotation, IReadOnlyList<LineNumberApplyPreflight> Rows);
|
||||
public sealed record LineNumberTextAsset(string Layer, string TextStyle, double Size, string TextValue, Point2Dto Position);
|
||||
public sealed record LineNumberLineAsset(string Layer, Point2Dto Start, Point2Dto End);
|
||||
public sealed record LineNumberMarkerAsset(string Layer, double MaskScaleFactor, IReadOnlyList<LineNumberTextAsset> Texts, IReadOnlyList<LineNumberLineAsset> Lines);
|
||||
|
||||
@@ -47,6 +47,46 @@ internal sealed class LineNumberAnnotationWriter
|
||||
return new(created, updated, unchanged);
|
||||
}
|
||||
|
||||
// Legacy PXP-LN materialises its selected marker/text pairs as a paper-space
|
||||
// LN_LIST block. The list ownership key makes repeat Apply update that
|
||||
// aggregate rather than append another list.
|
||||
public LineNumberApplyResult ApplyList(Document document, LineNumberListApplyPreflight plan, AnnotateLineNumbersAssets assets)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(document);
|
||||
ArgumentNullException.ThrowIfNull(plan);
|
||||
if (plan.Rows.Count == 0) return new(0, 0, 1);
|
||||
if (plan.Rows.Select(row => row.MarkerNumber).Distinct().Count() != plan.Rows.Count)
|
||||
throw new InvalidOperationException("LINE_NUMBER_LIST_DUPLICATE_MARKER_NUMBER");
|
||||
|
||||
using var documentLock = document.LockDocument();
|
||||
var database = document.Database;
|
||||
using var transaction = database.TransactionManager.StartTransaction();
|
||||
var blocks = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead);
|
||||
ValidateListAssets(transaction, database, assets.List);
|
||||
EnsureRegApp(transaction, database);
|
||||
var paper = (BlockTableRecord)transaction.GetObject(blocks[BlockTableRecord.PaperSpace], OpenMode.ForWrite);
|
||||
var existing = FindOwnedMarker(transaction, paper, plan.StableId);
|
||||
if (existing is not null)
|
||||
{
|
||||
var changed = UpdateList(transaction, existing, plan);
|
||||
transaction.Commit();
|
||||
return new(0, changed ? 1 : 0, changed ? 0 : 1);
|
||||
}
|
||||
|
||||
var definition = CreateListDefinition(transaction, database, blocks, plan, assets.List);
|
||||
using var reference = new BlockReference(new Point3d(plan.Position.X, plan.Position.Y, plan.Position.Z), definition)
|
||||
{
|
||||
Rotation = plan.Rotation,
|
||||
Layer = assets.List.Layer,
|
||||
XData = ListOwnership(plan.StableId)
|
||||
};
|
||||
paper.AppendEntity(reference);
|
||||
transaction.AddNewlyCreatedDBObject(reference, true);
|
||||
AppendListAttributes(transaction, definition, reference, plan.Rows);
|
||||
transaction.Commit();
|
||||
return new(1, 0, 0);
|
||||
}
|
||||
|
||||
private static void ValidateAssets(Transaction transaction, Database database, LineNumberMarkerAsset marker)
|
||||
{
|
||||
var layers = (LayerTable)transaction.GetObject(database.LayerTableId, OpenMode.ForRead);
|
||||
@@ -58,6 +98,105 @@ internal sealed class LineNumberAnnotationWriter
|
||||
if (marker.Texts.Count == 0) throw new InvalidOperationException("LINE_NUMBER_MARKER_TEXT_ASSET_MISSING");
|
||||
}
|
||||
|
||||
private static void ValidateListAssets(Transaction transaction, Database database, LineNumberListAsset list)
|
||||
{
|
||||
var layers = (LayerTable)transaction.GetObject(database.LayerTableId, OpenMode.ForRead);
|
||||
var styles = (TextStyleTable)transaction.GetObject(database.TextStyleTableId, OpenMode.ForRead);
|
||||
foreach (var layer in new[] { list.Layer, list.MarkerText.Layer, list.LineNumberText.Layer, list.Line.Layer }.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
if (!layers.Has(layer)) throw new InvalidOperationException($"LINE_NUMBER_LAYER_MISSING:{layer}");
|
||||
foreach (var style in new[] { list.MarkerText.TextStyle, list.LineNumberText.TextStyle }.Distinct(StringComparer.OrdinalIgnoreCase))
|
||||
if (!styles.Has(style)) throw new InvalidOperationException($"LINE_NUMBER_TEXT_STYLE_MISSING:{style}");
|
||||
if (list.LineHeight <= 0) throw new InvalidOperationException("LINE_NUMBER_LIST_HEIGHT_INVALID");
|
||||
}
|
||||
|
||||
private static ObjectId CreateListDefinition(Transaction transaction, Database database, BlockTable blocks, LineNumberListApplyPreflight plan, LineNumberListAsset list)
|
||||
{
|
||||
blocks.UpgradeOpen();
|
||||
var baseName = $"LN_LIST{plan.Rows.Count}";
|
||||
var name = baseName;
|
||||
for (var suffix = 1; blocks.Has(name); suffix++) name = $"{baseName}_{suffix}";
|
||||
using var definition = new BlockTableRecord { Name = name, Origin = Point3d.Origin };
|
||||
var id = blocks.Add(definition);
|
||||
transaction.AddNewlyCreatedDBObject(definition, true);
|
||||
var layers = (LayerTable)transaction.GetObject(database.LayerTableId, OpenMode.ForRead);
|
||||
var styles = (TextStyleTable)transaction.GetObject(database.TextStyleTableId, OpenMode.ForRead);
|
||||
for (var index = 0; index < plan.Rows.Count; index++)
|
||||
{
|
||||
var row = plan.Rows[index];
|
||||
AppendListAttribute(transaction, definition, row.MarkerNumber.ToString(System.Globalization.CultureInfo.InvariantCulture), $"NUMBER{row.MarkerNumber}", "Номер маркера:", list.MarkerText, index, list.LineHeight, list.MaskScaleFactor, layers, styles);
|
||||
AppendListAttribute(transaction, definition, row.Text.Replace("\n", "(|)", StringComparison.Ordinal), $"LINENUMBER{row.MarkerNumber}", "Номер линии:", list.LineNumberText, index, list.LineHeight, list.MaskScaleFactor, layers, styles);
|
||||
using var horizontal = new Line(
|
||||
Shift(list.Line.Start, index, list.LineHeight),
|
||||
Shift(list.Line.End, index, list.LineHeight)) { LayerId = layers[list.Line.Layer] };
|
||||
definition.AppendEntity(horizontal);
|
||||
transaction.AddNewlyCreatedDBObject(horizontal, true);
|
||||
}
|
||||
using var vertical = new Line(
|
||||
new Point3d(list.Line.Start.X, list.Line.Start.Y + list.LineHeight, 0),
|
||||
Shift(list.Line.Start, plan.Rows.Count - 1, list.LineHeight)) { LayerId = layers[list.Line.Layer] };
|
||||
definition.AppendEntity(vertical);
|
||||
transaction.AddNewlyCreatedDBObject(vertical, true);
|
||||
return id;
|
||||
}
|
||||
|
||||
private static void AppendListAttribute(Transaction transaction, BlockTableRecord definition, string text, string tag, string prompt, LineNumberTextAsset asset, int row, double lineHeight, double maskScale, LayerTable layers, TextStyleTable styles)
|
||||
{
|
||||
var position = new Point3d(asset.Position.X, asset.Position.Y - lineHeight * row, 0);
|
||||
using var attribute = new AttributeDefinition(position, text, tag, prompt, styles[asset.TextStyle]) { Height = asset.Size, IsMTextAttributeDefinition = true, LayerId = layers[asset.Layer] };
|
||||
var mtext = attribute.MTextAttributeDefinition;
|
||||
mtext.BackgroundFill = true;
|
||||
mtext.BackgroundScaleFactor = maskScale;
|
||||
mtext.UseBackgroundColor = true;
|
||||
attribute.MTextAttributeDefinition = mtext;
|
||||
definition.AppendEntity(attribute);
|
||||
transaction.AddNewlyCreatedDBObject(attribute, true);
|
||||
}
|
||||
|
||||
private static Point3d Shift(Point2Dto source, int row, double height) => new(source.X, source.Y - height * row, 0);
|
||||
|
||||
private static void AppendListAttributes(Transaction transaction, ObjectId definitionId, BlockReference reference, IReadOnlyList<LineNumberApplyPreflight> rows)
|
||||
{
|
||||
var values = rows.SelectMany(row => new[]
|
||||
{
|
||||
new KeyValuePair<string, string>($"NUMBER{row.MarkerNumber}", row.MarkerNumber.ToString(System.Globalization.CultureInfo.InvariantCulture)),
|
||||
new KeyValuePair<string, string>($"LINENUMBER{row.MarkerNumber}", row.Text.Replace("\n", "\\P", StringComparison.Ordinal))
|
||||
}).ToDictionary(value => value.Key, value => value.Value, StringComparer.OrdinalIgnoreCase);
|
||||
var definition = (BlockTableRecord)transaction.GetObject(definitionId, OpenMode.ForRead);
|
||||
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 = values[source.Tag];
|
||||
attribute.UpdateMTextAttribute();
|
||||
reference.AttributeCollection.AppendAttribute(attribute);
|
||||
transaction.AddNewlyCreatedDBObject(attribute, true);
|
||||
}
|
||||
}
|
||||
|
||||
private static bool UpdateList(Transaction transaction, BlockReference reference, LineNumberListApplyPreflight plan)
|
||||
{
|
||||
var position = new Point3d(plan.Position.X, plan.Position.Y, plan.Position.Z);
|
||||
var changed = reference.Position != position || reference.Rotation != plan.Rotation;
|
||||
reference.UpgradeOpen();
|
||||
reference.Position = position;
|
||||
reference.Rotation = plan.Rotation;
|
||||
var values = plan.Rows.SelectMany(row => new[]
|
||||
{
|
||||
new KeyValuePair<string, string>($"NUMBER{row.MarkerNumber}", row.MarkerNumber.ToString(System.Globalization.CultureInfo.InvariantCulture)),
|
||||
new KeyValuePair<string, string>($"LINENUMBER{row.MarkerNumber}", row.Text.Replace("\n", "\\P", StringComparison.Ordinal))
|
||||
}).ToDictionary(value => value.Key, value => value.Value, StringComparer.OrdinalIgnoreCase);
|
||||
foreach (ObjectId id in reference.AttributeCollection)
|
||||
{
|
||||
var attribute = (AttributeReference)transaction.GetObject(id, OpenMode.ForWrite);
|
||||
if (!values.TryGetValue(attribute.Tag, out var value)) continue;
|
||||
changed |= !string.Equals(attribute.TextString, value, StringComparison.Ordinal);
|
||||
attribute.TextString = value;
|
||||
attribute.UpdateMTextAttribute();
|
||||
}
|
||||
return changed;
|
||||
}
|
||||
|
||||
private static ObjectId EnsureMarkerDefinition(Transaction transaction, Database database, BlockTable blocks, LineNumberMarkerAsset marker)
|
||||
{
|
||||
if (blocks.Has(MarkerBlockName)) return blocks[MarkerBlockName];
|
||||
@@ -129,6 +268,11 @@ internal sealed class LineNumberAnnotationWriter
|
||||
return null;
|
||||
}
|
||||
|
||||
private static ResultBuffer ListOwnership(string stableId) => new(
|
||||
new TypedValue((int)DxfCode.ExtendedDataRegAppName, ApplicationName),
|
||||
new TypedValue((int)DxfCode.ExtendedDataAsciiString, stableId),
|
||||
new TypedValue((int)DxfCode.ExtendedDataAsciiString, "LIST"));
|
||||
|
||||
private static bool UpdateMarker(Transaction transaction, BlockReference marker, LineNumberApplyPreflight item, string layer)
|
||||
{
|
||||
var position = new Point3d(item.Position.X, item.Position.Y, item.Position.Z);
|
||||
|
||||
Reference in New Issue
Block a user