Friday, April 12, 2013

How to Get Current View Size

Sometimes one could run into question that you are sure you knew the answer and had dealt with it before, but just could not get it off your head right away.

A few days ago when I update an existing CAD application, I was asked to select certain entities visible in current view, be them in modelspace layout or paperspace layout, or in a active viewport of paperspace layout. I thought, "OK, I can get the size of current view and then select everything in a window that is the size of the current view (or slightly smaller than the view size). Then I went on searching Object Browser in Visual Studio for View, Viewport, ViewTableRecord, ViewportTableRecord... to see how I can easily get the size of current view in AutoCAD.

Well, it turned out that it would be unnecessarily complicated if I tried to use these classes for this task and luckily I recalled in my old VBA code I used AutoCAD system variables for similar work: VIEWSIZE and VIEWCTR.

System variable VIEWCTR provides a Point3d value as current view's centre point, while VIEWSIZE is the height of current view, measured in drawing unit. The problem left is how to know the width of current view? Again, another system variable SCREENSIZE comes to rescue: it provides the current viewport's size, but in PIXELs. Here what the unit of the size from SCREENSIZE is does not matter, we can use the ratio of width-height to calculate the current view's width in drawing unit from its height, like this:

view_width=view_height * (screen_width/screen_height)

The other thing to be careful is that the view's centre from VIEWCTR is in UCS coordinate.

Here is the code to get current view's size and its extents:

    1 using Autodesk.AutoCAD.ApplicationServices;
    2 using Autodesk.AutoCAD.DatabaseServices;
    3 using Autodesk.AutoCAD.Geometry;
    5 namespace GetViewSize
    6 {
    7     public class ViewUtil
    8     {
    9         private Document _dwg;
   11         public ViewUtil(Document dwg)
   12         {
   13             _dwg = dwg;
   14         }
   16         public Point2d GetCurrentViewSize()
   17         {
   18             //Get current view height
   19             double h = (double)Application.GetSystemVariable("VIEWSIZE");
   21             //Get current view width,
   22             //by calculate current view's width-height ratio
   23             Point2d screen = (Point2d)Application.GetSystemVariable("SCREENSIZE");
   24             double w = h * (screen.X / screen.Y);
   26             return new Point2d(w, h);
   27         }
   29         public Extents2d GetCurrentViewBound(
   30             double shrinkScale=1.0, bool drawBoundBox=false)
   31         {
   32             //Get current view size
   33             Point2d vSize = GetCurrentViewSize();
   35             double w = vSize.X * shrinkScale;
   36             double h = vSize.Y * shrinkScale;
   39             //Get current view's centre.
   40             //Note, the centre point from VIEWCTR is in UCS and
   41             //need to be transformed back to World CS
   42             Point3d cent = ((Point3d)Application.GetSystemVariable("VIEWCTR")).
   43                 TransformBy(_dwg.Editor.CurrentUserCoordinateSystem);
   45             Point2d minPoint = new Point2d(cent.X - w / 2.0, cent.Y - h / 2.0);
   46             Point2d maxPoint = new Point2d(cent.X + w / 2.0, cent.Y + h / 2.0);
   48             if (drawBoundBox)
   49             {
   50                 DrawBoundBox(minPoint, maxPoint);
   51             }
   53             return new Extents2d(minPoint, maxPoint);
   54         }
   56         private void DrawBoundBox(Point2d minPoint, Point2d maxPoint)
   57         {
   58             using (Transaction tran = _dwg.TransactionManager.StartTransaction())
   59             {
   60                 //Get current space
   61                 BlockTableRecord space = (BlockTableRecord)tran.GetObject(
   62                     _dwg.Database.CurrentSpaceId, OpenMode.ForWrite);
   64                 //Create a polyline as bounding box
   65                 Polyline pl = new Polyline(4);
   67                 pl.AddVertexAt(0, minPoint, 0.0, 0.0, 0.0);
   68                 pl.AddVertexAt(1, new Point2d(minPoint.X, maxPoint.Y), 0.0, 0.0, 0.0);
   69                 pl.AddVertexAt(2, maxPoint, 0.0, 0.0, 0.0);
   70                 pl.AddVertexAt(3, new Point2d(maxPoint.X, minPoint.Y), 0.0, 0.0, 0.0);
   71                 pl.Closed = true;
   73                 pl.SetDatabaseDefaults(_dwg.Database);
   75                 space.AppendEntity(pl);
   76                 tran.AddNewlyCreatedDBObject(pl, true);
   78                 tran.Commit();
   79             }
   80         }
   81     }
   82 }

In the GetCurrentViewBound() method, the optional argument "shrinkScale" is used for getting a slight smaller bound than the view is (or slightly larger, if it is greater than 1.0). The second optional argument is mostly for debugging purpose: if set to "true", a polyline would be drawn as the bounding box of the current view.

Here is the command class to run the code:

    1 using Autodesk.AutoCAD.ApplicationServices;
    2 using Autodesk.AutoCAD.EditorInput;
    3 using Autodesk.AutoCAD.Geometry;
    4 using Autodesk.AutoCAD.Runtime;
    6 [assembly: CommandClass(typeof(GetViewSize.MyCommands))]
    8 namespace GetViewSize
    9 {
   10     public class MyCommands
   11     {
   12         [CommandMethod("GetViewSize")]
   13         public static void RunMyCommand()
   14         {
   15             Document dwg = Application.DocumentManager.MdiActiveDocument;
   16             Editor ed = dwg.Editor;
   18             ViewUtil util = new ViewUtil(dwg);
   20             try
   21             {
   22                 //Get current view size
   23                 Point2d vSize = util.GetCurrentViewSize();
   24                 ed.WriteMessage("\nCurrent view size: " +
   25                     "H={0} W={1}", vSize.Y, vSize.X);
   27                 //Draw a bound box of current view, which
   28                 //is slightly smaller than current view
   29                 util.GetCurrentViewBound(0.95, true);
   31                 //Draw a bound box of current view, which
   32                 //is exactly the same size as current view
   33                 util.GetCurrentViewBound(1.0, true);
   34             }
   35             catch (System.Exception ex)
   36             {
   37                 ed.WriteMessage("\nError: {0}", ex.ToString());
   38             }
   40             Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
   41         }
   42     }
   43 }
 The code can be used to get current view's size. When in tiled multi-viewport modelspace, the view is the view in active viewport. When in paperspace layout, if there is no paperspace viewport active (after command "PS"), the view is the view of paperspace, while if there is a paperspace viewport active (after command "MS"), the view is the modelspace view seeing through the paperspace viewport.

Well, with paperspace viewport, one can use the size of the viewport and transform to size in modelspace in order to get the model view size seeing through the paperspace viewport. If the paperspace viewport has non-rectangular border, then the simple approach to get a rectangular view size shown in this article would be problematic (when the purpose is to selecting all entities visible in the view/viewport.



Anonymous said...

Nice article. Thank you.

Koshy Koshy said...

i'm having the same reference problem.

System.Windows.Point pointWindows = ed.PointToScreen(pickBoxCentre, 1);

System.Drawing.Point screenPt = new System.Drawing.Point((int) pointWindows.X, (int)pointWindows.Y) ;

this is what i did to solve. am i doing this right?



About Me

My photo
After graduating from university, I worked as civil engineer for more than 10 years. It was AutoCAD use that led me to the path of computer programming. Although I now do more generic business software development, such as enterprise system, timesheet, billing, web services..., AutoCAD related programming is always interesting me and I still get AutoCAD programming tasks assigned to me from time to time. So, AutoCAD goes, I go.