Author Topic: Transforming point from PaperSpace to ModelSpace  (Read 7335 times)

0 Members and 1 Guest are viewing this topic.

gile

  • Water Moccasin
  • Posts: 2232
  • Marseille, France
Transforming point from PaperSpace to ModelSpace
« on: August 15, 2012, 07:42:08 AM »
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:
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 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.         }
Speaking English as a French Frog

TheMaster

  • Guest
Re: Transforming point from PaperSpace to ModelSpace
« Reply #1 on: August 15, 2012, 03:16:24 PM »
Nice work.

Would any of the properties of the View class (AcGsView) help simplify this?

gile

  • Water Moccasin
  • Posts: 2232
  • Marseille, France
Re: Transforming point from PaperSpace to ModelSpace
« Reply #2 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.

Speaking English as a French Frog

Draftek

  • Water Moccasin
  • Posts: 1501
Re: Transforming point from PaperSpace to ModelSpace
« Reply #3 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.
Soli Deo Gloria

TheMaster

  • Guest
Re: Transforming point from PaperSpace to ModelSpace
« Reply #4 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.  
« Last Edit: August 15, 2012, 06:16:39 PM by TT »

gile

  • Water Moccasin
  • Posts: 2232
  • Marseille, France
Re: Transforming point from PaperSpace to ModelSpace
« Reply #5 on: August 16, 2012, 02:00:43 AM »
Nice Tony, for a direct WCS2PSDCS (or PSDCS2WCS) transformation.
Speaking English as a French Frog

Dale Bartlett

  • Mosquito
  • Posts: 17
Re: Transforming point from PaperSpace to ModelSpace
« Reply #6 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

gile

  • Water Moccasin
  • Posts: 2232
  • Marseille, France
Re: Transforming point from PaperSpace to ModelSpace
« Reply #7 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) ?
Speaking English as a French Frog

Dale Bartlett

  • Mosquito
  • Posts: 17
Re: Transforming point from PaperSpace to ModelSpace
« Reply #8 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

WILL HATCH

  • Bull Frog
  • Posts: 448
Re: Transforming point from PaperSpace to ModelSpace
« Reply #9 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?

BKSpurgeon

  • Mosquito
  • Posts: 6
Re: Transforming point from PaperSpace to ModelSpace
« Reply #10 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:
  • Open up the CAD file.
  • Netload the DLL.
  • Circles should be drawn in the paper space over the geometry picked up in the model space, considering the view port UCS is defined differently.

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();
            }
        }