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):
As we drag to the bottom left of our original point and select a point, we see our box created in that direction:
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.