Taking a snapshot of the AutoCAD model (take 2)

In this previous post, we looked at some code to do a programmatic snapshot of AutoCAD's modelspace, saving the results to an image file.

From the discussion that followed, I realised that the code had an undesired (and unnecessary) side-effect of creating a new 3D GS View and leaving the modelspace with that view active. GS Views in AutoCAD 2007 have grey backgrounds by default, and so this change can be quite disturbing for users. The only reason we created the GS View in the first place (if one didn't already exist), was to use it to query the view position/target/up vector/field width and height and apply it to our new view. Thankfully it seems this can also be determined directly from the viewport.

So rather than calling GetGSView() and using the returned view to get that information, we now simply call SetViewFromViewport() specifying the viewport number held in CVPORT, and the graphics system manager for that document handles the rest.

Here's the updated C# code, which appears to achieve the same goals without the side-effect. Check line 124 for the new code, a few extraneous lines around it having been removed:

    1 using Autodesk.AutoCAD.ApplicationServices;

    2 using Autodesk.AutoCAD.DatabaseServices;

    3 using Autodesk.AutoCAD.EditorInput;

    4 using Autodesk.AutoCAD.GraphicsInterface;

    5 using Autodesk.AutoCAD.GraphicsSystem;

    6 using Autodesk.AutoCAD.Runtime;

    7 using Autodesk.AutoCAD.Interop;

    8 using System.Drawing;

    9

   10 namespace OffscreenImageCreation

   11 {

   12   public class Commands

   13   {

   14     [CommandMethod("OSS")]

   15     static public void OffscreenSnapshot()

   16     {

   17       CreateSphere();

   18       SnapshotToFile(

   19         "c:\\sphere-Wireframe2D.png",

   20         VisualStyleType.Wireframe2D

   21       );

   22       SnapshotToFile(

   23         "c:\\sphere-Hidden.png",

   24         VisualStyleType.Hidden

   25       );

   26       SnapshotToFile(

   27         "c:\\sphere-Basic.png",

   28         VisualStyleType.Basic

   29       );

   30       SnapshotToFile(

   31         "c:\\sphere-ColorChange.png",

   32         VisualStyleType.ColorChange

   33       );

   34       SnapshotToFile(

   35         "c:\\sphere-Conceptual.png",

   36         VisualStyleType.Conceptual

   37       );

   38       SnapshotToFile(

   39         "c:\\sphere-Flat.png",

   40         VisualStyleType.Flat

   41       );

   42       SnapshotToFile(

   43         "c:\\sphere-Gouraud.png",

   44         VisualStyleType.Gouraud

   45       );

   46       SnapshotToFile(

   47         "c:\\sphere-Realistic.png",

   48         VisualStyleType.Realistic

   49       );

   50     }

   51

   52     static public void CreateSphere()

   53     {

   54       Document doc =

   55         Application.DocumentManager.MdiActiveDocument;

   56       Database db = doc.Database;

   57       Editor ed = doc.Editor;

   58

   59       Transaction tr =

   60         doc.TransactionManager.StartTransaction();

   61       using (tr)

   62       {

   63         BlockTable bt =

   64           (BlockTable)tr.GetObject(

   65             db.BlockTableId,

   66             OpenMode.ForRead

   67           );

   68         BlockTableRecord btr =

   69           (BlockTableRecord)tr.GetObject(

   70             bt[BlockTableRecord.ModelSpace],

   71             OpenMode.ForWrite

   72           );

   73         Solid3d sol = new Solid3d();

   74         sol.CreateSphere(10.0);

   75

   76         const string matname =

   77           "Sitework.Paving - Surfacing.Riverstone.Mortared";

   78         DBDictionary matdict =

   79           (DBDictionary)tr.GetObject(

   80             db.MaterialDictionaryId,

   81             OpenMode.ForRead

   82           );

   83         if (matdict.Contains(matname))

   84         {

   85           sol.Material = matname;

   86         }

   87         else

   88         {

   89           ed.WriteMessage(

   90             "\nMaterial (" + matname + ") not found" +

   91             " - sphere will be rendered without it.",

   92             matname

   93           );

   94         }

   95         btr.AppendEntity(sol);

   96

   97         tr.AddNewlyCreatedDBObject(sol, true);

   98         tr.Commit();

   99       }

  100       AcadApplication acadApp =

  101         (AcadApplication)Application.AcadApplication;

  102       acadApp.ZoomExtents();

  103     }

  104

  105     static public void SnapshotToFile(

  106       string filename,

  107       VisualStyleType vst

  108     )

  109     {

  110       Document doc =

  111         Application.DocumentManager.MdiActiveDocument;

  112       Editor ed = doc.Editor;

  113       Database db = doc.Database;

  114       Manager gsm = doc.GraphicsManager;

  115

  116       // Get some AutoCAD system variables

  117       int vpn =

  118         System.Convert.ToInt32(

  119           Application.GetSystemVariable("CVPORT")

  120         );

  121

  122       using (View view = new View())

  123       {

  124         gsm.SetViewFromViewport(view, vpn);

  125

  126         // Set the visual style to the one passed in

  127         view.VisualStyle = new VisualStyle(vst);

  128

  129         Device dev =

  130           gsm.CreateAutoCADOffScreenDevice();

  131         using (dev)

  132         {

  133           dev.OnSize(gsm.DisplaySize);

  134

  135           // Set the render type and the background color

  136           dev.DeviceRenderType = RendererType.Default;

  137           dev.BackgroundColor = Color.White;

  138

  139           // Add the view to the device and update it

  140           dev.Add(view);

  141           dev.Update();

  142

  143           using (Model model = gsm.CreateAutoCADModel())

  144           {

  145             Transaction tr =

  146               db.TransactionManager.StartTransaction();

  147             using (tr)

  148             {

  149               // Add the modelspace to the view

  150               // It's a container but also a drawable

  151               BlockTable bt =

  152                 (BlockTable)tr.GetObject(

  153                   db.BlockTableId,

  154                   OpenMode.ForRead

  155                 );

  156               BlockTableRecord btr =

  157                 (BlockTableRecord)tr.GetObject(

  158                   bt[BlockTableRecord.ModelSpace],

  159                   OpenMode.ForRead

  160                 );

  161               view.Add(btr, model);

  162               tr.Commit();

  163             }

  164             // Take the snapshot

  165             Rectangle rect = view.Viewport;

  166             using (Bitmap bitmap = view.GetSnapshot(rect))

  167             {

  168               bitmap.Save(filename);

  169               ed.WriteMessage(

  170                 "\nSnapshot image saved to: " +

  171                 filename

  172               );

  173               // Clean up

  174               view.EraseAll();

  175               dev.Erase(view);

  176             }

  177           }

  178         }

  179       }

  180     }

  181   }

  182 }

You can also download the .cs file from here.

April 18, 2007 in AutoCAD, AutoCAD .NET, Graphics system | Permalink | Comments (8) | TrackBack

Taking a snapshot of the AutoCAD model using .NET

This topic is a follow-up to the previous post dealing with offscreen rendering, and was suggested (and is based on code provided) by Fenton Webb, a member of DevTech Americas who has just moved across to San Rafael. Fenton was until recently part of DevTech EMEA, working from the UK.

The basic technique is similar to the one used in the previous post: we use the graphics system to create an "offscreen device", which we then set up and request to create a snapshot of our model. This can work on graphical objects stored in any AutoCAD database, but in this case we use it to capture an image of the contents of the active drawing's modelspace (after, once again, adding a sphere to it).

To spice up the results a little, I added the option to specify a particular visual style for the output. I've inserted the created images at the bottom (as before, you will need to add a particular material to the current drawing in order to get exactly the same results I as I have).

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.GraphicsInterface;

using Autodesk.AutoCAD.GraphicsSystem;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.Interop;

using System.Drawing;


namespace OffscreenImageCreation

{

  public class Commands

  {

    [CommandMethod("OSS")]

    static public void OffscreenSnapshot()

    {

      CreateSphere();

      SnapshotToFile(

        "c:\\sphere-Wireframe2D.png",

        VisualStyleType.Wireframe2D

      );

      SnapshotToFile(

        "c:\\sphere-Hidden.png",

        VisualStyleType.Hidden

      );

      SnapshotToFile(

        "c:\\sphere-Basic.png",

        VisualStyleType.Basic

      );

      SnapshotToFile(

        "c:\\sphere-ColorChange.png",

        VisualStyleType.ColorChange

      );

      SnapshotToFile(

        "c:\\sphere-Conceptual.png",

        VisualStyleType.Conceptual

      );

      SnapshotToFile(

        "c:\\sphere-Flat.png",

        VisualStyleType.Flat

      );

      SnapshotToFile(

        "c:\\sphere-Gouraud.png",

        VisualStyleType.Gouraud

      );

      SnapshotToFile(

        "c:\\sphere-Realistic.png",

        VisualStyleType.Realistic

      );

    }


    static public void CreateSphere()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;


      Transaction tr =

        doc.TransactionManager.StartTransaction();

      using (tr)

      {

        BlockTable bt =

          (BlockTable)tr.GetObject(

            db.BlockTableId,

            OpenMode.ForRead

          );

        BlockTableRecord btr =

          (BlockTableRecord)tr.GetObject(

            bt[BlockTableRecord.ModelSpace],

            OpenMode.ForWrite

          );

        Solid3d sol = new Solid3d();

        sol.CreateSphere(10.0);


        const string matname =

          "Sitework.Paving - Surfacing.Riverstone.Mortared";

        DBDictionary matdict =

          (DBDictionary)tr.GetObject(

            db.MaterialDictionaryId,

            OpenMode.ForRead

          );

        if (matdict.Contains(matname))

        {

          sol.Material = matname;

        }

        else

        {

          ed.WriteMessage(

            "\nMaterial (" + matname + ") not found" +

            " - sphere will be rendered without it.",

            matname

          );

        }

        btr.AppendEntity(sol);


        tr.AddNewlyCreatedDBObject(sol, true);

        tr.Commit();

      }

      AcadApplication acadApp =

        (AcadApplication)Application.AcadApplication;

      acadApp.ZoomExtents();

    }


    static public void SnapshotToFile(

      string filename,

      VisualStyleType vst

    )

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      Database db = doc.Database;

      Manager gsm = doc.GraphicsManager;


      // Get some AutoCAD system variables

      int vpn =

        System.Convert.ToInt32(

          Application.GetSystemVariable("CVPORT")

        );


      // Get AutoCAD's GS view for this document...

      View gsv =

        doc.GraphicsManager.GetGsView(vpn, true);


      // ... but create a new one for the actual snapshot

      using (View view = new View())

      {

        // Set the view to be just like the one

        // in the AutoCAD editor

        view.Viewport = gsv.Viewport;

        view.SetView(

          gsv.Position,

          gsv.Target,

          gsv.UpVector,

          gsv.FieldWidth,

          gsv.FieldHeight

        );

        // Set the visual style to the one passed in

        view.VisualStyle = new VisualStyle(vst);


        Device dev =

          gsm.CreateAutoCADOffScreenDevice();

        using (dev)

        {

          dev.OnSize(gsm.DisplaySize);


          // Set the render type and the background color

          dev.DeviceRenderType = RendererType.Default;

          dev.BackgroundColor = Color.White;


          // Add the view to the device and update it

          dev.Add(view);

          dev.Update();


          using (Model model = gsm.CreateAutoCADModel())

          {

            Transaction tr =

              db.TransactionManager.StartTransaction();

            using (tr)

            {

              // Add the modelspace to the view

              // It's a container but also a drawable

              BlockTable bt =

                (BlockTable)tr.GetObject(

                  db.BlockTableId,

                  OpenMode.ForRead

                );

              BlockTableRecord btr =

                (BlockTableRecord)tr.GetObject(

                  bt[BlockTableRecord.ModelSpace],

                  OpenMode.ForRead

                );

              view.Add(btr, model);

              tr.Commit();

            }

            // Take the snapshot

            Rectangle rect = view.Viewport;

            using (Bitmap bitmap = view.GetSnapshot(rect))

            {

              bitmap.Save(filename);

              ed.WriteMessage(

                "\nSnapshot image saved to: " +

                filename

              );

              // Clean up

              view.EraseAll();

              dev.Erase(view);

            }

          }

        }

      }

    }

  }

}

And here are the images created by the OSS command, listed by their visual style type...

Wireframe2D:

spherewireframe2d

Hidden:

spherehidden

Basic:

spherebasic

ColorChange:

spherecolorchange

Conceptual:

sphereconceptual

Flat:

sphereflat

Gouraud:

spheregouraud

Realistic:

sphererealistic

Update: an improved version of the above code is now available in this follow-up post.

April 9, 2007 in AutoCAD, AutoCAD .NET, Graphics system | Permalink | Comments (9) | TrackBack

Rendering AutoCAD models offscreen using .NET

This question came up in an internal discussion, and I thought I'd share the code provided by our Engineering team with you (with a few minor additions from my side, of course).

The idea is to render a 3D scene off-screen (i.e. save to file the rendered image not visible in the editor). In the below code, we do zoom to the extents of the 3D model that gets created, just to show it's there, but the rendering activity itself is not performed by the 3D view that is used to generate the graphics for AutoCAD's editor window.

A 3D model does need to be loaded in the editor for this to work, so I decided to add a simple sphere to the modelspace and set its material to one that renders nicely (Sitework.Paving - Surfacing.Riverstone.Mortared). The code doesn't import the material programmatically, so you'll want to make sure it's there in the drawing to get the full effect of the rendering (by dragging the material into the drawing view from the materials tool palette, for instance), or to change the code to point to one that exists in the active drawing.

Here's the C# code:

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

using Autodesk.AutoCAD.GraphicsSystem;

using Autodesk.AutoCAD.Interop;

using System.Drawing;


namespace OffscreenRendering

{

  public class Commands

  {

    [CommandMethod("OSR")]

    static public void OffscreenRender()

    {

      CreateSphere();

      RenderToFile("c:\\sphere.png");

    }


    static public void CreateSphere()

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Database db = doc.Database;

      Editor ed = doc.Editor;


      Transaction tr =

        doc.TransactionManager.StartTransaction();

      using (tr)

      {

        BlockTable bt =

          (BlockTable)tr.GetObject(

            db.BlockTableId,

            OpenMode.ForRead

          );

        BlockTableRecord btr =

          (BlockTableRecord)tr.GetObject(

            bt[BlockTableRecord.ModelSpace],

            OpenMode.ForWrite

          );

        Solid3d sol = new Solid3d();

        sol.CreateSphere(10.0);


        const string matname =

          "Sitework.Paving - Surfacing.Riverstone.Mortared";

        DBDictionary matdict =

          (DBDictionary)tr.GetObject(

            db.MaterialDictionaryId,

            OpenMode.ForRead

          );

        if (matdict.Contains(matname))

        {

          sol.Material = matname;

        }

        else

        {

          ed.WriteMessage(

            "\nMaterial (" + matname + ") not found" +

            " - sphere will be rendered without it.",

            matname

          );

        }

        btr.AppendEntity(sol);


        tr.AddNewlyCreatedDBObject(sol, true);

        tr.Commit();

      }

      AcadApplication acadApp =

        (AcadApplication)Application.AcadApplication;

      acadApp.ZoomExtents();

    }


    static public void RenderToFile(string filename)

    {

      Document doc =

        Application.DocumentManager.MdiActiveDocument;

      Editor ed = doc.Editor;

      int vpn =

        System.Convert.ToInt32(

          Application.GetSystemVariable("CVPORT")

        );

      View gsv =

        doc.GraphicsManager.GetGsView(vpn, true);

      using (View view = gsv.Clone(true, true))

      {

        Device dev =

          doc.GraphicsManager.CreateAutoCADOffScreenDevice();

        using (dev)

        {

          dev.OnSize(doc.GraphicsManager.DisplaySize);

          dev.DeviceRenderType = RendererType.FullRender;

          dev.Add(view);

          using (Bitmap bitmap = view.RenderToImage())

          {

            bitmap.Save(filename);

            ed.WriteMessage(

              "\nRendered image saved to: " +

              filename

            );

          }

        }

      }

    }

  }

}

And here's the resized contents of the file created at c:\sphere.png by the OSR command:

Offscreen_rendered_sphere

April 5, 2007 in AutoCAD, AutoCAD .NET, Graphics system | Permalink | Comments (8) | TrackBack