Author Topic: Move entities  (Read 227 times)

0 Members and 1 Guest are viewing this topic.

Eric

  • Mosquito
  • Posts: 4
Move entities
« on: November 25, 2017, 05:28:27 pm »
I'm stumped.
I'm working with RealDWG API C# and I can't seem to move all entities in ModelSpace along a vector.
I can change a layer, but I can't move entities.
Here the method. Any ideas?

public static void NormalizeSED(string blockPath)
{
    using (Database tmpDb = new Database(false, true))
    {
        tmpDb.ReadDwgFile(blockPath, System.IO.FileShare.ReadWrite, true, "");

      using (Transaction tmpTrans = (Transaction)tmpDb.TransactionManager.StartTransaction())
      {
         LayerTable layerTbl = (LayerTable)tmpTrans.GetObject(tmpDb.LayerTableId, OpenMode.ForRead);

         // Turn off the metric layer
         string sLayerName = "Metric";
         LayerTableRecord acLyrTblRec;
         if (layerTbl.Has(sLayerName))
         {
            acLyrTblRec = tmpTrans.GetObject(layerTbl[sLayerName], OpenMode.ForWrite) as LayerTableRecord;
            acLyrTblRec.IsOff = true;
         }
         
         var tempBt = (BlockTable)tmpTrans.GetObject(tmpDb.BlockTableId, OpenMode.ForRead);
            var tmpBtr = (BlockTableRecord)tmpTrans.GetObject(tempBt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
         
         Point3d delta = new Point3d(0, 0, 0);   
         Vector3d distanceToZero = zero.GetVectorTo(new Point3d(1.5, 1.5, 0));
         
         // Move all entities along the vector
         foreach (ObjectId entId in tmpBtr)
         {
            Entity ent = tmpTrans.GetObject(entId, OpenMode.ForWrite) as Entity;
            if (ent != null)
            {
               ent.TransformBy(Matrix3d.Displacement(distanceToZero));
            }
         }
         
         tmpTrans.Commit();
        }

        tmpDb.SaveAs(blockPath, DwgVersion.AC1024);   
   }
}


gile

  • Water Moccasin
  • Posts: 2095
  • Marseille, France
Re: Move entities
« Reply #1 on: November 25, 2017, 05:37:10 pm »
Hi,

There's something curious the code you show:

Point3d delta = new Point3d(0, 0, 0);   
Vector3d distanceToZero = zero.GetVectorTo(new Point3d(1.5, 1.5, 0));

Where does this zero come from?

If zero is equal to Point3d.Origin, you can simply build your vector like this:
Code - C#: [Select]
  1. Vector3d distanceToZero = new Vector3d(1.5, 1.5, 0);
Speaking English as a French Frog

Eric

  • Mosquito
  • Posts: 4
Re: Move entities
« Reply #2 on: November 25, 2017, 05:48:46 pm »
Here is what I'm doing. I'm trying to build a detail drawing with multiple details. The problem is that some of the details are not "standard". A standard detail should have the lowest left corner of the detail border to be at 0,0. This is what I left out of my example.

foreach (ObjectId entId in tmpBtr)
{
    Entity ent = tmpTrans.GetObject(entId, OpenMode.ForRead) as Entity;
    if (ent != null)
    {
         Polyline pLine = ent as Polyline;
         if (pLine != null && pLine.Layer.ToUpper() == "TITLELINES")
         {
             List<Point2d> points = new List<Point2d>();
             List<double> xVals = new List<double>();
             List<double> yVals = new List<double>();

             int vn = pLine.NumberOfVertices;
             for (int i = 0; i < vn; i++)
             {
                  var point = pLine.GetPoint2dAt(i);
                  points.Add(point);
                  xVals.Add(point.X);
                  yVals.Add(point.Y);
              }

              Point2d lowestLeft = points.FirstOrDefault(p => p.X == xVals.Min() && p.Y == yVals.Min());

              if (lowestLeft.X != 0 || lowestLeft.Y != 0)
              {
                  detailIsBad = true;
                  var zero = new Point3d(0, 0, 0);
                  distanceToZero = zero.GetVectorTo(new Point3d(lowestLeft.X, lowestLeft.Y, 0));
               }

                break;
          }
    }
}

if (detailIsBad)

// Here is where I try to transform

Eric

  • Mosquito
  • Posts: 4
Re: Move entities
« Reply #3 on: November 25, 2017, 05:50:52 pm »
Everything works. I get a vector, but when I open the drawing in AutoCAD, the metric layer is off, but nothing has moved to 0,0.

MickD

  • Gator
  • Posts: 2970
  • I don't need a job, I need Money!!
Re: Move entities
« Reply #4 on: November 25, 2017, 08:25:00 pm »
Hi Eric, welcome aboard!

First, a few comments on your code in general.
You really should refactor the 'detailIsBad' code out of your method, factor out anything else that shouldn't be there either. Having too much going on in a single method can do your head in as you can't logically isolate out any possible problems and you start looking over all your code over and over again (which I'm doing trying to review your code!).
When you have methods that only do one thing they are easy to both test and reason about and you can 'debug' them just by reading the few lines of code.

It's also good practice to call predicates or boolean values with the question first like 'isDetailBad' rather than 'detailIsBad'. If a method is doing something, again put the 'doing' word first like 'get' or 'set'.

Now, what is the effect of the 'break;' statement meant to achieve and is it doing the right thing or short-circuiting your overall method, it's hard to tell as you have cherry picked out parts of your code for examples.

Hope that helps and sorry to be nit-picky, it seems like more work to separate everything out but it saves time (mentally) in the long term, and it helps us to review your code. cheers.
"A language that doesn’t have everything is actually easier to program in than some that do."

        — Dennis M. Ritchie

kdub

  • SuperMod
  • Swamp Rat
  • Posts: 994
  • class keyThumper<T>:ILazy<T>
Re: Move entities
« Reply #5 on: November 25, 2017, 10:22:06 pm »

ThumbsUp();
called Kerry in my other life

Sometimes the question is more important than the answer.

MickD

  • Gator
  • Posts: 2970
  • I don't need a job, I need Money!!
Re: Move entities
« Reply #6 on: November 26, 2017, 06:26:34 pm »
I had a bit of spare time to re-write your code (what you have provided), this is how I would write it and how I think about the structure of what I'm doing.

I think of the CommandMethod as my recipe of what I want to do and it contains small helper functions to do each step. I use comments as the steps for a start then follow them with methods. These smaller methods do as little as possible and specialise in one thing only.

It can be hard to separate out things at times due to transactions and 'using's etc but it can be done even if at the expense of a few more transactions or some manual disposing of objects. If it makes you code easier to deal with it's worth a couple of extra clock cycles :)

hope it helps.

NOTE: Compiles but not tested
Code - C#: [Select]
  1. namespace MoveDetailSwamp
  2. {
  3.    public class Class1
  4.    {
  5.  
  6.        /// <summary>
  7.        /// Moves non-standard details into correct position(?)
  8.        /// </summary>
  9.        [CommandMethod("UPDATE_DETAILS")]
  10.        public static void UpdateDetails()
  11.        {
  12.            // - store the path of the drawing for save later:
  13.            string dwgPath = @"path\to\drawing.dwg";
  14.  
  15.            // - Get the dwg db, fail early if not available with try/catch:
  16.            try {
  17.                using (var db = new Database(false, true)) {
  18.  
  19.                    db.ReadDwgFile(dwgPath, System.IO.FileShare.ReadWrite, true, "");
  20.  
  21.                    // - turn off the 'metric' layer: (method returns a bool that you may want to use as a check?)
  22.                    TurnLayerOff("METRIC", db);
  23.  
  24.                    // - Get a collection of any incorrect details and their bottom left corner locations
  25.                    Dictionary<ObjectId, Point3d> dodgeyDetails = GetIncorrectDetailDict(db);
  26.  
  27.                    // - For each of the incorect details, transform them to 0,0,0
  28.                    foreach(KeyValuePair<ObjectId, Point3d> entry in dodgeyDetails) {
  29.  
  30.                        MoveToOrigin(entry.Key, entry.Value, db);
  31.  
  32.                    }
  33.  
  34.                    // - save the modified drawing
  35.                    db.SaveAs(dwgPath, DwgVersion.AC1024);
  36.                }
  37.            }
  38.  
  39.            // change the exception type to be more specific if needed
  40.            catch(System.Exception e) {
  41.                // do something with the exception message
  42.            }
  43.        }
  44.  
  45.  
  46.        /// <summary>
  47.        /// Moves an entity from its given location to the origin (0,0,0)
  48.        /// </summary>
  49.        /// <param name="entId"></param>
  50.        /// <param name="location">Current location (or entity origin if you like) of entity</param>
  51.        /// <param name="db"></param>
  52.        private static void MoveToOrigin(ObjectId entId, Point3d location, Database db)
  53.        {
  54.            using (var tr = db.TransactionManager.StartTransaction()) {
  55.  
  56.                var ent = tr.GetObject(entId, OpenMode.ForWrite) as Entity;
  57.                if (ent != null) {
  58.  
  59.                    // get the vector from the incorrect point to the origin (the direction we are going)
  60.                    Vector3d moveVec = location.GetVectorTo(Point3d.Origin);
  61.  
  62.                    // move it:
  63.                    ent.TransformBy(Matrix3d.Displacement(moveVec));
  64.                }
  65.                tr.commit();
  66.            }
  67.        }
  68.  
  69.  
  70.        /// <summary>
  71.        /// Loops through db ModelSpace to find details that are not in the correct location
  72.        /// (i.e. their bottom left corner is not at 0,0,0)
  73.        /// </summary>
  74.        /// <param name="db"></param>
  75.        /// <returns>A dictionary of ObjectIds as keys with the location of the incorrect detail as the value</returns>
  76.        private static Dictionary<ObjectId, Point3d> GetIncorrectDetailDict(Database db)
  77.        {
  78.            Dictionary<ObjectId, Point3d> details = new Dictionary<ObjectId, Point3d>();
  79.  
  80.            using (var tr = db.TransactionManager.StartTransaction()) {
  81.  
  82.                var blockTable = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;
  83.  
  84.                if(blockTable != null) {
  85.  
  86.                    var blockTableRecord = tr.GetObject(blockTable[BlockTableRecord.ModelSpace], OpenMode.ForRead) as BlockTableRecord;
  87.  
  88.                    if(blockTableRecord != null) {
  89.                        foreach(ObjectId entId in blockTableRecord) {
  90.                            var ent = tr.GetObject(entId, OpenMode.ForRead) as Entity;
  91.                            if(ent is Polyline) {
  92.                                Polyline poly = ent as Polyline;
  93.                                if(poly != null) {
  94.  
  95.                                    // ----- CHECKING CODE ----- //
  96.                                    // change to suit if detail is not rectangular
  97.                                    Point3d btmLeft = poly.GeometricExtents.MinPoint;
  98.                                    if(btmLeft != Point3d.Origin) {
  99.                                        details.Add(entId, btmLeft);
  100.                                    }
  101.  
  102.                                }
  103.                            }
  104.                        }
  105.                    }
  106.                }
  107.                tr.commit();
  108.            }
  109.  
  110.            return details;
  111.        }
  112.  
  113.  
  114.        /// <summary>
  115.        /// Turns off a layer given the layer name passed in.
  116.        /// The layername must match that of the one in the symbol table to work(?).
  117.        /// </summary>
  118.        /// <param name="layerName">Name of the layer to turn off</param>
  119.        /// <param name="db">The database containing the layer</param>
  120.        /// <returns>A bool for checking success or not (if it's important)</returns>
  121.        private static bool TurnLayerOff(string layerName, Database db)
  122.        {
  123.            using (var tr = db.TransactionManager.StartTransaction()) {
  124.                var layerTable = tr.GetObject(db.LayerTableId, OpenMode.ForRead) as LayerTable;
  125.                if(layerTable != null) {
  126.                    if (layerTable.Has(layerName)) {
  127.                        var layerTableRecord = tr.GetObject(layerTable[layerName], OpenMode.ForWrite) as LayerTableRecord;
  128.                        if (layerTableRecord != null) {
  129.                            layerTableRecord.IsOff = true;
  130.                            return layerTableRecord.IsOff;
  131.                        }
  132.                    }
  133.                }
  134.                tr.commit();
  135.            }
  136.            // if we get here, something went wrong, send notice of failure
  137.            return false;
  138.        }
  139.  
  140.    }
  141. }
  142.  

« Last Edit: November 26, 2017, 10:52:07 pm by MickD »
"A language that doesn’t have everything is actually easier to program in than some that do."

        — Dennis M. Ritchie

Atook

  • Swamp Rat
  • Posts: 846
Re: Move entities
« Reply #7 on: November 26, 2017, 09:31:19 pm »
I had a bit of spare time to re-write your code (what you have provided), this is how I would write it and how I think about the structure of what I'm doing....

Nice code Mick, thanks for sharing. I like the idea of returning a Dictionary<ObjectId, Point3d> from a function.

I noticed that the transactions weren't commited explicitly. Do db.TransactionManager.StartTransaction() transactions commit once they fall out of scope?

MickD

  • Gator
  • Posts: 2970
  • I don't need a job, I need Money!!
Re: Move entities
« Reply #8 on: November 26, 2017, 10:50:00 pm »
I had a bit of spare time to re-write your code (what you have provided), this is how I would write it and how I think about the structure of what I'm doing....

Nice code Mick, thanks for sharing. I like the idea of returning a Dictionary<ObjectId, Point3d> from a function.

I noticed that the transactions weren't commited explicitly. Do db.TransactionManager.StartTransaction() transactions commit once they fall out of scope?

Thanks Atook, nice catch! Will fix now.
I'm sure I would have found that after a proper test  :whistling:

Yeah, the dictionary is simple, it's sometimes too easy to create a new object type for something like this when all you want is data. Tuples are also handy!
« Last Edit: November 26, 2017, 10:54:13 pm by MickD »
"A language that doesn’t have everything is actually easier to program in than some that do."

        — Dennis M. Ritchie

Eric

  • Mosquito
  • Posts: 4
Re: Move entities
« Reply #9 on: November 27, 2017, 08:54:19 am »
Thanks gile for the quick response and thanks for the warm welcome MickD, love this forum, you guys rock! MickD, you have helped me a lot and your code is very elegant. And it appears to be working. I'm going to break it down to see how it works, but I believe it is due to breaking the code up into separate transactions. I will adjust it for my needs, but you are a life saver. I'll let you know how it goes.


MickD

  • Gator
  • Posts: 2970
  • I don't need a job, I need Money!!
Re: Move entities
« Reply #10 on: November 27, 2017, 03:59:44 pm »
Not a problem Eric, glad to help.

I think one of the problems was your calculation of the vector to move the entity:

distanceToZero = zero.GetVectorTo(new Point3d(lowestLeft.X, lowestLeft.Y, 0));

here you are getting a vector from the origin(?) to the lowest left point, a vector is a direction, not a distance so you want the direction from lowest left to the origin ;)



"A language that doesn’t have everything is actually easier to program in than some that do."

        — Dennis M. Ritchie