Hi,
This post is related to
this one on AutoCAD DevBlog where it si showned how to convert a point from PaperSpace to ModelSpace P/Invoking acedTrans.
It appears the code doesn't work as expected. There're, IMO, two mistakes:
1. PSDCS and DCS are related to the active Viewport, so it's needed to activate the Viewport (switching to ModelSpace if it's assumed there's only one viewport in the current layout) after the prompt for point and before calling acedTrans().
2. The returned point coordinates are DCS coordinates which need a second transformation from DCS to WCS coordinates before passing them to the DBPoint constructor.
With LISP:
So, here's the 'corrected' code:
[DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedTrans")]
static extern int acedTrans(double[] point, IntPtr fromRb, IntPtr toRb, int disp, double[] result);
[CommandMethod("ps2ms", CommandFlags.NoTileMode)]
static public void ps2ms()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
// pick some point in PS
PromptPointResult res = ed.GetPoint("Pick Model Space Point");
if (res.Status == PromptStatus.OK)
{
// Transform from PS point to MS point
ResultBuffer rbPSDCS
= new ResultBuffer
(new TypedValue
(5003,
3)); ResultBuffer rbDCS
= new ResultBuffer
(new TypedValue
(5003,
2)); ResultBuffer rbWCS
= new ResultBuffer
(new TypedValue
(5003,
0)); double[] retPoint
= new double[] { 0,
0,
0 }; ed.SwitchToModelSpace();
using (Viewport vp = (Viewport)ed.CurrentViewportObjectId.Open(OpenMode.ForRead))
{
// translate from from the DCS of Paper Space (PSDCS) RTSHORT=3
// to the DCS of the current model space viewport RTSHORT=2
acedTrans(res.Value.ToArray(), rbPSDCS.UnmanagedObject, rbDCS.UnmanagedObject, 0, retPoint);
//translate the DCS of the current model space viewport RTSHORT=2
//to the WCS RTSHORT=0
acedTrans(retPoint, rbDCS.UnmanagedObject, rbWCS.UnmanagedObject, 0, retPoint);
}
ed.SwitchToPaperSpace();
// create a new DBPoint and add to model space to show where we picked
using (DBPoint pnt
= new DBPoint
(new Point3d
(retPoint
[0], retPoint
[1], retPoint
[2]))) using (BlockTable bt = ed.Document.Database.BlockTableId.Open(OpenMode.ForRead) as BlockTable)
using (BlockTableRecord ms = bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite) as BlockTableRecord)
ms.AppendEntity(pnt);
}
}
But this can be done without P/Invoke by using some extension methods from the
GeometryExtensions library.
The same as upper using the Point3d.Trans() extension method. This method is overloaded and may accept arguments from the CoordSystem enum or integers (as acedTrans or trans LISP function).
using GeometryExtensions;
[CommandMethod("ps2ms", CommandFlags.NoTileMode)]
public void ps2ms()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
Database db = HostApplicationServices.WorkingDatabase;
PromptPointResult res = ed.GetPoint("Pick Model Space Point");
if (res.Status != PromptStatus.OK) return;
//Point3d pt = res.Value.Trans(3, 2).Trans(2, 0);
Point3d pt = res.Value
.Trans(GeomExt.CoordSystem.PSDCS, GeomExt.CoordSystem.DCS)
.Trans(GeomExt.CoordSystem.DCS, GeomExt.CoordSystem.WCS);
using (Transaction tr = db.TransactionManager.StartTransaction())
{
BlockTableRecord ms =
(BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
DBPoint dbPt
= new DBPoint
(pt
); ms.AppendEntity(dbPt);
tr.AddNewlyCreatedDBObject(dbPt, true);
tr.Commit();
}
}
The upper examples are both not very safe, because they activate a viewport in the the current layout which may not be the required one if there're many.
In the GeometryExtensions library some methods extend the Viewport class returning transformation Matrix3d which can be used for this task avoiding the viewport activation.
Here's the same command using this way. It needs to prompt the user for selection the viewport.
using GeometryExtensions
[CommandMethod("ps2ms", CommandFlags.NoTileMode)]
public void ps2ms()
{
Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
Database db = HostApplicationServices.WorkingDatabase;
PromptPointResult res = ed.GetPoint("Pick Model Space Point");
if (res.Status != PromptStatus.OK) return;
PromptEntityOptions opts
= new PromptEntityOptions
("\nSelect a viewport: "); opts.SetRejectMessage("Object non valide.");
opts
.AddAllowedClass(typeof(Viewport
),
true); PromptEntityResult per = ed.GetEntity(opts);
if (per.Status != PromptStatus.OK) return;
using (Transaction tr = db.TransactionManager.StartTransaction())
{
Viewport vp = (Viewport)tr.GetObject(per.ObjectId, OpenMode.ForRead);
Point3d pt = res.Value.TransformBy(vp.DCS2WCS() * vp.PSDCS2DCS());
DBPoint dbPt
= new DBPoint
(pt
); BlockTableRecord ms =
(BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
ms.AppendEntity(dbPt);
tr.AddNewlyCreatedDBObject(dbPt, true);
tr.Commit();
}
}