Another .NET AutoCAD blog

Just found this:

http://dwgdotnet.blogspot.com/

Along with this blog, that brings the CAD .NET blogs to a total of 4 :)

Through the interface: http://through-the-interface.typepad.com/
Bobby C. Jones: http://bobbycjones.spaces.live.com/
Cup o Cad .NET: http://cupocadnet.blogspot.com/
Dwg Dot Net: http://dwgdotnet.blogspot.com/

Any suggestions for additions are welcome, of course.

Posted by Bert Vanpeteghem 0 reacties

Using Jigs from Palettes

Facing a problem when trying to combine Jigs and Palettes, I posted a question on the AutoDesk Discussion Forums.

The problem is as followed: I implemented some code from the TTI blog to create polylines with a Jig. (http://through-the-interface.typepad.com/through_the_interface/2006/11/controlling_int_1.html)

It is basically a Jig prompting the user to set points while a line is drawn to show the possible result. This mimics the PLINE functionality, which should be familiar to all of us :).

When doing this from a command, all is good. The line appears nicely when the user is moving his mouse. However, when doing this from a button on a palette, the line drawn by the Jig is not visible, nor are snaps.

I also noticed that when running from a command, the comboboxes in the toolbars lock up, but this does not happen from the palette.

To get around this, Tony Tanzillo posted a nice solution with the AddCommand method in Autodesk.AutoCAD.Internals.Utils. This would work nicely, but this particular project is aimed at AutoCad 2007, where the AddCommand method is not yet present.

So, how can you get around this problem without AddCommand?

There may be many answers, but here is my implementation:

Say there’s a Palette where we want to display the number of Points or Vertices in the Polyline the user has just drawn with the Jig.

image

To display the number of points, we need to have the Polyline that was drawn. So we need to call a method returning the Polyline that was drawn.

Now, the first button is where the problem is. There, I call the Jig directly from the button, and if you’d run the code at the CCN repository, you’d see that the Jig does not really behave as expected.

So, I needed another way. When the Jig is run from a command, the problem is non existent. Bottom line: run a command from the palette, and make sure that the palette receives the result from the command.

We need a command to be run: so that will be the command from the Through The Interface post: MYPOLY. I changed the code a little bit, so that the promt itself is in a separate class.

This simply looks like this:

   1: [CommandMethod("MYPOLY")] 
   2: public void MyPolyJig() 
   3: { 
   4:     new Prompts().PromptNewPolylineByPoints(); 
   5: } 

Second we need a delegate that we can call from the PromptNewPolylineByPoints method, and we need to invoke that delegate from the prompt:



   1: public static Action<Polyline> ActionOnPromptPolyline;

The code stays the same overall, only difference is at the end:



   1: // Added this code, so that a certain action can be called after prompting for polyline
   2: if (ActionOnPromptPolyline != null)
   3: {
   4:     try
   5:     {
   6:         ActionOnPromptPolyline.Invoke(resultingPolyline);
   7:     }
   8:     catch (Exception)
   9:     {
  10:  
  11:         throw;
  12:     }
  13:     finally
  14:     {
  15:         ActionOnPromptPolyline = null;
  16:     }
  17: }



Only thing left to do is setting an action to do when prompting the polyline, and we do this in the buttons Click Event:



   1: private void promptPolylineViaCommand_Click(object sender, EventArgs e)
   2: {
   3:  
   4:     // SendStringToExecute has asynchronous behaviour, so we cannot inspect the result of the
   5:     // command here.
   6:  
   7:     // Set the actionOnPromptPolyline delegate to the desired action and 
   8:     // in the command the action will be executed.
   9:     Prompts.ActionOnPromptPolyline = OnPromptPolyline;
  10:  
  11:     // call command
  12:     Autodesk.AutoCAD.ApplicationServices.Application.
  13:         DocumentManager.MdiActiveDocument.SendStringToExecute("MYPOLY ", true, false, false);
  14:  
  15: }

And, finally, doing the desired logic on the resulting polyline:



   1: private void OnPromptPolyline(Polyline polyline)
   2: {
   3:     if (polyline == null || !polyline.ObjectId.IsValid)
   4:     {
   5:         numberOfPointsTextbox.Text = "No polyline drawn";
   6:         return;
   7:     }
   8:  
   9:     numberOfPointsTextbox.Text = polyline.NumberOfVertices.ToString();
  10:  
  11: }

That’s it, this nicely solved the problem for me. If you have any other solutions, or a different approach, please share :).


Links for the post:


Posted by Bert Vanpeteghem 1 reacties

Migrating AutoCAD COM API applications to AutoCAD 2010

1. Change your CreateObject and GetObject calls from “AutoCAD.Application.17” to “AutoCAD.Application.18”

2. Change the reference “AutoCAD” from “AutoCAD 2009 Type Library” to “AutoCAD 2010 Type Library” (Autodesk.AutoCAD.Interop)

3. Change the reference “AXDBLib” from “AutoCAD/ObjectDBX Common 17.0 Type Library” to “AutoCAD/ObjectDBX Common 18.0 Type Library” (Autodesk.AutoCAD.Interop.Common)

See also:

http://autocad.autodesk.com/?nd=autocad_tips_tutorials_detail&adsk_tip_id=676&jid=2036

 

You will probably have to make some extra changes when reading/saving .dwg’s but I have yet to check those problems.

Posted by Bert Vanpeteghem 2 reacties

Revit API introduction webcast

Note to self: Jeremy Tammik posted a webcast on the Revit API: http://thebuildingcoder.typepad.com/blog/2009/05/revit-api-introduction-webcast.html

Posted by Bert Vanpeteghem 0 reacties

Rarities: another CAD .NET blog

I found another blog (partially) focused on .NET in AutoCAD. I didn’t know that there existed one next to Through The Interface…

So, here it is: http://bobbycjones.spaces.live.com/blog/

And he might just have brought me a solution to a problem I’ve been facing today: He mentiones that when using Database.ReadDwgFile, you ALWAYS need to dispose that.

I’ve been struggling with random AccessViolationException error’s when trying to set Block Properties. This might solve it… Let’s hope so.

Posted by Bert Vanpeteghem 1 reacties

Disable and restore Code Analysis for all projects in a solution

Code Analysis is a Visual Studio tool that can analyze your code and warn you about bad practices and plain bad code.

It can be a very handy tool that can lift your code to a higher level and allows you to simply create better and more maintainable applications.

However, having code analysis enabled, severely slows down your build. Changing a little thing in code causes Code Analysis to run on the next build, which can be plain annoying.

For these reasons I have created a little Visual Studio Add In, which can disable Code Analysis on all projects in the currently opened solution, and can restore the initial settings.

The project home is: http://code.google.com/p/codeanalyzethis/

You can download the source  here: http://code.google.com/p/codeanalyzethis/downloads/list

Or you can check out the source with SVN: svn checkout http://codeanalyzethis.googlecode.com/svn/trunk/

I am not familiar with administering Open Source so I just picked the GNU GPL v3 licence. If anyone has any considerations towards the licence, please let me know.

Posted by Bert Vanpeteghem 0 reacties

Using App.Config with AutoCad

In Winforms projects, it is quite common to use app.config. For instance, to store settings  and application parameters etc. When programming AutoCad extensions, you’ll run into problems: trying to address the default app.config file leads you to the app.config of the running process, being AutoCad. AutoCad has provided a solution for this (although it would be possible to do this yourself): they created an acad.exe.config.

Placing your application settings in acad.exe.config lets frameworks like Unity, Enterprise library and your own application to run correctly after all. This solution is not perfect, and still causes lots of problems with Unity, but there must be workarounds. This, again, might be interesting for a more extensive blog post on the subject. Just let me know if this is desired…

Posted by Bert Vanpeteghem 2 reacties

Geometry and Algebra libraries

Have you ever spent too much time figuring out a difficult algorithm with AutoCAD API’s Vectors and Matrices? I feel I have. In fact, I know I have. Last year, I needed several algorithms, such as Point In Polygon, Bounding Boxes and Convex Hull. I looked up the algorithms on Wikipedia or some other computational library, and I found mostly C or C++ code doing the job.

I know these languages, but I don’t use them all the time. It also seemed like a waste of time trying to write them in C# or VB. And it looked ugly as hell. Searching a little bit further, I found that these libraries actually already exist. More importantly, they are also very extensive and Open Source! These libraries are mostly GIS-oriented, but can definitely be used in CAD calculations too. A polygon is a polygon, no?

The most important one I found is Topology Framework, this is actually a collection of libraries with slight modifications and renamings. There are several frameworks referenced by this one. And they can all be found under the Credits section.

Today, I read the following blogpost. It mentions a numerical library providing mathematical functions for .NET. I haven’t looked into it in detail, but if you’re no longer happy with the Math namespace in .NET, this might be worth checking out: dnAnalytics

I might do a post on how to use these frameworks together with the AutoCad API, so if you’re interested, let me know…

Posted by Bert Vanpeteghem 0 reacties

Screencast on fluent interfaces

Ever since I’ve used Fluent NHibernate I’ve become a big fan of Fluent interfaces. There is something very sexy, but more important, literally ‘fluent’ about using them.

Within possibilities I have been trying to implement the technique in my own coding & frameworks. While doing that, I found that it is really important to have a clear strategy, before starting to create all types of methods returning the ‘this’ object.

For example, when creating wrapper classes for selections in AutoCad, the Fluent styles seem very appropriate. (Note to self: blogpost ;).

Anyway, there is a great screencast up on Dimecasts about creating Fluent Interfaces. The screencast is not very extensive, but gives a nice overview and strategy on creating them.

Posted by Bert Vanpeteghem 0 reacties

Jigging Block Properties

A few days ago, I noticed a post on the AutoDesk Forums, asking for a way to edit a block property, immediately after inserting the block.

I bet that there is more than one way to handle this kind of action, but I’ll describe how I’d do it.

There are a few steps to do, before you can do this action.

First, to insert a Block and Jig it, you need to insert the block into the drawing. Next step is to position the block with a Jig. And then use a Jig that controls some properties of the Jig.
In my case I will use a Jig that controls the rotation of the block, and the length-property.

Note that this could be easily mimicked by just a regular rectangle, but you’ll get the point. I believe that being creative with this technique can lead to powerful, very user-friendly mechanisms.

To the code: Setting up the basics first, as these will happen exactly as in my last post, I will not include these in the code samples. Complete files of the code can be found on Google Code. Just go to the tab ‘Source’, click on ‘Browse’, expand the trunk and choose ‘BlockPropertyDrag’.

The first thing we need to do, is to set everything up. So basically, insert a block.

   1: Database db = doc.Database;
   2: BlockTable blockTable = t.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
   3:  
   4: // first, create the block reference in memory
   5: ObjectId blockDefinitionId = blockTable["dummy"];
   6: BlockReference blockReference = new BlockReference(
   7:     new Point3d(0, 0, 0), blockDefinitionId);
   8:  
   9: // first append the block
  10: BlockTableRecord modelSpace = t.GetObject(
  11:     blockTable[BlockTableRecord.ModelSpace], OpenMode.ForWrite, false) as BlockTableRecord;
  12:  
  13: modelSpace.AppendEntity(blockReference);
  14:  
  15: t.AddNewlyCreatedDBObject(blockReference, true);


Then we will first call the ‘place-the-block-into-position’-Jig:



   1: // allow user to move the block around with the InsertBlockJig
   2: InsertBlockJig insertBlockJig = new InsertBlockJig(blockReference);
   3:  
   4: if (ed.Drag(insertBlockJig).Status != PromptStatus.OK)
   5:     return;

And then we call the property-drag Jig:



   1: // do the property drag jig on the block
   2: BlockPropertyDragJig dragJig = new BlockPropertyDragJig(blockReference);
   3:  
   4: if (ed.Drag(dragJig).Status != PromptStatus.OK)
   5:     return;
   6:  
   7: t.Commit();

So far for the command. Of course, the real action is in the jigs. I’ll only show the Update() methods, the rest of the code is very default, and again, can be found on Google Code.


I did add 2 readonly properties to the Jigs: CurrentPosition and JiggedBlockReference. These properties basically just cast the protected Entity to BlockReference and return its position.


Our InsertBlockJig Update() method is very simple, we simply add the resulting Vector the the Block’s Position.



   1: protected override bool Update()
   2: {
   3:     JiggedBlockReference.Position = JiggedBlockReference.Position.Add(currentVector);
   4:     return true;
   5: }

That’s it for positioning. The currentVector variable is a global variable, set in the Sampler() Method. It just holds the Vector from the current Block’s Position and the registered mouse position in Sampler(). This is also how I did it in my last post on post on jigging multiple entities.


Now, for the BlockPropertyDragJig, where we will be updating the block properties, we need to fill in the Update() as follows:



   1: protected override bool Update()
   2: {
   3:  
   4:     // only 2d
   5:     Point2d oldPosition = new Point2d(CurrentPosition.X, CurrentPosition.Y);
   6:     Point2d newPosition = new Point2d(lastMousePosition.X, lastMousePosition.Y);
   7:  
   8:     JiggedBlockReference.Rotation = oldPosition.GetVectorTo(newPosition).Angle - Math.PI/2;
   9:  
  10:     // loop properties to find the ones we need
  11:     foreach (DynamicBlockReferenceProperty prop in JiggedBlockReference.DynamicBlockReferencePropertyCollection)
  12:     {
  13:         if (prop.PropertyName.ToUpper() == "LENGTH")
  14:         {
  15:             prop.Value = oldPosition.GetDistanceTo(newPosition);
  16:         }
  17:     }
  18:         
  19:     return true;
  20: }


Basically, we’re calculating 2 things here: the distance and the angle from the mouse position to the Block’s position (which is his Insertion Point).


The angle is controlling the rotation of the Block. The distance is controlling the Length property.


So that’s all there is to it. Full code on Google Code.


One more thing, I personally find working with Blocks in the AutoCad API very inefficient and cumbersome. I can only suggest that you create a Wrapper for your Blocks, and service classes that provide all the needed functionality for you. If you’ll decide to create all your Block-Handling code on-the-fly, you’ll most likely have a very error-prone process and lots of duplicate code.

Posted by Bert Vanpeteghem 4 reacties

Jigging multiple entities with the DrawJig

Today I’m giving a simple example of how to use a Jig on multiple entities.

There are 2 kinds of jigs (as far as I know, maybe there are new types in the newer API’s):

  • EntityJig
  • DrawJig

The EntityJig only allows us to jig one entity at the time, so the type we need here is the DrawJig.

This example is very simple, and by no means exploits full potential of jigging, but it might be something to get you going.

We create a command called ccnSimpleJig (ccn for Cup of Code .Net). The point of this command is to select some polylines, and then use a Jig on them to achieve a similar affect to te MOVE command.

We start by creating the command:

   1: [CommandMethod("ccnSimpleJig")]
   2: public void SimpleGeometryJig()
   3: {
   4:     Document doc = Application.DocumentManager.MdiActiveDocument;
   5:     Editor ed = doc.Editor;
   6:  
   7:     using (Transaction t = doc.TransactionManager.StartTransaction())
   8:     {
   9: ...

All goes nicely in a transaction. I’m not using a nice Try Catch Finally pattern, because the point here isthe jig.


Next, we select some entities and filter out the Polylines:



   1: // select some polylines
   2: PromptSelectionOptions promptSelection = new PromptSelectionOptions();
   3: PromptSelectionResult result = ed.GetSelection(promptSelection);
   4:  
   5: if (result.Status != PromptStatus.OK)
   6:     return;
   7:  
   8: // Iterate results to find polylines (I know filters are smarter..)
   9: List<Polyline> polylines = new List<Polyline>();
  10:  
  11: foreach (ObjectId oid in result.Value.GetObjectIds())
  12: {
  13:     DBObject ent = t.GetObject(oid, OpenMode.ForWrite);
  14:  
  15:     Polyline p = ent as Polyline;
  16:  
  17:     if (p == null)
  18:         continue;
  19:  
  20:     polylines.Add(p);
  21:  
  22: }

Then we prompt for a base or reference point:



   1: // prompt refernce point
   2: PromptPointOptions promptPoint = new PromptPointOptions("select reference point");
   3: PromptPointResult promptPointResult = ed.GetPoint(promptPoint);
   4:  
   5: if (promptPointResult.Status != PromptStatus.OK)
   6:     return;


Now that we have all this, we can start the Jig, and don’t forget to commit the transaction.



   1: // Jig It.
   2: SimpleGeometryJig jig = new SimpleGeometryJig(polylines, promptPointResult.Value);
   3: PromptResult res = ed.Drag(jig);
   4:  
   5: // Commit transaction to commit the moves we did.
   6: t.Commit();

The Jig itself is a class inheriting from the AutoCAD DrawJig class.
When inheriting the DrawJig, you must override (CTRL+ALT+F10 is your friend) 2 Methods: Sampler and WorldDraw.
Sampler simply tells the Jig to sample the needed data, and check if anything needs a redraw.
In WorldDraw, you can then update the entities so that the changes detected in the Sampler method are shown on the screen.


But, first things first, our SimpleGeometryJig Class. In the constructor of this class, we take in a collection of Polylines and a reference point (wich we specified in the command):



   1: private IList<Polyline> polylines;
   2: private Point3d currentPosition;
   3: private Vector2d currentVector;
   4:  
   5: public SimpleGeometryJig(IList<Polyline> polylines, Point3d referencePoint)
   6: {
   7:     this.polylines = polylines;
   8:  
   9:     // use first point in polyline collection as reference point
  10:     currentPosition = referencePoint;
  11:  
  12:     // init current vector as 0,0,0
  13:     currentVector = new Vector2d(0, 0);
  14:     
  15: }


Notice that we also use a global Vector2d. This is for tracking the movements, and makes it easy to update the polylines.


So, now our Sample method. Here we are looking for the current mouse position, and we will remember the difference of the current position with the new position. We are also returning Sampler States matching the outcome of the current Prompt (NoChange, OK, Cancel)



   1: protected override SamplerStatus Sampler(JigPrompts prompts)
   2: {
   3:     JigPromptPointOptions jigOpt = new JigPromptPointOptions("select insertion point");
   4:     jigOpt.UserInputControls = UserInputControls.Accept3dCoordinates;
   5:  
   6:  
   7:     PromptPointResult res = prompts.AcquirePoint(jigOpt);
   8:  
   9:     if (res.Status != PromptStatus.OK)
  10:         return SamplerStatus.Cancel;
  11:  
  12:     // compare points
  13:     if (res.Value.IsEqualTo(currentPosition, new Tolerance(0.1, 0.1)))
  14:         return SamplerStatus.NoChange;
  15:  
  16:     // get vector to current position
  17:     Vector3d v3d = currentPosition.GetVectorTo(res.Value);
  18:     currentVector = new Vector2d(v3d.X, v3d.Y);
  19:         
  20:  
  21:     // reset current position
  22:     currentPosition = res.Value;
  23:     
  24:  
  25:     return SamplerStatus.OK;
  26: }


In the WorldDraw function, we can now edit the polylines with the calculated vector and draw these changes to the screen.



   1: protected override bool WorldDraw(Autodesk.AutoCAD.GraphicsInterface.WorldDraw draw)
   2: {
   3:     try
   4:     {
   5:  
   6:         // add vector to all points of all polylines
   7:         foreach (var pl in polylines)
   8:         {
   9:             for (int i = 0; i < pl.NumberOfVertices; i++)
  10:             {
  11:                 // add vector to point
  12:                 pl.SetPointAt(i, pl.GetPoint2dAt(i).Add(currentVector));
  13:             }
  14:  
  15:             draw.Geometry.Draw(pl);
  16:  
  17:         }
  18:  
  19:         
  20:  
  21:         
  22:  
  23:     }
  24:     catch (System.Exception)
  25:     {
  26:         return false;
  27:     }
  28:  
  29:     return true;
  30: }

Voila, Jig is finished.
This is all the code we need, and we have a simple Jig that can move selected entities around.
For more useful and interesting jigs, you should take a look at Kean's site, filtered on the jig-category


Oh, and the code for this article can be found on my samples (first sample for now) page on Google Code. (More specifcally under simpledrawjig)

Posted by Bert Vanpeteghem 5 reacties