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        










« Updated versions of the AutoCAD .NET Wizard & Labs | Main | Searching the web from inside AutoCAD using the Bing API and .NET »

September 16, 2009

Jigging an AutoCAD solid using IronRuby and .NET (yes, finally)

Back in April I posted an IronRuby sample that I had hoped would cause AutoCAD to jig a box in 3D, just like its IronPython counterpart.

The sample didn’t work with IronRuby 0.3, but recently David Blackmon got in touch to let me know he had a version of the code working with IronRuby 0.9. Now that I’ve started preparing for my upcoming AU class, AutoCAD® .NET: Developing for AutoCAD® Using IronPython and IronRuby, I decided to take a closer look at the update to IronRuby and more specifically at the changes to the code David made to get it to work.

David has been having some real fun with IronRuby and AutoCAD: for those interested, I strongly recommend checking out the work he’s posted on github. David claims to have been inspired by my previous IronRuby posts to develop some helper classes for AutoCAD development and the results are really interesting: having someone passionate about Ruby take a look at this is great, as it demonstrates to the rest of us some of the possibilities.

I haven’t used these helpers in the below code – which only borrows the tricks David used to get the jig to work – but that’s more for consistency with my previous post than for any other reason.

Here’s the updated IronRuby code, with the modified/additional lines marked in red (and the full source file can be downloaded from here):

    1 require 'acmgd.dll'

    2 require 'acdbmgd.dll'

    3 

    4 Ai =  Autodesk::AutoCAD::Internal

    5 Aiu = Autodesk::AutoCAD::Internal::Utils

    6 Aas = Autodesk::AutoCAD::ApplicationServices

    7 Ads = Autodesk::AutoCAD::DatabaseServices

    8 Aei = Autodesk::AutoCAD::EditorInput

    9 Ag =  Autodesk::AutoCAD::Geometry

   10 Ar =  Autodesk::AutoCAD::Runtime

   11 

   12 def print_message(msg)

   13   app = Aas::Application

   14   doc = app.DocumentManager.MdiActiveDocument

   15   ed = doc.Editor

   16   ed.WriteMessage(msg)

   17 end

   18 

   19 # Function to register AutoCAD commands

   20 

   21 def autocad_command(cmd) 

   22   cc = Ai::CommandCallback.new method(cmd)

   23   Aiu.AddCommand(

   24     'rbcmds', cmd, cmd, Ar::CommandFlags.Modal, cc)

   25 

   26   # Let's now write a message to the command-line

   27 

   28   print_message("\nRegistered Ruby command: " + cmd)

   29 end

   30 

   31 def add_commands(names)

   32   names.each { |n| autocad_command n }

   33 end

   34 

   35 # Let's do something a little more complex...

   36 

   37 class SolidJig < Aei::EntityJig

   38 

   39   # Constructor

   40 

   41   def SolidJig.new(ent)

   42     super

   43   end

   44 

   45   # The function called to run the jig

   46 

   47   def start_jig(ed, pt)

   48 

   49     # The start point is specified outside the jig

   50 

   51     @start = pt

   52     @end = pt

   53     @sol = self.Entity

   54 

   55     return ed.Drag(self)

   56   end

   57 

   58   # The sampler function

   59 

   60   def sampler(prompts)

   61 

   62     # Set up our selection options

   63 

   64     jo = Aei::JigPromptPointOptions.new

   65     jo.UserInputControls = (

   66    Aei::UserInputControls.Accept3dCoordinates |

   67    Aei::UserInputControls.NoZeroResponseAccepted |

   68    Aei::UserInputControls.NoNegativeResponseAccepted)

   69     jo.Message = "\nSelect end point: "  

   70 

   71     # Get the end point of our box

   72 

   73     res = prompts.AcquirePoint(jo)

   74 

   75     if @end == res.Value

   76    return Aei::SamplerStatus.NoChange

   77     else

   78    @end = res.Value

   79     end

   80 

   81     return Aei::SamplerStatus.OK

   82   end

   83 

   84   # The update function

   85 

   86   def update()

   87 

   88     # Recreate our Solid3d box

   89 

   90     begin

   91 

   92    # Get the width (x) and depth (y)

   93 

   94    x = @end.X - @start.X

   95    y = @end.Y - @start.Y

   96 

   97    # We need a non-zero Z value, so we copy Y

   98 

   99    z = y

  100 

  101    # Create our box and move it to the right place

  102 

  103    if x.abs > 0 and y.abs > 0

  104      @sol.CreateBox(x.abs,y.abs,z.abs)    

  105      @sol.TransformBy(

  106    Ag::Matrix3d.Displacement(

  107      Ag::Vector3d.new(

  108    @start.X + x/2,

  109    @start.Y + y/2,

  110    @start.Z + z/2)))

  111    end

  112     rescue

  113    return false

  114     end 

  115     return true

  116   end

  117 end

  118 

  119 # Create a box using a jig

  120 

  121 def boxjig

  122 

  123   app = Aas::Application

  124   doc = app.DocumentManager.MdiActiveDocument

  125   db = doc.Database

  126   ed = doc.Editor

  127 

  128   # Select the start point before entering the jig

  129 

  130   ppr = ed.GetPoint("\nSelect start point: ")

  131 

  132   if ppr.Status == Aei::PromptStatus.OK

  133 

  134     # We'll add our solid to the modelspace

  135 

  136     tr = doc.TransactionManager.StartTransaction

  137     bt =

  138    tr.GetObject(

  139      db.BlockTableId,

  140      Ads::OpenMode.ForRead)

  141     btr =

  142    tr.GetObject(db.CurrentSpaceId,Ads::OpenMode.ForWrite)

  143 

  144     # Make sure we're recording history to allow grip editing

  145 

  146     sol = Ads::Solid3d.new

  147     sol.RecordHistory = true

  148 

  149     # Now we add our solid

  150 

  151     btr.AppendEntity(sol)

  152     tr.AddNewlyCreatedDBObject(sol, true)

  153 

  154     # And call the jig before finishing

  155 

  156     begin

  157 

  158    sj = SolidJig.new sol

  159 

  160    ppr2 = sj.start_jig(ed, ppr.Value)

  161 

  162    # Only commit if all completed well

  163 

  164    if ppr2.Status == Aei::PromptStatus.OK

  165      tr.Commit

  166    end

  167 

  168     rescue

  169 

  170    print_message("\nProblem found: " + $! + "\n")

  171 

  172     end

  173 

  174     tr.Dispose

  175   end

  176 end

  177 

  178 add_commands ["boxjig"]

Let’s look at the specific changes:

  • Lines 1-2 just removes the paths, to make the code more portable. I've also removed the load of acmgdinternal.dll, as this is no longer a separate assembly with AutoCAD 2010.
  • Lines 39 and 41 turn my prior initialization attempt into a constructor. Line 53 sets our "@sol" member to be the entity passed into the constructor, although this code has to be in start_jig to work properly, for some reason – it can’t be in the constructor itself.
  • Lines 47, 60, 86 & 160 are make the code more Rubyesque (as mentioned in my last IronRuby post, Ruby functions should  really be named with the lowercase_and_delimited convention).
  • Lines 103-4 and 111 allow a box to be created even when the cursor crosses the start point (either to its left or below it, which would previously have thrown an exception as we create a box with zero/negative height/width/depth).

To load the .rb file, I simply used the RBLOAD command implemented by the C# loader application shown in previous IronRuby posts.

Now let’s run the BOXJIG command. After selecting a start point, we drag off to the top-right and see our box changing size (displayed using the 3D hidden visual style, in this case):

Start of drag

As we drag to the bottom left of our original point and select a point, we see our box created in that direction:

End of drag

In my next post I plan on taking the lid off some of David’s AutoCAD helper classes, as well as showing some of IronPython’s debugging capabilities when integrated with Visual Studio 2008.

blog comments powered by Disqus

Feed/Share

10 Random Posts