December 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 31      










« Autodesk ReCap Photo | Main | Autodesk Exchange Apps webinar »

May 27, 2013

Fixing block draw-order in AutoCAD drawings exported by SmartSketch using .NET

Here’s a question I received recently from Dustin Vest, who works as a PDMS administrator at Fluor:

I am having a problem with Intergraph’s SmartSketch dwg files it exports... I found some code on your site that got me into the blocks, but I can’t seem to change the draworder of entities within the block. SmartPlant 3D exports all the symbols as blocks but with wipeouts on top of the rest of the block.

Dustin provided some code that was very nearly working – it really didn’t need much work to get it in shape – and has kindly allowed me to share it, here.

The below C# code defines a WTB command (for “WipeoutsToBottom” – it’s not a new Internet swearcronym ;-) that moves any wipeouts to the bottom of the selected block (or of all blocks in the drawing if the user hits Enter).

using Autodesk.AutoCAD.ApplicationServices;

using Autodesk.AutoCAD.DatabaseServices;

using Autodesk.AutoCAD.EditorInput;

using Autodesk.AutoCAD.Runtime;

 

namespace BlockFixer

{

  public class Commands

  {

    [CommandMethod("WTB")]

    public static void WipeoutsToBottom()

    {

      var doc = Application.DocumentManager.MdiActiveDocument;

      var ed = doc.Editor;

      var db = doc.Database;

 

      try

      {

        // Ask the user to select a block or None for "all"

 

        var peo =

          new PromptEntityOptions("\nSelect block to fix <all>");

        peo.SetRejectMessage("Must be a block.");

        peo.AddAllowedClass(typeof(BlockReference), false);

        peo.AllowNone = true;

 

        var per = ed.GetEntity(peo);

 

        if (

          per.Status != PromptStatus.OK &&

          per.Status != PromptStatus.None

        )

          return;

 

        // If the user hit enter, run on all blocks in the drawing

 

        bool allBlocks = per.Status == PromptStatus.None;

 

        using (var tr = db.TransactionManager.StartTransaction())

        {

          var toProcess = new ObjectIdCollection();

          if (allBlocks)

          {

            var bt =

              (BlockTable)tr.GetObject(

                db.BlockTableId, OpenMode.ForRead

              );

 

            // Collect all the non-layout blocks in the drawing

 

            foreach (ObjectId btrId in bt)

            {

              var btr =

                (BlockTableRecord)tr.GetObject(

                  btrId, OpenMode.ForRead

                );

              if (!btr.IsLayout)

              {

                toProcess.Add(btrId);

              }

            }

          }

          else

          {

            // A specific block was selected, let's open it

 

            var brId = per.ObjectId;

            var br =

              (BlockReference)tr.GetObject(brId, OpenMode.ForRead);

 

            // Collect the ID of its underlying block definition

 

            toProcess.Add(br.BlockTableRecord);

          }

 

          var brIds = MoveWipeoutsToBottom(tr, toProcess);

          var count = toProcess.Count;

 

          // Open each of the returned block references and

          // request that they be redrawn

 

          foreach (ObjectId brId in brIds)

          {

            var br =

              (BlockReference)tr.GetObject(brId, OpenMode.ForWrite);

 

            // We want to redraw a specific block, so let's modify a

            // property on the selected block reference

 

            // We might also have called this method:

            // br.RecordGraphicsModified(true);

            // but setting a property works better with undo

 

            br.Visible = br.Visible;

          }

 

          // Report the number of blocks modified (after

          // being filtered by MoveWipeoutsToBottom())

 

          ed.WriteMessage(

            "\nModified {0} block definition{1}.",

            count, count == 1 ? "" : "s"

          );

 

          // Commit the transaction

 

          tr.Commit();

        }

      }

      catch (Autodesk.AutoCAD.Runtime.Exception e)

      {

        doc.Editor.WriteMessage(

        "\nException: {0}", e.Message

        );

      }

    }

 

    // Move the wipeouts to the bottom of the specified

    // block definitions

 

    private static ObjectIdCollection MoveWipeoutsToBottom(

      Transaction tr, ObjectIdCollection ids

    )

    {

      // The IDs of any block references we find

      // to return to the call for updating

 

      var brIds = new ObjectIdCollection();

 

      // We only need to get this once

 

      var wc = RXClass.GetClass(typeof(Wipeout));

 

      // Take a copy of the IDs passed in, as we'll modify the

      // original list for the caller to use

 

      var btrIds = new ObjectId[ids.Count];

      ids.CopyTo(btrIds, 0);

 

      // Loop through the blocks passed in, opening each one

 

      foreach (var btrId in btrIds)

      {

        var btr =

          (BlockTableRecord)tr.GetObject(

            btrId, OpenMode.ForWrite

          );

 

        // Collect the wipeouts in the block

 

        var wipeouts = new ObjectIdCollection();

        foreach (ObjectId id in btr)

        {

          var ent = (Entity)tr.GetObject(id, OpenMode.ForRead);

          if (ent.GetRXClass().IsDerivedFrom(wc))

          {

            wipeouts.Add(id);

          }

        }

 

        // Move the collected wipeouts to the bottom

 

        if (wipeouts.Count > 0)

        {

          // Modify the draw order table, if we have wipepouts

 

          var dot =

            (DrawOrderTable)tr.GetObject(

              btr.DrawOrderTableId, OpenMode.ForWrite

            );

          dot.MoveToBottom(wipeouts);

 

          // Collect the block references to this block, to pass

          // back to the calling function for updating

 

          var btrBrIds = btr.GetBlockReferenceIds(false, false);

          foreach (ObjectId btrBrId in btrBrIds)

          {

            brIds.Add(btrBrId);

          }

        }

        else

        {

          ids.Remove(btrId);

        }

      }

      return brIds;

    }

  }

}

The code is a bit longer than I had originally expected as I decided that, rather than relying on Editor.Regen() to reflect the changes to the drawing, it would be cleaner to modify each block reference in turn in order to force their graphics to be refreshed.

We’re not actually making a change to each block reference, but it’s not enough to just open them for write. The classic approach, here, would be to call RecordGraphicsModified(true) on each object, but I’ve actually found that making a property modification (even if to the same value) leads to undo working better – it forces a redraw even on undo. So I’ve chosen to set the Visible property of each block reference to the current value of the Visible property – I’m sure you’ll agree that’s as innocuous a modification as you can get. :-)

The function that checks each block for wipeouts and then modifies the draw order now collects the block references to these blocks and returns them to the caller. It’s clear that calling Regen() would have been simpler (i.e. taking fewer lines of code), but it would have exhibited similar behaviour to calling RecordGraphicsModified() with respect to undo. So while it takes more code, this approach is ultimately cleaner and works better, too.

Dustin has tried the application and tells me it works well for him – and it works well on the drawings Dustin provided for me to test with – but, beyond that, as I don’t personally use SmartSketch I have to take his word for it. If any other SmartSketch users who’ve hit this particular problem are able to give this a test, it’d be great to get some feedback in the comments.

blog comments powered by Disqus

Feed/Share

10 Random Posts