TheSwamp

Code Red => .NET => Topic started by: gile on August 15, 2012, 07:42:08 AM

Title: Transforming point from PaperSpace to ModelSpace
Post by: gile on August 15, 2012, 07:42:08 AM
Hi,

This post is related to this one (http://adndevblog.typepad.com/autocad/2012/03/converting-paper-space-point-to-model-space-using-autocadnet.html) 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:
Code - Auto/Visual Lisp: [Select]
  1. (trans (trans pt 3 2) 2 0)

So, here's the 'corrected' code:
Code - C#: [Select]
  1.         [DllImport("acad.exe", CallingConvention = CallingConvention.Cdecl, EntryPoint = "acedTrans")]
  2.         static extern int acedTrans(double[] point, IntPtr fromRb, IntPtr toRb, int disp, double[] result);
  3.  
  4.         [CommandMethod("ps2ms", CommandFlags.NoTileMode)]
  5.         static public void ps2ms()
  6.         {
  7.             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  8.             // pick some point in PS
  9.             PromptPointResult res = ed.GetPoint("Pick Model Space Point");
  10.             if (res.Status == PromptStatus.OK)
  11.             {
  12.                 // Transform from PS point to MS point
  13.                 ResultBuffer rbPSDCS = new ResultBuffer(new TypedValue(5003, 3));
  14.                 ResultBuffer rbDCS = new ResultBuffer(new TypedValue(5003, 2));
  15.                 ResultBuffer rbWCS = new ResultBuffer(new TypedValue(5003, 0));
  16.                 double[] retPoint = new double[] { 0, 0, 0 };
  17.                 ed.SwitchToModelSpace();
  18.                 using (Viewport vp = (Viewport)ed.CurrentViewportObjectId.Open(OpenMode.ForRead))
  19.                 {
  20.                      // translate from from the DCS of Paper Space (PSDCS) RTSHORT=3
  21.                      // to the DCS of the current model space viewport RTSHORT=2
  22.                     acedTrans(res.Value.ToArray(), rbPSDCS.UnmanagedObject, rbDCS.UnmanagedObject, 0, retPoint);
  23.  
  24.                     //translate the DCS of the current model space viewport RTSHORT=2
  25.                     //to the WCS RTSHORT=0
  26.                     acedTrans(retPoint, rbDCS.UnmanagedObject, rbWCS.UnmanagedObject, 0, retPoint);
  27.                 }
  28.                 ed.SwitchToPaperSpace();
  29.  
  30.                 // create a new DBPoint and add to model space to show where we picked
  31.                 using (DBPoint pnt = new DBPoint(new Point3d(retPoint[0], retPoint[1], retPoint[2])))
  32.                 using (BlockTable bt = ed.Document.Database.BlockTableId.Open(OpenMode.ForRead) as BlockTable)
  33.                 using (BlockTableRecord ms = bt[BlockTableRecord.ModelSpace].Open(OpenMode.ForWrite) as BlockTableRecord)
  34.                     ms.AppendEntity(pnt);
  35.             }
  36.         }

But this can be done without P/Invoke by using some extension methods from the GeometryExtensions (http://www.theswamp.org/index.php?topic=31865.msg373672#msg373672) 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).

Code - C#: [Select]
  1. using GeometryExtensions;
  2.  
  3.         [CommandMethod("ps2ms", CommandFlags.NoTileMode)]
  4.         public void ps2ms()
  5.         {
  6.             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  7.             Database db = HostApplicationServices.WorkingDatabase;
  8.            
  9.             PromptPointResult res = ed.GetPoint("Pick Model Space Point");
  10.             if (res.Status != PromptStatus.OK) return;
  11.  
  12.             //Point3d pt = res.Value.Trans(3, 2).Trans(2, 0);
  13.             Point3d pt = res.Value
  14.                 .Trans(GeomExt.CoordSystem.PSDCS, GeomExt.CoordSystem.DCS)
  15.                 .Trans(GeomExt.CoordSystem.DCS, GeomExt.CoordSystem.WCS);
  16.  
  17.             using (Transaction tr = db.TransactionManager.StartTransaction())
  18.             {
  19.                 BlockTableRecord ms =
  20.                     (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
  21.                 DBPoint dbPt = new DBPoint(pt);
  22.                 ms.AppendEntity(dbPt);
  23.                 tr.AddNewlyCreatedDBObject(dbPt, true);
  24.                 tr.Commit();
  25.             }
  26.         }

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.

Code - C#: [Select]
  1. using GeometryExtensions
  2.  
  3.         [CommandMethod("ps2ms", CommandFlags.NoTileMode)]
  4.         public void ps2ms()
  5.         {
  6.             Editor ed = Application.DocumentManager.MdiActiveDocument.Editor;
  7.             Database db = HostApplicationServices.WorkingDatabase;
  8.  
  9.             PromptPointResult res = ed.GetPoint("Pick Model Space Point");
  10.             if (res.Status != PromptStatus.OK) return;
  11.  
  12.             PromptEntityOptions opts = new PromptEntityOptions("\nSelect a viewport: ");
  13.             opts.SetRejectMessage("Object non valide.");
  14.             opts.AddAllowedClass(typeof(Viewport), true);
  15.             PromptEntityResult per = ed.GetEntity(opts);
  16.             if (per.Status != PromptStatus.OK) return;
  17.  
  18.             using (Transaction tr = db.TransactionManager.StartTransaction())
  19.             {
  20.                 Viewport vp = (Viewport)tr.GetObject(per.ObjectId, OpenMode.ForRead);
  21.                 Point3d pt = res.Value.TransformBy(vp.DCS2WCS() * vp.PSDCS2DCS());
  22.                 DBPoint dbPt = new DBPoint(pt);
  23.                 BlockTableRecord ms =
  24.                     (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(db), OpenMode.ForWrite);
  25.                 ms.AppendEntity(dbPt);
  26.                 tr.AddNewlyCreatedDBObject(dbPt, true);
  27.                 tr.Commit();
  28.             }
  29.         }
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: TheMaster on August 15, 2012, 03:16:24 PM
Nice work.

Would any of the properties of the View class (AcGsView) help simplify this?
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: gile on August 15, 2012, 04:17:44 PM
Thanks Tony.

Quote
Would any of the properties of the View class (AcGsView) help simplify this?
I never look this way before. I'll do it as soon as I have some time to.

Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: Draftek on August 15, 2012, 04:22:42 PM
Thanks,
I need to go back and look how I'm doing this in some previous written apps.
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: TheMaster on August 15, 2012, 05:57:27 PM
Thanks Tony.

Quote
Would any of the properties of the View class (AcGsView) help simplify this?
I never look this way before. I'll do it as soon as I have some time to.

Well, they might be useful, and I just checked my code bin here and found these:

Code - C#: [Select]
  1. // ViewportExtensionMethods.cs  (c) 2007-2012  Tony Tanzillo
  2.  
  3. using System;
  4. using System.Collections.Generic;
  5. using System.Linq;
  6. using System.Text;
  7. using Autodesk.AutoCAD.Geometry;
  8.  
  9. namespace Autodesk.AutoCAD.DatabaseServices
  10. {
  11.    public static class ViewportExtensionMethods
  12.    {
  13.       public static Matrix3d GetModelToPaperTransform( this Viewport vport )
  14.       {
  15.          if( vport.PerspectiveOn )
  16.             throw new NotSupportedException( "Perspective views not supported" );
  17.          Point3d center = new Point3d( vport.ViewCenter.X, vport.ViewCenter.Y, 0.0 );
  18.          return Matrix3d.Displacement( new Vector3d( vport.CenterPoint.X - center.X, vport.CenterPoint.Y - center.Y, 0.0 ) )
  19.             * Matrix3d.Scaling( vport.CustomScale, center )
  20.             * Matrix3d.Rotation( vport.TwistAngle, Vector3d.ZAxis, Point3d.Origin )
  21.             * Matrix3d.WorldToPlane( new Plane( vport.ViewTarget, vport.ViewDirection ) );
  22.       }
  23.  
  24.       public static Matrix3d GetPaperToModelTransform( this Viewport vport )
  25.       {
  26.          return GetModelToPaperTransform( vport ).Inverse();
  27.       }
  28.  
  29.       public static Point3d PaperToModel( this Point3d point, Viewport vport )
  30.       {
  31.          return point.TransformBy( GetModelToPaperTransform( vport ).Inverse() );
  32.       }
  33.  
  34.       public static Point3d ModelToPaper( this Point3d point, Viewport viewport )
  35.       {
  36.          return point.TransformBy( GetModelToPaperTransform( viewport ) );
  37.       }
  38.  
  39.       public static void PaperToModel( this Entity entity, Viewport vport )
  40.       {
  41.          entity.TransformBy( GetModelToPaperTransform( vport ).Inverse() );
  42.       }
  43.  
  44.       public static void ModelToPaper( this Entity entity, Viewport viewport )
  45.       {
  46.          entity.TransformBy( GetModelToPaperTransform( viewport ) );
  47.       }
  48.  
  49.       public static IEnumerable<Point3d> PaperToModel( this IEnumerable<Point3d> source, Viewport viewport )
  50.       {
  51.          Matrix3d xform = GetModelToPaperTransform( viewport ).Inverse();
  52.          return source.Select( p => p.TransformBy( xform ) );
  53.       }
  54.  
  55.       public static IEnumerable<Point3d> ModelToPaper( this IEnumerable<Point3d> source, Viewport viewport )
  56.       {
  57.          Matrix3d xform = GetModelToPaperTransform( viewport );
  58.          return source.Select( p => p.TransformBy( xform ) );
  59.       }
  60.  
  61.       public static void PaperToModel( this IEnumerable<Entity> src, Viewport viewport )
  62.       {
  63.          Matrix3d xform = GetModelToPaperTransform( viewport ).Inverse();
  64.          foreach( Entity ent in src )
  65.             ent.TransformBy( xform );
  66.       }
  67.  
  68.       public static void ModelToPaper( this IEnumerable<Entity> src, Viewport viewport )
  69.       {
  70.          Matrix3d xform = GetModelToPaperTransform( viewport );
  71.          foreach( Entity ent in src )
  72.             ent.TransformBy( xform );
  73.       }
  74.    }
  75. }
  76.  
  77.  
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: gile on August 16, 2012, 02:00:43 AM
Nice Tony, for a direct WCS2PSDCS (or PSDCS2WCS) transformation.
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: Dale Bartlett on November 21, 2013, 02:55:12 AM
Hi Gile, I just discovered the errors in the original Autodesk post also and am very appreciative of your (and TT) solutions. Annoyingly I didn't read through all the blog comments before attempting my own solution.
Unless I am mistaken, the third example you give in your original post to this topic returns an incorrect Z value. The one headed "Here's the same command using this way. It needs to prompt the user for selection the viewport." My test data is all Z=0 at WCS and it returns a large number.
I have used your code unchanged. The GeometryExtensions_19.dll is still showing v1.4 whereas the _18 is updated to v1.6, not sure if that is relevant.
Thanks again. Dale
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: gile on November 21, 2013, 05:15:54 AM
Hi,

Quote
at WCS and it returns a large number
I do not enconter this kind of issue.
Isn't the "large number"  a number very closed to 0 displayed in scientific mode (i.e 1.2345 1e-16) ?
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: Dale Bartlett on November 25, 2013, 12:46:31 AM
Hi Gile, Attached is a sample drawing and results (MS + PS) of running your routine. Select the point marked by the circle. It always returns a z value of: Z=97261.7614413466 no matter the elevation of any object in MS. I could swear I used your code untouched but will re-visit to see  if I've done something really dumb. Regards, Dale
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: WILL HATCH on November 25, 2013, 04:24:41 PM
that elevation you're getting is the z elevation of the camera, the elevation of the objects in model space is 0.  Can you show the code you use to gather points and display that message?
Title: Re: Transforming point from PaperSpace to ModelSpace
Post by: BKSpurgeon on September 10, 2019, 03:28:14 AM
Thank you, this helped me immensely.

If you want to do it the other way: to go from the model space to the paper space, here is a little test project that I came up with. I hope it helps someone.

The key bit are the following methods:
Code: [Select]
Matrix3d m = ed.WCS2DCS();
                Matrix3d m2 = ed.DCS2PSDCS();

First we need to go from the WCS to the DCS, and then the DCS to the PSDCS. They were done using the handy extension methods provided in Gilles Geometry Extensions library. I'll paste them here for your easy reference - but be sure to get the originals + their associated commentary:

Code: [Select]

        public static Matrix3d WCS2DCS(this Editor ed)
        {
            return ed.DCS2WCS().Inverse();
        }

 public static Matrix3d DCS2PSDCS(this Editor ed)
        {
            Database db = ed.Document.Database;
            if (db.TileMode)
                throw new AcRx.Exception(AcRx.ErrorStatus.NotInPaperspace);
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                Viewport vp =
                    (Viewport)tr.GetObject(ed.CurrentViewportObjectId, OpenMode.ForRead);
                if (vp.Number == 1)
                {
                    try
                    {
                        ed.SwitchToModelSpace();
                        vp = (Viewport)tr.GetObject(ed.CurrentViewportObjectId, OpenMode.ForRead);
                        ed.SwitchToPaperSpace();
                    }
                    catch
                    {
                        throw new AcRx.Exception(AcRx.ErrorStatus.CannotChangeActiveViewport);
                    }
                }
                return vp.DCS2PSDCS();
            }
        }

Instructions:

Code: [Select]
        [CommandMethod("ViewPortTest")]
        public static void ViewPortTest()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                BlockTableRecord ms = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;

                Line line = new Line();
                line.StartPoint = new Point3d(3167, 448, 0);
                line.EndPoint = new Point3d(3167, 594, 0);

                ms.AppendEntity(line);
                tr.AddNewlyCreatedDBObject(line, true);

                tr.Commit();
            }

            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
                BlockTableRecord ms = tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;
                RXClass rxLine = RXClass.GetClass(typeof(Line));
                Line line =  ms.Cast<ObjectId>().Where(id => id.ObjectClass == rxLine).Select(id => tr.GetObject(id, OpenMode.ForRead) as Line).First();

                Point3d startPoint = line.StartPoint;
                Point3d endPoint = line.EndPoint;

                //// When working with paper space
                /// PSDCS to DCS
                /// DCS to WCS
                Matrix3d m = ed.WCS2DCS();
                Matrix3d m2 = ed.DCS2PSDCS();

                Point3d newStart = startPoint.TransformBy(m).TransformBy(m2);
                Point3d newEnd = endPoint.TransformBy(m).TransformBy(m2);

                ed.SwitchToPaperSpace();

                BlockTableRecord ps = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                using (Circle circle = new Circle(newStart, Vector3d.ZAxis, 20))
                {
                    ps.AppendEntity(circle);
                    tr.AddNewlyCreatedDBObject(circle, true);
                }

                using (Circle circle = new Circle(newEnd, Vector3d.ZAxis, 20))
                {
                    ps.AppendEntity(circle);
                    tr.AddNewlyCreatedDBObject(circle, true);
                }

                tr.Commit();
            }
        }