September 2014

Sun Mon Tue Wed Thu Fri Sat
  1 2 3 4 5 6
7 8 9 10 11 12 13
14 15 16 17 18 19 20
21 22 23 24 25 26 27
28 29 30        










« Attaching a (QR Code) raster image using AutoCAD .NET | Main | RockScroll: my new favourite Visual Studio Add-In »

August 23, 2010

Attaching an “upright” raster image at a user-specified location in an AutoCAD drawing using .NET

As promised, the code in this post extends that from the last post to add some user selection to the process of defining and placing a QR Code in an AutoCAD drawing.

This version of the code also makes sure the raster is positioned in an “upright” orientation, irrespective of the order in which the corners are selected. Many images need to be positioned in such a way (although QR Codes are presumably more tolerant due to the positioning squares in three of the corners), so I expect this technique will be of interest to various people who need to place images at user-specified locations. QR Codes are also square, so we’re choosing the smallest of the axes to define size (we could very easily also take the largest). This approach will become even more interesting when we migrate it to a jig, so we actually see a square area during the selection process.

One final change: I’ve used code from this previous post to add the message string as XData to the raster image. This will allow us to provide some kind of interface for updating the string, in the future (should we need to). Although we could probably also divine this from the image's URL, as an alternative approach.

Here’s the updated C# code, with modified or new lines in red (here’s the source file for download):

    1 using Autodesk.AutoCAD.Runtime;

    2 using Autodesk.AutoCAD.ApplicationServices;

    3 using Autodesk.AutoCAD.DatabaseServices;

    4 using Autodesk.AutoCAD.EditorInput;

    5 using Autodesk.AutoCAD.Geometry;

    6 using System;

    7 

    8 namespace QRCodeApplication

    9 {

   10   public class Commands

   11   {

   12     [CommandMethod("QR")]

   13     static public void QRCode()

   14     {

   15       // Base record name and URL constants

   16 

   17       const string recBase = "ADNP_QR";

   18       const string rootUrl =

   19         "http://chart.apis.google.com/chart?cht=qr&chs=500x500&chl=";

   20 

   21       Document doc =

   22         Application.DocumentManager.MdiActiveDocument;

   23       Database db = doc.Database;

   24       Editor ed = doc.Editor;

   25 

   26       PromptResult pr =

   27         ed.GetString("\nEnter email address to encode: ");

   28       if (pr.Status != PromptStatus.OK)

   29         return;

   30 

   31       // Encode the colon and the @ symbol for the URL

   32 

   33       string message =

   34         "mailto%3A" + pr.StringResult.Replace("@", "%40");

   35 

   36       Transaction tr =

   37         doc.TransactionManager.StartTransaction();

   38       using (tr)

   39       {

   40         ObjectId dictId =

   41           RasterImageDef.GetImageDictionary(db);

   42 

   43         if (dictId.IsNull)

   44         {

   45           // Image dictionary doesn't exist, create new

   46 

   47           dictId =

   48             RasterImageDef.CreateImageDictionary(db);

   49         }

   50 

   51         // Open the image dictionary

   52 

   53         DBDictionary dict =

   54           (DBDictionary)tr.GetObject(

   55             dictId,

   56             OpenMode.ForRead

   57           );

   58 

   59         // Get a unique record name for our raster image

   60         // definition

   61 

   62         int i = 0;

   63         string recName = recBase + i.ToString();

   64 

   65         while (dict.Contains(recName))

   66         {

   67           i++;

   68           recName = recBase + i.ToString();

   69         }

   70 

   71         RasterImageDef rid = new RasterImageDef();

   72 

   73         // Set its source image

   74 

   75         rid.SourceFileName = rootUrl + message;

   76 

   77         // Load it

   78 

   79         rid.Load();

   80         dict.UpgradeOpen();

   81 

   82         ObjectId defId = dict.SetAt(recName, rid);

   83 

   84         // Let the transaction know

   85 

   86         tr.AddNewlyCreatedDBObject(rid, true);

   87 

   88         PromptPointResult ppr =

   89           ed.GetPoint("\nFirst corner of QR Code: ");

   90         if (ppr.Status != PromptStatus.OK)

   91           return;

   92 

   93         Point3d start = ppr.Value;

   94 

   95         ppr =

   96           ed.GetCorner("\nSecond corner of QR Code: ", start);

   97         if (ppr.Status != PromptStatus.OK)

   98           return;

   99 

  100         // Get offset between the two corners

  101 

  102         Vector3d diff = ppr.Value - start;

  103 

  104         // Get the smallest of the X and Y

  105         // (could also be the largest - this is a choice)

  106 

  107         double size =

  108           Math.Min(Math.Abs(diff.X), Math.Abs(diff.Y));

  109 

  110         // If we're at zero size, don't update

  111 

  112         if (size < Tolerance.Global.EqualPoint)

  113           return;

  114 

  115         // Determing the image's orientation...

  116 

  117         // The original will depend on the order of the corners

  118         // It will be offset to the left and/or down depending

  119         // on the values of the vector between the two points

  120 

  121         Point3d orig;

  122 

  123         // The axes stay the same, as we will always keep the

  124         // image oriented the same way relative to the UCS

  125 

  126         Vector3d xAxis = new Vector3d(size, 0, 0);

  127         Vector3d yAxis = new Vector3d(0, size, 0);

  128 

  129         if (diff.X > 0 && diff.Y > 0) // Dragging top-right

  130           orig = start;

  131         else if (diff.X < 0 && diff.Y > 0) // Top-left

  132           orig = start + new Vector3d(-size, 0, 0);

  133         else if (diff.X > 0 && diff.Y < 0) // Bottom-right

  134           orig = start + new Vector3d(0, -size, 0);

  135         else // if (diff.X < 0 && diff.Y < 0) // Bottom-left

  136           orig = start - new Vector3d(size, size, 0);

  137 

  138         // Create the raster image that references the

  139         // definition

  140 

  141         RasterImage ri = new RasterImage();

  142         ri.ImageDefId = defId;

  143         ri.ShowImage = true;

  144 

  145         // Set the image's orientation in WCS

  146 

  147         Matrix3d ucs = ed.CurrentUserCoordinateSystem;

  148 

  149         ri.Orientation =

  150           new CoordinateSystem3d(

  151             orig.TransformBy(ucs),

  152             xAxis.TransformBy(ucs),

  153             yAxis.TransformBy(ucs)

  154           );

  155 

  156         BlockTable bt =

  157           (BlockTable)tr.GetObject(

  158             db.BlockTableId,

  159             OpenMode.ForRead

  160           );

  161 

  162         BlockTableRecord btr =

  163           (BlockTableRecord)tr.GetObject(

  164             bt[BlockTableRecord.ModelSpace],

  165             OpenMode.ForWrite

  166           );

  167 

  168         btr.AppendEntity(ri);

  169         tr.AddNewlyCreatedDBObject(ri, true);

  170 

  171         // Create a reactor between the RasterImage and the

  172         // RasterImageDef to avoid the "unreferenced"

  173         // warning in the XRef palette

  174 

  175         RasterImage.EnableReactors(true);

  176         ri.AssociateRasterDef(rid);

  177 

  178         // Let's add our message string as XData,

  179         // in case we need it later

  180 

  181         AddRegAppTableRecord("ADNP_QR");

  182         ResultBuffer rb =

  183           new ResultBuffer(

  184             new TypedValue(1001, "ADNP_QR"),

  185             new TypedValue(1000, message)

  186           );

  187         ri.XData = rb;

  188         rb.Dispose();

  189 

  190         tr.Commit();

  191       }

  192     }

  193 

  194     static void AddRegAppTableRecord(string regAppName)

  195     {

  196       Document doc =

  197         Application.DocumentManager.MdiActiveDocument;

  198       Editor ed = doc.Editor;

  199       Database db = doc.Database;

  200 

  201       Transaction tr =

  202         doc.TransactionManager.StartTransaction();

  203       using (tr)

  204       {

  205         RegAppTable rat =

  206           (RegAppTable)tr.GetObject(

  207             db.RegAppTableId,

  208             OpenMode.ForRead,

  209             false

  210           );

  211         if (!rat.Has(regAppName))

  212         {

  213           rat.UpgradeOpen();

  214           RegAppTableRecord ratr =

  215             new RegAppTableRecord();

  216           ratr.Name = regAppName;

  217           rat.Add(ratr);

  218           tr.AddNewlyCreatedDBObject(ratr, true);

  219         }

  220         tr.Commit();

  221       }

  222     }

  223   }

  224 }

Let’s take a look at the individual changes:

  • Line 6
    • Add the System namespace for the various Math functions we’re now using
  • Lines 24-34
    • Query the user for the email address to encode
  • Lines 88-136
    • Query for the location of the image, manipulate the position of the image so that it's "upright"
  • Lines 145-154
    • Transform the orientation relative to the current UCS
  • Lines 178-188 & 194-222
    • Add the message string as XData, so we can come back and get it/update it in the future

We can see that as we run the QR command we can enter an email address to encode (we’ll extend this UI to cover other types of relevant information, at some point) as well as the location of the QR Code. Irrespective of the order of corner selection, the raster is always square and upright:

Selecting the location of various QR Codes in AutoCAD

After the positioning of our last QR Code

blog comments powered by Disqus

Feed/Share

10 Random Posts