feat: port p-rec-annot line-number edit mutations
This commit is contained in:
@@ -70,6 +70,8 @@ public sealed record ElevationAnnotationOperation(string StableId, AnnotationOpe
|
|||||||
public sealed record ElevationAnnotationPlan(IReadOnlyList<ElevationAnnotationOperation> Operations);
|
public sealed record ElevationAnnotationPlan(IReadOnlyList<ElevationAnnotationOperation> Operations);
|
||||||
public sealed record LineConnectionCandidate(string StableId, string LineNumber, Point3Dto FirstEndpoint, Point3Dto SecondEndpoint, Bounds3Dto Bounds, AnnotationPoint Position, double Rotation);
|
public sealed record LineConnectionCandidate(string StableId, string LineNumber, Point3Dto FirstEndpoint, Point3Dto SecondEndpoint, Bounds3Dto Bounds, AnnotationPoint Position, double Rotation);
|
||||||
public sealed record LineConnectionGroup(string LineNumber, IReadOnlyList<LineConnectionCandidate> Members, AnnotationPoint Placement, double Rotation);
|
public sealed record LineConnectionGroup(string LineNumber, IReadOnlyList<LineConnectionCandidate> Members, AnnotationPoint Placement, double Rotation);
|
||||||
|
public sealed record LineNumberOwnedRow(string SourceStableId, int MarkerNumber, string Text, AnnotationPoint MarkerAttributePosition);
|
||||||
|
public sealed record LineNumberRowMutation(string SourceStableId, int PreviousMarkerNumber, int MarkerNumber, string Text, AnnotationPoint? MarkerAttributePosition);
|
||||||
public sealed record LineNumberTextAsset(string Layer, string TextStyle, double Size, string TextValue, Point2Dto Position);
|
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 LineNumberLineAsset(string Layer, Point2Dto Start, Point2Dto End);
|
||||||
public sealed record LineNumberMarkerAsset(string Layer, double MaskScaleFactor, IReadOnlyList<LineNumberTextAsset> Texts, IReadOnlyList<LineNumberLineAsset> Lines);
|
public sealed record LineNumberMarkerAsset(string Layer, double MaskScaleFactor, IReadOnlyList<LineNumberTextAsset> Texts, IReadOnlyList<LineNumberLineAsset> Lines);
|
||||||
|
|||||||
@@ -0,0 +1,24 @@
|
|||||||
|
using PRecAnnot.Abstractions;
|
||||||
|
|
||||||
|
namespace PRecAnnot.Core;
|
||||||
|
|
||||||
|
public static class LineNumberEditPlanner
|
||||||
|
{
|
||||||
|
public static IReadOnlyList<LineNumberRowMutation> Renumber(IEnumerable<LineNumberOwnedRow> rows, int newStart)
|
||||||
|
{
|
||||||
|
if (newStart <= 0) throw new ArgumentOutOfRangeException(nameof(newStart), "LINE_NUMBER_START_MUST_BE_POSITIVE");
|
||||||
|
var ordered = rows.OrderBy(value => value.MarkerNumber).ThenBy(value => value.SourceStableId, StringComparer.Ordinal).ToArray();
|
||||||
|
if (ordered.Length == 0) throw new ArgumentException("LINE_NUMBER_LIST_EMPTY", nameof(rows));
|
||||||
|
if (ordered.Select(value => value.MarkerNumber).Distinct().Count() != ordered.Length) throw new InvalidOperationException("LINE_NUMBER_LIST_DUPLICATE_MARKER_NUMBER");
|
||||||
|
return ordered.Select((value, index) => new LineNumberRowMutation(value.SourceStableId, value.MarkerNumber, checked(newStart + index), value.Text, null)).ToArray();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LineNumberRowMutation EditText(LineNumberOwnedRow row, string text)
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(text)) throw new ArgumentException("LINE_NUMBER_TEXT_EMPTY", nameof(text));
|
||||||
|
return new(row.SourceStableId, row.MarkerNumber, row.MarkerNumber, text, null);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static LineNumberRowMutation ResetMarkerAttributePosition(LineNumberOwnedRow row, Point2Dto configuredPosition) =>
|
||||||
|
new(row.SourceStableId, row.MarkerNumber, row.MarkerNumber, row.Text, new(configuredPosition.X, configuredPosition.Y));
|
||||||
|
}
|
||||||
@@ -87,6 +87,50 @@ internal sealed class LineNumberAnnotationWriter
|
|||||||
return new(1, 0, 0);
|
return new(1, 0, 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public LineNumberApplyResult ApplyEdits(Document document, IReadOnlyList<LineNumberRowMutation> mutations)
|
||||||
|
{
|
||||||
|
if (mutations.Count == 0) return new(0, 0, 1);
|
||||||
|
using var documentLock = document.LockDocument();
|
||||||
|
var database = document.Database;
|
||||||
|
using var transaction = database.TransactionManager.StartTransaction();
|
||||||
|
var blocks = (BlockTable)transaction.GetObject(database.BlockTableId, OpenMode.ForRead);
|
||||||
|
var paper = (BlockTableRecord)transaction.GetObject(blocks[BlockTableRecord.PaperSpace], OpenMode.ForRead);
|
||||||
|
var updated = 0;
|
||||||
|
foreach (var mutation in mutations)
|
||||||
|
{
|
||||||
|
var marker = FindOwnedMarker(transaction, paper, mutation.SourceStableId) ?? throw new InvalidOperationException("LINE_NUMBER_OWNED_MARKER_NOT_FOUND");
|
||||||
|
foreach (ObjectId id in marker.AttributeCollection)
|
||||||
|
{
|
||||||
|
var attribute = (AttributeReference)transaction.GetObject(id, OpenMode.ForWrite);
|
||||||
|
if (!string.Equals(attribute.Tag, MarkerAttributeTag, StringComparison.OrdinalIgnoreCase)) continue;
|
||||||
|
attribute.TextString = mutation.MarkerNumber.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
if (mutation.MarkerAttributePosition is { } reset) attribute.Position = new Point3d(reset.X, reset.Y, reset.Z).TransformBy(marker.BlockTransform);
|
||||||
|
}
|
||||||
|
foreach (ObjectId entityId in paper)
|
||||||
|
{
|
||||||
|
if (transaction.GetObject(entityId, OpenMode.ForRead) is not BlockReference list || !list.Name.StartsWith("LN_LIST", StringComparison.OrdinalIgnoreCase)) continue;
|
||||||
|
foreach (ObjectId attributeId in list.AttributeCollection)
|
||||||
|
{
|
||||||
|
var attribute = (AttributeReference)transaction.GetObject(attributeId, OpenMode.ForWrite);
|
||||||
|
if (string.Equals(attribute.Tag, $"NUMBER{mutation.PreviousMarkerNumber}", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
attribute.Tag = $"NUMBER{mutation.MarkerNumber}";
|
||||||
|
attribute.TextString = mutation.MarkerNumber.ToString(System.Globalization.CultureInfo.InvariantCulture);
|
||||||
|
}
|
||||||
|
else if (string.Equals(attribute.Tag, $"LINENUMBER{mutation.PreviousMarkerNumber}", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
attribute.Tag = $"LINENUMBER{mutation.MarkerNumber}";
|
||||||
|
attribute.TextString = mutation.Text.Replace("\n", "\\P", StringComparison.Ordinal);
|
||||||
|
attribute.UpdateMTextAttribute();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
updated++;
|
||||||
|
}
|
||||||
|
transaction.Commit();
|
||||||
|
return new(0, updated, 0);
|
||||||
|
}
|
||||||
|
|
||||||
private static void ValidateAssets(Transaction transaction, Database database, LineNumberMarkerAsset marker)
|
private static void ValidateAssets(Transaction transaction, Database database, LineNumberMarkerAsset marker)
|
||||||
{
|
{
|
||||||
var layers = (LayerTable)transaction.GetObject(database.LayerTableId, OpenMode.ForRead);
|
var layers = (LayerTable)transaction.GetObject(database.LayerTableId, OpenMode.ForRead);
|
||||||
@@ -169,6 +213,12 @@ internal sealed class LineNumberAnnotationWriter
|
|||||||
attribute.SetAttributeFromBlock(source, reference.BlockTransform);
|
attribute.SetAttributeFromBlock(source, reference.BlockTransform);
|
||||||
attribute.TextString = values[source.Tag];
|
attribute.TextString = values[source.Tag];
|
||||||
attribute.UpdateMTextAttribute();
|
attribute.UpdateMTextAttribute();
|
||||||
|
var row = rows.Single(value => source.Tag.EndsWith(value.MarkerNumber.ToString(System.Globalization.CultureInfo.InvariantCulture), StringComparison.Ordinal));
|
||||||
|
attribute.XData = new ResultBuffer(
|
||||||
|
new TypedValue((int)DxfCode.ExtendedDataRegAppName, ApplicationName),
|
||||||
|
new TypedValue((int)DxfCode.ExtendedDataAsciiString, row.StableId),
|
||||||
|
new TypedValue((int)DxfCode.ExtendedDataAsciiString, source.Tag.StartsWith("NUMBER", StringComparison.OrdinalIgnoreCase) ? "MARKER" : "LINENUMBER"),
|
||||||
|
new TypedValue((int)DxfCode.ExtendedDataInteger32, row.MarkerNumber));
|
||||||
reference.AttributeCollection.AppendAttribute(attribute);
|
reference.AttributeCollection.AppendAttribute(attribute);
|
||||||
transaction.AddNewlyCreatedDBObject(attribute, true);
|
transaction.AddNewlyCreatedDBObject(attribute, true);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,27 @@
|
|||||||
|
using PRecAnnot.Abstractions;
|
||||||
|
using PRecAnnot.Core;
|
||||||
|
|
||||||
|
namespace PRecAnnot.Core.Tests;
|
||||||
|
|
||||||
|
public sealed class LineNumberEditPlannerTests
|
||||||
|
{
|
||||||
|
[Fact]
|
||||||
|
public void RenumbersLegacyListContiguouslyFromPositiveStart()
|
||||||
|
{
|
||||||
|
var rows = new[] { new LineNumberOwnedRow("b", 7, "L-2", new()), new LineNumberOwnedRow("a", 5, "L-1", new()) };
|
||||||
|
var result = LineNumberEditPlanner.Renumber(rows, 10);
|
||||||
|
Assert.Equal(new[] { 10, 11 }, result.Select(value => value.MarkerNumber));
|
||||||
|
Assert.Equal(new[] { 5, 7 }, result.Select(value => value.PreviousMarkerNumber));
|
||||||
|
}
|
||||||
|
|
||||||
|
[Fact] public void RejectsNonPositiveStart() => Assert.Throws<ArgumentOutOfRangeException>(() => LineNumberEditPlanner.Renumber([new("a", 1, "L", new())], 0));
|
||||||
|
[Fact] public void RejectsDuplicateExistingMarkerNumbers() => Assert.Throws<InvalidOperationException>(() => LineNumberEditPlanner.Renumber([new("a", 1, "L", new()), new("b", 1, "L2", new())], 2));
|
||||||
|
|
||||||
|
[Fact]
|
||||||
|
public void PlansTextEditAndConfiguredMarkerPositionReset()
|
||||||
|
{
|
||||||
|
var row = new LineNumberOwnedRow("a", 3, "old", new(9, 9));
|
||||||
|
Assert.Equal("new", LineNumberEditPlanner.EditText(row, "new").Text);
|
||||||
|
Assert.Equal(new AnnotationPoint(-4, 4.4), LineNumberEditPlanner.ResetMarkerAttributePosition(row, new(-4, 4.4)).MarkerAttributePosition);
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user