I’m in Las Vegas, running between meetings, presentations and performance reviews, so just a quick post, today (it’s just before midnight here, but already Monday morning in Europe).
1 using Autodesk.AutoCAD.ApplicationServices;
2 using Autodesk.AutoCAD.DatabaseServices;
3 using Autodesk.AutoCAD.EditorInput;
4 using Autodesk.AutoCAD.Geometry;
5 using Autodesk.AutoCAD.Runtime;
6 using Autodesk.AutoCAD.Windows;
7 using System.Runtime.InteropServices;
8 using System.Windows.Forms;
9 using System.Drawing;
10 using System.IO;
11 using System;
12
13 namespace DragAndDrop
14 {
15 public enum Msgs
16 {
17 WM_DRAWCLIPBOARD = 0x0308,
18 WM_CHANGECBCHAIN = 0x030D
19 }
20
21 public class ClipboardView : UserControl
22 {
23 [DllImport("user32.dll")]
24 public static extern IntPtr SetClipboardViewer(
25 IntPtr hWndNewViewer
26 );
27
28 [DllImport("user32.dll", CharSet = CharSet.Auto)]
29 public static extern IntPtr SendMessage(
30 IntPtr hWnd, int Msg, IntPtr wParam, IntPtr lParam
31 );
32
33 private IntPtr _nxtVwr;
34 private PictureBox _img;
35 private PaletteSet _ps;
36
37 public System.Drawing.Image Contents
38 {
39 get { return _img.Image; }
40 }
41
42 public ClipboardView(PaletteSet ps)
43 {
44 _img = new PictureBox();
45 _img.Anchor =
46 (AnchorStyles)(AnchorStyles.Top |
47 AnchorStyles.Bottom |
48 AnchorStyles.Left |
49 AnchorStyles.Right);
50 _img.Location = new Point(0, 0);
51 _img.Size = this.Size;
52 _img.SizeMode = PictureBoxSizeMode.StretchImage;
53 Controls.Add(_img);
54
55 // Add our event handler to launch a new drag event
56 // when someone clicks on the control
57
58 _img.MouseDown +=
59 delegate(object sender, MouseEventArgs e)
60 {
61 // Simply initiate the drag & drop in AutoCAD,
62 // specifying an instance of our custom "drop
63 // target" class
64
65 Autodesk.AutoCAD.ApplicationServices.
66 Application.DoDragDrop(
67 _img, this.Contents, DragDropEffects.Copy,
68 new MyDropTarget()
69 );
70 };
71
72 _nxtVwr = SetClipboardViewer(this.Handle);
73 _ps = ps;
74 }
75
76 private void ExtractImage()
77 {
78 IDataObject iData;
79 try
80 {
81 iData = Clipboard.GetDataObject();
82 }
83 catch (System.Exception ex)
84 {
85 MessageBox.Show(ex.ToString());
86 return;
87 }
88
89 if (iData.GetDataPresent("Bitmap"))
90 {
91 object o = iData.GetData("Bitmap");
92 Bitmap b = o as Bitmap;
93 if (b != null)
94 {
95 _img.Image = b;
96 if (_ps != null)
97 {
98 _ps.Size =
99 new Size(b.Size.Width / 3, b.Size.Height / 3);
100 }
101 }
102 }
103 }
104
105 protected override void WndProc(ref Message m)
106 {
107 switch ((Msgs)m.Msg)
108 {
109 case Msgs.WM_DRAWCLIPBOARD:
110 ExtractImage();
111 SendMessage(_nxtVwr, m.Msg, m.WParam, m.LParam);
112 break;
113 case Msgs.WM_CHANGECBCHAIN:
114 if (m.WParam == _nxtVwr)
115 _nxtVwr = m.LParam;
116 else
117 SendMessage(_nxtVwr, m.Msg, m.WParam, m.LParam);
118 break;
119 default:
120 base.WndProc(ref m);
121 break;
122 }
123 }
124 }
125
126 // Our custom drop target class
127
128 public class MyDropTarget : DropTarget
129 {
130 public override void OnDrop(DragEventArgs e)
131 {
132 Document doc =
133 Autodesk.AutoCAD.ApplicationServices.Application.
134 DocumentManager.MdiActiveDocument;
135
136 // If we have a valid bitmap, save it and send
137 // our custom command (RINS)
138
139 if (e.Data.GetDataPresent("Bitmap"))
140 {
141 object o = e.Data.GetData("Bitmap");
142 Bitmap b = o as Bitmap;
143 if (b != null)
144 {
145 // We'll save the bitmap as a "temp" file
146 // (as its unique - a better approach
147 // would probably to create a unique file
148 // in the same folder as the drawing, but
149 // that's left to the reader)
150
151 string path = Path.GetTempFileName();
152 b.Save(path);
153
154 // Call our command
155
156 string cmd =
157 String.Format(
158 "_RINS {0}\n{1},{2},0\n", path, e.X, e.Y
159 );
160 doc.SendStringToExecute(
161 cmd, false, false, false
162 );
163 }
164 }
165 }
166 }
167
168 public class Commands
169 {
170 private PaletteSet _ps = null;
171 private ClipboardView _cv = null;
172 static private Point3d _pt = Point3d.Origin;
173
174 [CommandMethod("DRAGDROP")]
175 public void DragAndDropClipboardRaster()
176 {
177 if (_ps == null)
178 {
179 _ps = new PaletteSet(
180 "DRAGDROP",
181 new Guid("5C8FC28C-45ED-4796-BD40-28D235B6D7DA")
182 );
183
184 if (_cv == null)
185 {
186 _cv = new ClipboardView(_ps);
187 }
188
189 _ps.Text = "Clipboard";
190 _ps.DockEnabled =
191 DockSides.Left | DockSides.Right | DockSides.None;
192 _ps.Size = new System.Drawing.Size(300, 500);
193 _ps.Add("ClipboardView", _cv);
194 }
195 _ps.Visible = true;
196 }
197
198
199 // Our custom command to insert a raster image
200
201 [CommandMethod("RINS")]
202 public void RasterInsert()
203 {
204 Document doc =
205 Autodesk.AutoCAD.ApplicationServices.Application.
206 DocumentManager.MdiActiveDocument;
207 Database db = doc.Database;
208 Editor ed = doc.Editor;
209
210 // Pick up the location of the bitmap image
211
212 PromptStringOptions pso =
213 new PromptStringOptions(
214 "\nPath of raster image: "
215 );
216 pso.AllowSpaces = true;
217 PromptResult pr = ed.GetString(pso);
218 if (pr.Status != PromptStatus.OK)
219 return;
220
221 if (!File.Exists(pr.StringResult))
222 {
223 ed.WriteMessage(
224 "\nFile does not exist."
225 );
226 return;
227 }
228
229 string path = pr.StringResult;
230
231 // Pick up the position of the drop in screen coords
232
233 PromptPointOptions ppo =
234 new PromptPointOptions(
235 "\nPosition of raster image: "
236 );
237 PromptPointResult ppr = ed.GetPoint(ppo);
238 if (pr.Status != PromptStatus.OK)
239 return;
240
241 Point3d pos = ppr.Value;
242
243 // Calculate the displacement vector
244
245 Point3d pt =
246 ed.PointToWorld(new Point((int)pos.X, (int)pos.Y));
247 Matrix3d disp =
248 Matrix3d.Displacement(pt - Point3d.Origin);
249
250 Transaction tr =
251 db.TransactionManager.StartTransaction();
252 using (tr)
253 {
254 // Get or create our raster image dictionary
255
256 ObjectId dictId =
257 RasterImageDef.GetImageDictionary(db);
258 if (dictId == ObjectId.Null)
259 dictId = RasterImageDef.CreateImageDictionary(db);
260
261 // And open it for write
262
263 DBDictionary dict =
264 (DBDictionary)tr.GetObject(
265 dictId, OpenMode.ForWrite
266 );
267
268 // Get a unique name for our definition object
269
270 string name = RasterImageDef.SuggestName(dict, path);
271
272 // Create the definition and add it to the dictionary
273
274 RasterImageDef rid = new RasterImageDef();
275 rid.SourceFileName = path;
276 rid.Load();
277 ObjectId ridId = dict.SetAt(name, rid);
278 tr.AddNewlyCreatedDBObject(rid, true);
279
280 // Now we'll add our raster image reference
281 // to the current space
282
283 BlockTableRecord btr =
284 (BlockTableRecord)tr.GetObject(
285 db.CurrentSpaceId,
286 OpenMode.ForWrite
287 );
288
289 // Call our jig to define the raster
290
291 RasterJig jig =
292 new RasterJig(
293 ridId,
294 rid.Size,
295 ed.CurrentUserCoordinateSystem,
296 pt
297 );
298 PromptResult prj = ed.Drag(jig);
299
300 if (prj.Status != PromptStatus.OK)
301 {
302 rid.Erase();
303 return;
304 }
305
306 // Get our entity and add it to the modelspace
307
308 RasterImage ri = (RasterImage)jig.GetEntity();
309 ri.ImageDefId = ridId;
310 ri.SetClipBoundaryToWholeImage();
311 btr.AppendEntity(ri);
312 tr.AddNewlyCreatedDBObject(ri, true);
313
314 // Create a reactor between the RasterImage and the
315 // RasterImageDef to avoid the "unreferenced"
316 // warning in the XRef palette
317
318 RasterImage.EnableReactors(true);
319 ri.AssociateRasterDef(rid);
320
321 // Of course we commit
322
323 tr.Commit();
324 }
325 }
326 }
327
328 class RasterJig : EntityJig
329 {
330 Matrix3d _ucs;
331 Vector2d _sz;
332 Point3d _start = Point3d.Origin;
333 Point3d _end = Point3d.Origin;
334
335 public RasterJig(
336 ObjectId defId,
337 Vector2d sz,
338 Matrix3d ucs,
339 Point3d start
340 )
341 : base(new RasterImage())
342 {
343 _sz = sz;
344 _start = start;
345 _ucs = ucs;
346
347 RasterImage ri = (RasterImage)Entity;
348 ri.ImageDefId = defId;
349
350 // Create a near zero size default image,
351 // to avoid the boundary flicker
352
353 double size = Tolerance.Global.EqualPoint;
354 ri.Orientation =
355 new CoordinateSystem3d(
356 _start,
357 new Vector3d(size, 0, 0),
358 new Vector3d(0, size, 0)
359 );
360 ri.ShowImage = true;
361 }
362
363 protected override SamplerStatus Sampler(
364 JigPrompts prompts
365 )
366 {
367 JigPromptPointOptions opts =
368 new JigPromptPointOptions();
369 opts.UserInputControls =
370 (UserInputControls.Accept3dCoordinates |
371 UserInputControls.NoNegativeResponseAccepted);
372 opts.Message = "\nSecond corner of raster image: ";
373
374 // Get the point itself
375
376 PromptPointResult res = prompts.AcquirePoint(opts);
377
378 if (res.Status == PromptStatus.OK)
379 {
380 // Convert the supplied point into UCS
381
382 Point3d tmp =
383 res.Value.TransformBy(_ucs.Inverse());
384
385 // Check if changed (reduces flicker)
386
387 if (_end == tmp)
388 {
389 return SamplerStatus.NoChange;
390 }
391 else
392 {
393 _end = tmp;
394 return SamplerStatus.OK;
395 }
396 }
397 return SamplerStatus.Cancel;
398 }
399
400 protected override bool Update()
401 {
402 RasterImage ri = (RasterImage)Entity;
403
404 // Get offset between the two corners
405
406 Vector3d diff = _end - _start;
407
408 // Get the ratios of the dimensions of the dragged
409 // image with the source
410
411 double sizeX = Math.Abs(diff.X),
412 sizeY = Math.Abs(diff.Y),
413 ratX = sizeX / _sz.X,
414 ratY = sizeY / _sz.Y;
415
416 // Find out which is bigger and use that to decide
417 // which size should change to keep the proportion
418
419 bool biggerX = (ratX > ratY);
420
421 if (biggerX)
422 sizeY = ratX * _sz.Y;
423 else
424 sizeX = ratY * _sz.X;
425
426 // Determine the image's orientation...
427
428 // The original will depend on the order of the corners
429 // It will be offset to the left and/or down depending
430 // on the values of the vector between the two points
431
432 Point3d orig;
433
434 // The axes stay the same, as we will always keep the
435 // image oriented the same way relative to the UCS
436
437 Vector3d xAxis = new Vector3d(sizeX, 0, 0);
438 Vector3d yAxis = new Vector3d(0, sizeY, 0);
439
440 if (diff.X > 0 && diff.Y > 0) // Dragging top-right
441 orig = _start;
442 else if (diff.X < 0 && diff.Y > 0) // Top-left
443 orig = _start + new Vector3d(-sizeX, 0, 0);
444 else if (diff.X > 0 && diff.Y < 0) // Bottom-right
445 orig = _start + new Vector3d(0, -sizeY, 0);
446 else // if (diff.X < 0 && diff.Y < 0) // Bottom-left
447 orig = _start - new Vector3d(sizeX, sizeY, 0);
448
449 // Set the image's orientation in WCS
450
451 ri.Orientation =
452 new CoordinateSystem3d(
453 orig.TransformBy(_ucs),
454 xAxis.TransformBy(_ucs),
455 yAxis.TransformBy(_ucs)
456 );
457
458 return true;
459 }
460
461 public Entity GetEntity()
462 {
463 return Entity;
464 }
465 }
466 }
When we run the new DRAGDROP command and drag & drop from our palette onto the drawing, we see the raster images are now inserted and sized by the jig proportionally to the original image’s size: