Zooming to a window or entity inside AutoCAD with .NET

This post was inspired by a recent email from Sreekar Devatha, who is just about to leave DevTech India to work in one of our Engineering teams in Singapore (all the best on your new adventure, Sreekar! :-).

It shows how to perform a programmatic zoom in .NET, whether to a window or to an entity. The .NET API in AutoCAD doesn't expose a handy "ZoomWindow" method, but there are a few options open that use officially supported APIs:

  1. Create a ViewTableRecord and set it as the current view (the classic ObjectARX technique, as described in this DevNote on the ADN site)
  2. Use the COM API to perform a ZoomWindow
  3. Call the ZOOM command using one of the techniques in this previous post

The first two change the view very effectively, but don't show the nice view transitions you get with the ZOOM command (hence the 3rd option).

I ended up implementing the following C# code to show each of these techniques, which can be applied to commands that zoom either to a window (ZW) or to an entity (ZE). Just change the functions used from ZoomWin() to ZoomWin2() or ZoomWin3(), as needed:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Geometry;

using Autodesk.AutoCAD.Interop;

namespace ZoomZoom

{

  public class Commands

  {

    // Zoom to a window specified by the user

    [CommandMethod("ZW")]

    static public void ZoomWindow()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

      // Get the window coordinates

      PromptPointOptions ppo =

        new PromptPointOptions(

          "\nSpecify first corner:"

        );

      PromptPointResult ppr =

        ed.GetPoint(ppo);

      if (ppr.Status != PromptStatus.OK)

        return;

      Point3d min = ppr.Value;

      PromptCornerOptions pco =

        new PromptCornerOptions(

          "\nSpecify opposite corner: ",

          ppr.Value

        );

      ppr = ed.GetCorner(pco);

      if (ppr.Status != PromptStatus.OK)

        return;

      Point3d max = ppr.Value;

      // Call out helper function

      // [Change this to ZoomWin2 or WoomWin3 to

      // use different zoom techniques]

      ZoomWin(ed, min, max);

    }

    // Zoom to the extents of an entity

    [CommandMethod("ZE")]

    static public void ZoomToEntity()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;

      // Get the entity to which we'll zoom

      PromptEntityOptions peo =

        new PromptEntityOptions(

          "\nSelect an entity:"

        );

      PromptEntityResult per = ed.GetEntity(peo);

      if (per.Status != PromptStatus.OK)

        return;

      // Extract its extents

      Extents3d ext;

      Transaction tr =

        db.TransactionManager.StartTransaction();

      using (tr)

      {

        Entity ent =

          (Entity)tr.GetObject(

            per.ObjectId,

            OpenMode.ForRead

          );

        ext =

          ent.GeometricExtents;

        tr.Commit();

      }

      ext.TransformBy(

        ed.CurrentUserCoordinateSystem.Inverse()

      );

      // Call our helper function

      // [Change this to ZoomWin2 or WoomWin3 to

      // use different zoom techniques]

      ZoomWin(ed, ext.MinPoint, ext.MaxPoint);

    }

    // Helper functions to zoom using different techniques

    // Zoom using a view object

    private static void ZoomWin(

      Editor ed, Point3d min, Point3d max

    )

    {

      Point2d min2d = new Point2d(min.X, min.Y);

      Point2d max2d = new Point2d(max.X, max.Y);

      ViewTableRecord view =

        new ViewTableRecord();

      view.CenterPoint =

        min2d + ((max2d - min2d) / 2.0);

      view.Height = max2d.Y - min2d.Y;

      view.Width = max2d.X - min2d.X;

      ed.SetCurrentView(view);

    }

    // Zoom via COM

    private static void ZoomWin2(

      Editor ed, Point3d min, Point3d max

    )

    {

      AcadApplication app =

        (AcadApplication)Application.AcadApplication;

      double[] lower =

        new double[3] { min.X, min.Y, min.Z };

      double[] upper

        = new double[3] { max.X, max.Y, max.Z };

      app.ZoomWindow(lower, upper);

    }

    // Zoom by sending a command

    private static void ZoomWin3(

      Editor ed, Point3d min, Point3d max

    )

    {

      string lower =

        min.ToString().Substring(

          1,

          min.ToString().Length - 2

        );

      string upper =

        max.ToString().Substring(

          1,

          max.ToString().Length - 2

        );

      string cmd =

        "_.ZOOM _W " + lower + " " + upper + " ";

      // Call the command synchronously using COM

      AcadApplication app =

        (AcadApplication)Application.AcadApplication;

      app.ActiveDocument.SendCommand(cmd);

      // Could also use async command calling:

      //ed.Document.SendStringToExecute(

      //  cmd, true, false, true

      //);

    }

  }

}

The code isn't as complete as I'd like: I haven't coded for all the view parameters (which are addressed in the DevNote quoted above in item 1, for those of you who are ADN members and can understand ObjectARX), and haven't tested for handling views not-aligned with the current UCS (etc., etc.). My main purpose was to outline the techniques, rather than to provide an exhaustive solution (and I'm currently on the train back from Geneva airport, having just flown to California and back for 3 days in the San Rafael office, which means any attempt on my side to make this exhaustive might end up exhausting me :-).

31 responses to “Zooming to a window or entity inside AutoCAD with .NET”

  1. Excellent post! Just fix this in ZoomWin
    It says:
    view.Width = max2d.X - max2d.Y;
    Should be:
    view.Width = max2d.X - min2d.X;

  2. Kean Walmsley Avatar

    Of course - well spotted, Hector! 🙂

    I've fixed the post.

    Kean

  3. emmaddai owusu Avatar
    emmaddai owusu

    Hi, it seems you guys are well versed in what ever you do.

    Could you help me in a application am trying to develop for my project work in school?

    I have developed this application whic calculates for coordinates of ground points on the surface of the earth. I want to represent these points in autocad without actually launching autocad.
    What i want to do is to just click a button from VB.net to plot the points and save it as dxf or dwg file which can later be opened with autocad 2002 or 2004.

    Or maybe, if you could also help me so that, as the coordinates are calculated in XYZ system, it does the representation in an autocad window on the same form.
    thanks.

  4. Kean Walmsley Avatar

    You should look into an Autodesk product called RealDWG.

    Kean

  5. Thanks Kean

    If you happen to read comments from old posts, the ZoomWin function should have type Point3d for parameters min and max.

  6. Got it - my mistake. I've fixed it.

    Kean

  7. Tim Catalano Avatar

    The ViewTableRecord method works in modelspace when i'm trying to zoom extents (using database.extmin and extmax) but is there a way to do something similar with paperspace? i can't seem to figure out the right way.

  8. Kean Walmsley Avatar

    I don't know, off the top of my head. Have you tried asking for help on the discussion groups or via ADN, if you're a member?

    Kean

  9. insider outline Avatar

    Your source code helped me to understand it in detail. Thank you.

  10. Artur Löwen Avatar
    Artur Löwen

    Here is a better version to zoom to entities which also works in 3d:

    static public void ZoomToEntity(ObjectId[] objIds)
    {
    Editor editor = Application.DocumentManager.MdiActiveDocument.Editor;

    PromptSelectionResult psr = editor.SelectImplied();
    ObjectId[] selected = null;
    if (psr.Status == PromptStatus.OK)
    selected = psr.Value.GetObjectIds();
    editor.SetImpliedSelection(objIds);

    Autodesk.AutoCAD.Internal.Utils.ZoomObjects(true);

    editor.SetImpliedSelection(selected);
    }

  11. Hello, great example! Is there an alternative way to zoom dynamically (like in version ZoomWin3) programmatically without invoking the "_ZOOM" command? the problem is that this method clears the selection, and I can't in any way programmatically recover previous selection...
    Thanks a lot
    Gf

  12. I assume by "dynamically" you mean with smooth view transitions.

    If that's the case, then this may be of help (although I don't recall how well it works in 2d - it certainly worked well for 3D).

    keanw.com/2009/02/smoothly-transitioning-between-3d-autocad-views-using-net---part-1.html
    keanw.com/2009/03/smoothly-transitioning-between-3d-autocad-views-using-net---part-2.html

    Regards,

    Kean

  13. Thanks a lot! You really are THE man... 😉
    A bit complicated but I'll try to go in that direction!

    Gianfry

  14. Just in case could be useful to someone else, this is the function I implemented to achieve Smooth Transition Zoom between views, with ease out effect. For sure it's not the best way to do it, but it's a start...

    It perform the transition from the current view to a new view defined by the rectangle between pMin and pMax.

    public static void MyDynZoom(Document doc, Point3d pMin, Point3d pMax, double timeToTake)
    {
    // Save current view
    ViewTableRecord startView = doc.Editor.GetCurrentView();

    // Calculate end transition view
    Point2d min2d = new Point2d(pMin.X, pMin.Y);
    Point2d max2d = new Point2d(pMax.X, pMax.Y);

    ViewTableRecord endView = new ViewTableRecord();
    endView.CenterPoint = min2d + ((max2d - min2d) / 2.0);
    endView.Height = max2d.Y - min2d.Y;
    endView.Width = max2d.X - min2d.X;

    using(ViewTableRecord animView = (ViewTableRecord)startView.Clone())
    {

    double last = 0;
    double sleepTime = 0;
    double pos = 0;
    for (float mu = 0; mu <= 1; mu += 0.01F)
    {
    // Interpolate center point
    animView.CenterPoint = new Point2d(
    CosInterp(startView.CenterPoint.X, endView.CenterPoint.X, mu),
    CosInterp(startView.CenterPoint.Y, endView.CenterPoint.Y, mu)
    );
    // Interpolate height
    animView.Height = CosInterp(startView.Height, endView.Height, mu);

    // Interpolate width
    animView.Width = CosInterp(startView.Width, endView.Width, mu);

    doc.Editor.SetCurrentView(animView);

    // Decrease the sleep time, ease out effect

    pos = Math.Pow(mu, 2) * (timeToTake) /2;
    sleepTime = pos - last;
    last = pos;
    Thread.Sleep((int)(sleepTime));
    }
    doc.Editor.SetCurrentView(endView);
    }
    }

  15. Great - I'm glad you managed to get something working. 🙂

    Kean

  16. Hi Kean, I know the question is not inherent with the current thread, but I didn't find a suitable post to add the comment. Even Google this time is not my friend! Sorry about that...

    Is there a way to know when a Transaction is effectively committed and executed so that changes are completed on the working database? I understand that trans.Commit() is an async call, am I right? Even the public property "IsCommitted" is flagged as soon the transaction.Commit() is issued but not effectively completed...
    Thanks

    Gf

  17. Hmm - I don't believe trans.Commit() is asynchronous. If this is about your own app, then waiting until Commit() has happened should be enough - otherwise you may need C++ to get transaction reactors.

    But yes, this is off-topic (and if I start answering off-topic questions here the blog will devolve into a support forum :-). Please try posting to the AutoCAD .NET discussion group or to ADN...

    Kean

  18. That's fair enough 😉 Thanks again anyway
    Gf

  19. Hi Kean,

    I was wondering if there's any solution to implement this solution in RealDWG engine?
    I have an application which creates DWG file, one of the requests I have is to automatically zoom the view to extents of the drawing.
    I couldn't find a way as I have no access to Editor object in acdbmngd library.

    Afshin

  20. Hi Afshin,

    You can certainly create new ViewTableRecords in the Database. As for setting the current view, that may be trickier...

    I've just noticed Database.HomeView, which appears to be editable. Could you try setting the parameters to see what happens? I'm guessing that has something to do with the ViewCube, but I could be wrong.

    Kean

  21. As usual..this post is very usefull and this helped me out..Thanks Kean

  22. Any chance of an example not aligned with UCS. Specifically setting up a view with a center point and just giving it a Dip (Degrees) , Direction (Azimuth) (Degrees) , Width and Height.

    Thanks again for all your other posts!!!

    1. Anything's possible, Piet. 🙂

      But could you give me a bit more information about what you're trying to do?

      Kean

  23. Thanks for the Post Kean! Is there a way to change ZoomToEntity to zoom to Entity's parent class DBObject? I want to be able to zoom to groups as well as entities but their parent doesn't have a definition for GeometricExtents

    1. Kean Walmsley Avatar

      Hi Nick,

      It might take a bit of work, but should be possible: you could open the grouped entities and aggregate their extents, then zoom to that...

      Regards,

      Kean

  24. Hello Kean, can we detect when the user or the program change the view state? In other word, does CAD has zoom event? Thanks.

    1. Kean Walmsley Avatar

      Hi there,

      Please post your question to the AutoCAD .NET Discussion Group:

      forums.autodesk.com/

      Regards,

      Kean

  25. Andrey Bushman Avatar
    Andrey Bushman

    Your code haven't the initializing of the `ViewTableRecord.IsPaperspaceView` property. Without it the `eNullObjectPointer` exception will happens often. More info with the screens look here: theswamp.org/in...

    1. Kean Walmsley Avatar

      Good to know, although if the absence of this property (which should be defaulting to false, anyway) leads to a crash then there's another problem somewhere (whether inside AutoCAD or elsewhere).

      This should ideally be submitted to the AutoCAD team via ADN or the discussion group.

      Kean

      1. Alexander Rivilis Avatar
        Alexander Rivilis

        Andrew talks about when we in PaperSpace:
        [code]

        private static void ZoomWin(Editor ed, Point3d min, Point3d max)
        {

        Point2d min2d = new Point2d(min.X, min.Y);
        Point2d max2d = new Point2d(max.X, max.Y);
        using (ViewTableRecord view = new ViewTableRecord())
        {
        view.CenterPoint = min2d + ((max2d - min2d) / 2.0);
        view.Height = max2d.Y - min2d.Y;
        view.Width = max2d.X - min2d.X;
        Database db = ed.Document.Database;
        view.IsPaperspaceView =
        (!db.TileMode && db.PaperSpaceVportId == ed.CurrentViewportObjectId);
        ed.SetCurrentView(view);
        }
        }
        [/code]

        So it is not a bug, but your's function work only in ModelSpace.

        1. Kean Walmsley Avatar

          Oh, I see. Yes - that makes sense.

          Kean

Leave a Reply

Your email address will not be published. Required fields are marked *