TIRADE (The IronRuby Autocad Development Environment)

Here is a sneak-peek at TIRADE

TIRADE extends my work on acadhelper and allows instant (or at least very rapid) gratification and enjoyment of Ruby goodness when extending AutoCAD with IronRuby.

TIRADE runs modelessly, integrates RSpec and Test::Unit testing directly (slowly, but directly), has syntax highlighting and will soon have (hopeably) code completion for the acadhelper wrappers and methods.

I used the ICSharpCode TextEditor control (with some local modifications) as the editor control and used the open source DockPanel Suite that very closely matches the Visual Studio docking that we all know and love.

After a bit more testing, I hope to release TIRADE on the world. Stay tuned

IronRuby and RSpec with color

So I’m using Rspec to test some IronRuby code on my Vista box. I tried the —colour switch only to be told that I needed to install the win32console gem. No biggie right? I installed that gem and was told something about something about something, I don’t even remember it all and can’t find my notes now.

The thing that I do remember is that I still could not get color output from Rspec. So I did some googling (or is it googleing?) and found iron-term-ansicolor.rb in this github repo by hotgazpacho .

So I gave that a try. It worked well in ir, but still wouldn’t work with Rspec. hotgazpacho was opening Kernel#puts but it appeared that I needed to open IO#write to get console.exe to show the colored output. ( I use console.exe when I runs tests on my Vista box. It is great).

So, using the iron-term-ansicolor.rb code as a guide, I hacked together this code

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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
# somefile.rb
require 'mscorlib'


# add the trailing m  because some of the rspec output spans multiple lines
ANSI_REGEXP = /\e\[(.+?)m(.+?)(?=(\e\[0m|\z))/m
SsC=System::ConsoleColor

class IO
alias_method :original_write, :write

def write(*args)
   count = 0
   args.each do |arg|

   fg_color = System::Console.ForegroundColor
   bg_color = System::Console.BackgroundColor

  ##dkb
   if ANSI_REGEXP.match(arg)
     data = extract_ansi_data(arg)
   else
     data = {:text => arg}
   end 

   System::Console.ForegroundColor = data[:foreground] || fg_color
   System::Console.BackgroundColor = data[:background] || bg_color
   count = original_write(data[:text])
   System::Console.BackgroundColor = bg_color
   System::Console.ForegroundColor = fg_color
 end
 count
end

private
def extract_ansi_data(arg)
 fg_color_map =    Hash[30,SsC.Black,31,SsC.Red,32,SsC.DarkGreen ,
                             33,SsC.DarkYellow, 34, SsC.DarkBlue,35,SsC.DarkMagenta,
                              36,SsC.DarkCyan,37,SsC.Gray]
 bg_color_map = Hash[40,SsC.Black,41,SsC.DarkRed,42,SsC.DarkGreen,
                          43,SsC.DarkYellow, 44,SsC.DarkBlue, 45,SsC.DarkMagenta, 
                          46,SsC.DarkCyan,47,SsC.Gray]
 matches = ANSI_REGEXP.match(arg)
 fg_color = fg_color_map[matches[1].to_i] || System::Console.ForegroundColor
 bg_color = bg_color_map[matches[1].to_i] || System::Console.BackgroundColor
 { :foreground => fg_color, :background => bg_color,:text => matches[2]}
end

(Save this code somewhere in the IronRuby load path)

Then, I dove into the rspec code and in rspec-1.2.9\lib\spec\runner\options.rb I made this small change to the def colour= method (about line 191)

1
2
3
4
 ##comment out the line below
 #require 'Win32/Console/ANSI' ;\
 ## add this line
    require 'dkb-iron-term-ansicolor'  #or whatever you name the code above

With these 2 changes, I can use the -c option of Rspec to get the glorious green/red output when testing my IronRuby code. There may be a better, cleaner way, but I did a bit of searching and could not find it. This way seems to work, plus it gave me a good excuse to dig into the Rspec code.

BlockReference Attribute Handling

I am currently testing a method that simplifies the editing of block reference attributes.

Given a simple block reference with attributes Name, Sex, Age, and Height, wouldn’t it be nice to be able to
edit the attributes like:

1
2
attribs.age = 45
attribs.name = "David"

So I hacked together a little method that uses my TransHelper class and an OpenStruct to store the attribute tags and values

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
def get_block_ref(obj_id, mode=:Read)
    begin
        helper = TransHelper.new
        helper.trans([:Block])  do |tr, db, tables|
                ref = helper.get_obj obj_id, mode
                attrib_hash = {}
                ref.AttributeCollection.each do |id|
                        attrib = helper.get_obj id, :Read
                        attrib_hash[attrib.Tag.downcase] = attrib.TextString
                end
                @attribs = OpenStruct.new(attrib_hash)
                yield @attribs
                #update the attribs
                if mode == :Write
                        ref.AttributeCollection.each do |id|
                                attrib = helper.get_obj id, :Write
                                attrib.TextString = @attribs.send(attrib.Tag.downcase).to_s
                        end
                end        
            end    
        rescue Exception => e
                puts_ex e
        end
end

This little methods allows me to write an entire attribute editing method with very little coding. All that is really needed is some knowledge of the attribute tags (but surely we know the tag names if were are coding against the block, right?)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
def edit_block_ref
        begin
          ref_id = get_entity_id #from AcadHelper to pick an entity on the screen
          get_block_ref(ref_id, :Write) do |attribs|
            attribs.age = 50  #numbers can be used - get_block_ref will convert to string.  YEA!
            attribs.name = "Chuck"
            attribs.height = "72 inches"
            attribs.sex = "Male"
           end
        rescue Exception => e
          puts_ex e
        end
end

I’m still testing this, but feel free to take if for a test ride.

When I complete the testing, I will add the method to AcadHelper

Ruby and AutoCAD via IronRuby

Inspired by a blog post by Kean Walmsley here, I have been working on some wrapper and helper functions for driving AutoCAD using Ruby.

IronRuby and Kean’s loader function gives the AutoCAD developer full access to the managed API. But any that has coded against that API knows it can be verbose and lead to some not-so-readable code, like the C# code below.

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
31
32
33

[CommandMethod("AddLine")]
public static void AddLine()
{
// Get the current document and database

  Document acDoc = Application.DocumentManager.MdiActiveDocument;
  Database acCurDb = acDoc.Database;

  // Start a transaction
  using (Transaction acTrans = acCurDb.TransactionManager.StartTransaction())
  {
    // Open the Block table for read
    BlockTable acBlkTbl;
    acBlkTbl = acTrans.GetObject(acCurDb.BlockTableId, OpenMode.ForRead) as BlockTable;

      // Open the Block table record Model space for write
    BlockTableRecord acBlkTblRec;
    acBlkTblRec = acTrans.GetObject(acBlkTbl[BlockTableRecord.ModelSpace],
    OpenMode.ForWrite) as BlockTableRecord;

     // Create a line that starts at 5,5 and ends at 12,3
    Line acLine = new Line(new Point3d(5, 5, 0), new Point3d(12, 3, 0));
    acLine.SetDatabaseDefaults();

    // Add the new object to the block table record and the transaction
    acBlkTblRec.AppendEntity(acLine);
    acTrans.AddNewlyCreatedDBObject(acLine, true);
    // Save the new object to the database
    acTrans.Commit();
  }
}

In Ruby, using my AcadHelper module,that code becomes

1
2
3
4
5
6

def add_line
  line = create_line [1,1], [2,4,5]
  line.add_to_model_space
end

Add in a begin-rescue-end block and it still only 8 lines of, in my opinion, very readable code

1
2
3
4
5
6
7
8
9

def add_line
  begin
    line = create_line [1,1], [2,4,5]
    line.add_to_model_space
  rescue Exception => e
    puts_ex e
  end
end

I hope Ruby catches on as development option for AutoCAD.
As a long-time AutoLISP hacker, I love having an interpreted language to use for development.
Ruby offers that, along with full access to the API and the beauty and power of Ruby

I have posted an initial verision of my AcadHelper.rb on github.
And development continues. I will be updated everything as time allows. I have added auto-loading
of functions, create_text and create_mtext functions, Editor.GetEntity helper,
an exception and backtrace print helper.

Next on the list is selection sets. If anyone wants to help out or has ideas, please let me know

Pages

Github

Categories

There Must Be An Easier Way © David Blackmon. Valid XHTML and ATOM. Powered by Enki.