123 lines
8.6 KiB
C#
123 lines
8.6 KiB
C#
using Autodesk.AutoCAD.ApplicationServices;
|
|
using Autodesk.AutoCAD.DatabaseServices;
|
|
using Autodesk.AutoCAD.EditorInput;
|
|
using Autodesk.AutoCAD.Geometry;
|
|
using Autodesk.ProcessPower.PlantInstance;
|
|
using Autodesk.ProcessPower.P3dProjectParts;
|
|
using PRecAnnot.Abstractions;
|
|
using PRecAnnot.Core;
|
|
using AcadApplication = Autodesk.AutoCAD.ApplicationServices.Core.Application;
|
|
|
|
namespace PRecAnnot.Plant3D2026;
|
|
|
|
internal static class ElevationCommands
|
|
{
|
|
public static void RunAutomatic() => Run("PXP-AE", manual: false);
|
|
public static void RunManual() => Run("PXP-IE", manual: true);
|
|
|
|
private static void Run(string command, bool manual)
|
|
{
|
|
var document = AcadApplication.DocumentManager.MdiActiveDocument;
|
|
if (document is null) return;
|
|
try
|
|
{
|
|
var assets = ElevationRuntimeConfiguration.Read();
|
|
var datum = PromptDatum(document.Editor);
|
|
if (datum is null) return;
|
|
if (manual) RunManual(document, assets, datum.Value);
|
|
else RunAutomatic(document, assets, datum.Value);
|
|
}
|
|
catch (Exception exception)
|
|
{
|
|
RuntimeDiagnostics.Write("ElevationCommandFailed", "ERROR", commandName: command, resultStatus: exception.Message, exception: exception, document: document);
|
|
document.Editor.WriteMessage($"\n{command}: {exception.Message}");
|
|
}
|
|
}
|
|
|
|
private static void RunAutomatic(Document document, ElevationAssets assets, double datum)
|
|
{
|
|
var selected = SelectViewport(document.Editor);
|
|
if (selected.Status != PromptStatus.OK) return;
|
|
OrthoViewportInfo viewport;
|
|
using (var transaction = document.Database.TransactionManager.StartOpenCloseTransaction())
|
|
viewport = ResolveViewport(document, (Viewport)transaction.GetObject(selected.ObjectId, OpenMode.ForRead));
|
|
var candidates = new PlantObjectReader().ReadObjects(viewport, CancellationToken.None)
|
|
.Where(value => value.MembershipStatus == ViewMembershipStatus.Resolved && value.Geometry.PaperPosition is not null && value.Geometry.Elevation.HasValue)
|
|
.Select(value => new ElevationCandidate(value.SnapshotId, value.Geometry.Elevation!.Value, datum,
|
|
new AnnotationPoint(value.Geometry.PaperPosition!.Value.X, value.Geometry.PaperPosition.Value.Y), value.Geometry.Rotation ?? 0, assets.Layer, assets.TextStyle))
|
|
.ToArray();
|
|
var writer = new ElevationAnnotationWriter();
|
|
var plan = new ElevationAnnotationPlanner().Build(candidates, writer.ReadExisting(document));
|
|
WritePreview(document, "PXP-AE", plan);
|
|
if (!IsApply()) return;
|
|
ElevationApplyPermitReader.ReadAndValidate(document);
|
|
var result = writer.Apply(document, plan.Operations, assets);
|
|
WriteApply(document, "PXP-AE", result);
|
|
}
|
|
|
|
private static void RunManual(Document document, ElevationAssets assets, double datum)
|
|
{
|
|
while (true)
|
|
{
|
|
var point = document.Editor.GetPoint("\nУкажите положение высотной отметки: ");
|
|
if (point.Status != PromptStatus.OK) return;
|
|
Viewport? viewport = null;
|
|
Point3d modelPoint = default;
|
|
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);
|
|
viewport = paper.Cast<ObjectId>().Select(id => transaction.GetObject(id, OpenMode.ForRead)).OfType<Viewport>()
|
|
.Where(value => value.Number > 1 && !value.PerspectiveOn && Contains(value, point.Value))
|
|
.OrderBy(value => value.Number).FirstOrDefault();
|
|
if (viewport is not null) modelPoint = point.Value.TransformBy(ModelToPaper(viewport).Inverse());
|
|
}
|
|
if (viewport is null) { document.Editor.WriteMessage("\nТочка должна находиться внутри ортографического видового экрана."); continue; }
|
|
var stableId = $"manual:{viewport.Handle}:{point.Value.X:R}:{point.Value.Y:R}";
|
|
var candidate = new ElevationCandidate(stableId, modelPoint.Z, datum, new AnnotationPoint(point.Value.X, point.Value.Y), 0, assets.Layer, assets.TextStyle);
|
|
var plan = new ElevationAnnotationPlanner().Build([candidate], Array.Empty<ExistingElevationAnnotation>());
|
|
WritePreview(document, "PXP-IE", plan);
|
|
if (!IsApply()) return;
|
|
ElevationApplyPermitReader.ReadAndValidate(document);
|
|
var result = new ElevationAnnotationWriter().Apply(document, plan.Operations, assets, manual: true);
|
|
WriteApply(document, "PXP-IE", result);
|
|
// Legacy PXP-IE repeats until Escape; every committed iteration is
|
|
// a native AutoCAD transaction and therefore participates in UNDO.
|
|
}
|
|
}
|
|
|
|
private static double? PromptDatum(Editor editor)
|
|
{
|
|
var options = new PromptDoubleOptions("\nСмещение нуля МСК (в мм) <0>: ") { AllowNone = true, DefaultValue = 0, UseDefaultValue = true };
|
|
var result = editor.GetDouble(options);
|
|
return result.Status == PromptStatus.OK ? result.Value : result.Status == PromptStatus.None ? 0 : null;
|
|
}
|
|
|
|
private static PromptEntityResult SelectViewport(Editor editor)
|
|
{
|
|
var options = new PromptEntityOptions("\nВыберите видовой экран: "); options.SetRejectMessage("\nДолжен быть видовой экран."); options.AddAllowedClass(typeof(Viewport), true);
|
|
return editor.GetEntity(options);
|
|
}
|
|
|
|
private static OrthoViewportInfo ResolveViewport(Document document, Viewport selected)
|
|
{
|
|
var project = PlantApplication.ProjectPart("Ortho") as OrthoProject ?? throw new InvalidOperationException("ELEVATION_ORTHO_PROJECT_UNAVAILABLE");
|
|
var matches = new PlantOrthoViewportReader(project, document.Name).ReadViewports(CancellationToken.None)
|
|
.Where(value => Math.Abs(value.CenterX - selected.CenterPoint.X) < 0.001 && Math.Abs(value.CenterY - selected.CenterPoint.Y) < 0.001 && Math.Abs(value.Width - selected.Width) < 0.001 && Math.Abs(value.Height - selected.Height) < 0.001).ToArray();
|
|
return matches.Length == 1 ? matches[0] : throw new InvalidOperationException(matches.Length == 0 ? "ELEVATION_ORTHO_VIEW_NOT_FOUND" : "ELEVATION_ORTHO_VIEW_AMBIGUOUS");
|
|
}
|
|
|
|
private static Matrix3d ModelToPaper(Viewport viewport)
|
|
{
|
|
var center = new Point3d(viewport.ViewCenter.X, viewport.ViewCenter.Y, 0);
|
|
return Matrix3d.Displacement(new Vector3d(viewport.CenterPoint.X - center.X, viewport.CenterPoint.Y - center.Y, 0))
|
|
* Matrix3d.Scaling(viewport.CustomScale, center)
|
|
* Matrix3d.Rotation(viewport.TwistAngle, Vector3d.ZAxis, Point3d.Origin)
|
|
* Matrix3d.WorldToPlane(new Plane(viewport.ViewTarget, viewport.ViewDirection));
|
|
}
|
|
private static bool Contains(Viewport viewport, Point3d point) => viewport.GeometricExtents.MinPoint.X <= point.X && viewport.GeometricExtents.MaxPoint.X >= point.X && viewport.GeometricExtents.MinPoint.Y <= point.Y && viewport.GeometricExtents.MaxPoint.Y >= point.Y;
|
|
private static bool IsApply() => string.Equals(Environment.GetEnvironmentVariable("P_REC_ANNOT_RUNTIME_MODE"), "Apply", StringComparison.OrdinalIgnoreCase);
|
|
private static void WritePreview(Document document, string command, ElevationAnnotationPlan plan) { document.Editor.WriteMessage($"\n{command} preview: create={plan.Operations.Count(x => x.Kind == AnnotationOperationKind.Create)}; update={plan.Operations.Count(x => x.Kind == AnnotationOperationKind.Update)}; skip={plan.Operations.Count(x => x.Kind == AnnotationOperationKind.Skip)}; conflict={plan.Operations.Count(x => x.Kind == AnnotationOperationKind.Conflict)}. DWG не изменён."); RuntimeDiagnostics.Write("ElevationPreviewCompleted", commandName: command, resultStatus: "PreviewCompleted", document: document, details: new Dictionary<string, object?> { ["OperationCount"] = plan.Operations.Count }); }
|
|
private static void WriteApply(Document document, string command, ElevationApplyResult result) { document.Editor.WriteMessage($"\n{command} Apply: created={result.Created}; updated={result.Updated}; already-current={result.Unchanged}."); RuntimeDiagnostics.Write("ElevationApplyCompleted", commandName: command, resultStatus: "Applied", document: document, details: new Dictionary<string, object?> { ["Created"] = result.Created, ["Updated"] = result.Updated, ["AlreadyCurrent"] = result.Unchanged }); }
|
|
}
|