feat: persist p-rec-annot manual annotation state

This commit is contained in:
codex
2026-08-07 14:14:34 +03:00
parent 064cfdd803
commit 700922b832
4 changed files with 45 additions and 3 deletions
@@ -0,0 +1,15 @@
namespace PRecAnnot.Core;
public sealed record ManualAnnotationState(string Kind, ManualDatumMode DatumMode, double DatumOffset)
{
public string Serialize() => string.Join("|", Kind, DatumMode, DatumOffset.ToString(System.Globalization.CultureInfo.InvariantCulture));
public static ManualAnnotationState? Deserialize(string? value)
{
if (string.IsNullOrWhiteSpace(value)) return null;
var fields = value.Split('|');
if (fields.Length != 3 || !Enum.TryParse<ManualDatumMode>(fields[1], true, out var mode) ||
!double.TryParse(fields[2], System.Globalization.NumberStyles.Float, System.Globalization.CultureInfo.InvariantCulture, out var offset)) return null;
return new ManualAnnotationState(fields[0], mode, offset);
}
}
@@ -58,7 +58,7 @@ public sealed class EntryPoint : IExtensionApplication
[CommandMethod("mto-ma")] public void ShowManualAnnotationPaletteCommand() => ManualAnnotationPalette.Show();
[CommandMethod("PXP-MA")] public void ShowManualAnnotationPaletteAliasCommand() => ManualAnnotationPalette.Show();
[CommandMethod("CAC1")] public void PlaceManualAnnotationCommand() => ManualAnnotationPalette.PlaceSelected();
[CommandMethod("PXP-MAB")] public void InsertManualBlockCommand() => ManualBlockInsertionCommand.Run();
[CommandMethod("PXP-MAB")] public void InsertManualBlockCommand() => ManualBlockInsertionCommand.Run(ManualAnnotationPalette.CurrentState());
[CommandMethod("PHP_REC_AUTOMATION_START", CommandFlags.Session)]
public void AutomationStartCommand() => FirstLimitedApplyAutomationBootstrap.Arm();
@@ -38,6 +38,7 @@ internal static class ManualAnnotationPalette
RuntimeDiagnostics.Write("ManualAnnotationPaletteShown", resultStatus: "Visible");
}
public static void PlaceSelected() => control?.Place();
public static ManualAnnotationState CurrentState() => control?.CurrentState() ?? new ManualAnnotationState("AutoCadBlock", ManualDatumMode.Absolute, 0);
private static void DocumentActivated(object sender, Autodesk.AutoCAD.ApplicationServices.DocumentCollectionEventArgs args) => control?.RefreshAvailability();
}
@@ -74,6 +75,7 @@ internal sealed class ManualAnnotationControl : WinForms.UserControl
document.SendStringToExecute(command + " ", true, false, false);
RuntimeDiagnostics.Write("ManualAnnotationActionInvoked", commandName: command, resultStatus: kind.ToString(), document: document);
}
public ManualAnnotationState CurrentState() => new(kinds.First(value => value.Key.Checked).Value.ToString(), relativeDatum.Checked ? ManualDatumMode.Relative : ManualDatumMode.Absolute, (double)datumOffset.Value);
private static void Reconnect() => AcadApplication.DocumentManager.MdiActiveDocument?.SendStringToExecute("PXP-ULN ", true, false, false);
private static void OpenHelp()
{
@@ -90,7 +92,7 @@ internal sealed class ManualAnnotationControl : WinForms.UserControl
internal static class ManualBlockInsertionCommand
{
public static void Run()
public static void Run(ManualAnnotationState state)
{
var document = AcadApplication.DocumentManager.MdiActiveDocument; if (document is null) return;
var name = document.Editor.GetString(new PromptStringOptions("\nBlock name: ") { AllowSpaces = true }); if (name.Status != PromptStatus.OK || string.IsNullOrWhiteSpace(name.StringResult)) return;
@@ -99,7 +101,12 @@ internal static class ManualBlockInsertionCommand
using var documentLock = document.LockDocument(); using var transaction = document.Database.TransactionManager.StartTransaction();
var blocks = (BlockTable)transaction.GetObject(document.Database.BlockTableId, OpenMode.ForRead); if (!blocks.Has(name.StringResult)) { document.Editor.WriteMessage("\nBlock definition not found."); return; }
var space = (BlockTableRecord)transaction.GetObject(document.Database.CurrentSpaceId, OpenMode.ForWrite); var definition = (BlockTableRecord)transaction.GetObject(blocks[name.StringResult], OpenMode.ForRead);
using var reference = new BlockReference(point.Value, definition.ObjectId) { Rotation = angle.Status == PromptStatus.OK ? angle.Value : 0 }; space.AppendEntity(reference); transaction.AddNewlyCreatedDBObject(reference, true);
using var reference = new BlockReference(point.Value, definition.ObjectId) { Rotation = angle.Status == PromptStatus.OK ? angle.Value : 0 };
space.AppendEntity(reference); transaction.AddNewlyCreatedDBObject(reference, true);
var regApps = (RegAppTable)transaction.GetObject(document.Database.RegAppTableId, OpenMode.ForRead);
const string appName = "P_REC_ANNOT_MANUAL";
if (!regApps.Has(appName)) { regApps.UpgradeOpen(); using var app = new RegAppTableRecord { Name = appName }; regApps.Add(app); transaction.AddNewlyCreatedDBObject(app, true); }
reference.XData = new ResultBuffer(new TypedValue((int)DxfCode.ExtendedDataRegAppName, appName), new TypedValue((int)DxfCode.ExtendedDataAsciiString, state.Serialize()));
foreach (ObjectId id in definition)
if (transaction.GetObject(id, OpenMode.ForRead) is AttributeDefinition source && !source.Constant)
{ var value = document.Editor.GetString(new PromptStringOptions($"\n{source.Tag} <{source.TextString}>: ") { AllowSpaces = true }); using var attribute = new AttributeReference(); attribute.SetAttributeFromBlock(source, reference.BlockTransform); attribute.TextString = value.Status == PromptStatus.OK ? value.StringResult : source.TextString; reference.AttributeCollection.AppendAttribute(attribute); transaction.AddNewlyCreatedDBObject(attribute, true); }
@@ -0,0 +1,20 @@
using PRecAnnot.Core;
namespace PRecAnnot.Core.Tests;
public sealed class ManualAnnotationPersistenceTests
{
[Theory]
[InlineData("AutoCadBlock", ManualDatumMode.Absolute, 0)]
[InlineData("SectionElevation", ManualDatumMode.Relative, -12.5)]
[InlineData("InstrumentPosition", ManualDatumMode.Relative, 4.25)]
public void State_round_trips_without_losing_legacy_datum_values(string kind, ManualDatumMode mode, double offset)
{
var state = new ManualAnnotationState(kind, mode, offset);
var restored = ManualAnnotationState.Deserialize(state.Serialize());
Assert.Equal(state, restored);
}
[Fact]
public void Invalid_state_is_not_accepted() => Assert.Null(ManualAnnotationState.Deserialize("bad|payload"));
}