setting Z value to 0 in all objects in a drawing

setting Z value to 0 in all objects in a drawing

a.kouchakzadeh
Advocate Advocate
872 Views
6 Replies
Message 1 of 7

setting Z value to 0 in all objects in a drawing

a.kouchakzadeh
Advocate
Advocate

Hi every one, Im trying to remove the Z value of all objects within a drawing (similar to flatten).

this is my code, how ever I having a problem and two questions.

 

namespace Z_remove
{
    public class Class1
    {
        private static HashSet<string> LockedLayers(Database db, Editor ed)
        {
            var lockedLayer = new HashSet<string>();
            using (var tr = db.TransactionManager.StartTransaction())
            {
                var layerTable = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
                foreach (ObjectId id in layerTable)
                {
                    LayerTableRecord layer = tr.GetObject(id, OpenMode.ForRead) as LayerTableRecord;
                    if (layer.IsLocked)
                    {
                        tr.GetObject(id, OpenMode.ForWrite);
                        lockedLayer.Add(layer.Name);
                    }
                }
                tr.Commit();
                return lockedLayer;
            }
        }

        private static void LockUnlockLayers(Database db, Editor ed, HashSet<string> lockedLayers, bool lockLayers)
        {
            using (var tr = db.TransactionManager.StartTransaction())
            {
                var layerTable = (LayerTable)tr.GetObject(db.LayerTableId, OpenMode.ForRead);
                foreach (ObjectId id in layerTable)
                {
                    LayerTableRecord layer = tr.GetObject(id, OpenMode.ForRead) as LayerTableRecord;
                    if (lockedLayers.Contains(layer.Name))
                    {
                        tr.GetObject(id, OpenMode.ForWrite);
                        layer.IsLocked = lockLayers;
                    }
                }
                tr.Commit();
            }
        }

        [CommandMethod("NSVZ")]
        public void RemoveZ()
        {
            var doc = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument;
            var ed = doc.Editor;
            var db = doc.Database;
            HashSet<string> lockedLayers = LockedLayers(db, ed);
            var lockLayers = false;
            LockUnlockLayers(db, ed, lockedLayers, lockLayers);     
            var sSSet = ed.SelectAll();
            using (var tr = db.TransactionManager.StartTransaction())
            {
                foreach(ObjectId objid in sSSet.Value.GetObjectIds())
                {
                    var ent = (Entity)tr.GetObject(objid, OpenMode.ForWrite);
                        try
                        {
                            ed.Command("CHANGE", objid, "", "P", "E", 0, "","");
                            ed.WriteMessage("\n--------------------\n");
                        }
                        catch (System.Exception ex)
                        {

                            ed.WriteMessage("\nexception " + ex.Message);
                        }  
                }
                tr.Commit();
            }

            lockLayers = true;
            LockUnlockLayers(db, ed, lockedLayers, lockLayers);
    }
}

 the problem is when running the command every thing works perfectly fine, except there is one block reference which I can see its insertion point has a z=0, but the block reference still has an elevation. the name is *E42 which Is an anonymous block I guess. anonymous block are bad beasts which I cant even find them in the AutoCAD "insert" menu. how can I deal with them?

my first question is, if I have a nested block reference in the drawing, do I have to create a function and set each element's elevation to 0 by using a foreach loop and:

ed.Command("CHANGE", objid, """P""E"0"","");

my second question is: is there any better way to set all z values to 0?

 

thanks in advance

0 Likes
Accepted solutions (1)
873 Views
6 Replies
Replies (6)
Message 2 of 7

_gile
Consultant
Consultant
Accepted solution

Hi,

1. If not confidential, you should attach this block so that we can try to see what's wrong with ih.

2. Here're some extension methods to flatten the main 2d entities. Some other entities could be added if needed.
It works as the _CHANGE command with _Elevation option, i.e. entities must lie on a plane which is parallel to the WCS XY plane to be "flattened".

 

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

using static System.Math;

namespace FlattenLibrary
{
    public static class FlattenExtension
    {
        public static Point3d Flatten(this Point3d point) =>
            new Point3d(point.X, point.Y, 0.0);

        public static bool TryFlatten(this Entity entity)
        {
            if (entity == null)
                throw new System.ArgumentNullException(nameof (entity));
            var tr = entity.Database.GetTopTransaction();

            bool condition;
            switch (entity)
            {
                case DBPoint dbPoint:
                    dbPoint.Position = dbPoint.Position.Flatten();
                    return true;
                case Arc arc:
                    if (condition = arc.Normal.IsParallelTo(Vector3d.ZAxis))
                        arc.Center = arc.Center.Flatten();
                    break;
                case Circle circle:
                    if (condition = circle.Normal.IsParallelTo(Vector3d.ZAxis))
                        circle.Center = circle.Center.Flatten();
                    break;
                case Ellipse ellipse:
                    if (condition = ellipse.Normal.IsParallelTo(Vector3d.ZAxis))
                        ellipse.Center = ellipse.Center.Flatten();
                    break;
                case Line line:
                    if (condition = Abs(line.StartPoint.Z - line.EndPoint.Z) < 1E-12)
                    {
                        line.StartPoint = line.StartPoint.Flatten();
                        line.EndPoint = line.EndPoint.Flatten();
                    }
                    break;
                case Polyline pline:
                    if (condition = pline.Normal.IsParallelTo(Vector3d.ZAxis))
                        pline.Elevation = 0.0;
                    break;
                case BlockReference br:
                    if (condition = br.Normal.IsParallelTo(Vector3d.ZAxis))
                    {
                        br.Position = br.Position.Flatten();
                        foreach (ObjectId id in br.AttributeCollection)
                        {
                            var attRef = (AttributeReference)tr.GetObject(id, OpenMode.ForWrite);
                            attRef.TryFlatten();
                        }
                    }
                    break;
                case Hatch hatch:
                    if (condition = hatch.Normal.IsParallelTo(Vector3d.ZAxis))
                        hatch.Elevation = 0.0;
                    break;
                case DBText text:
                    if (condition = text.Normal.IsParallelTo(Vector3d.ZAxis))
                    {
                        if (text.Justify == AttachmentPoint.BaseLeft)
                            text.Position = text.Position.Flatten();
                        else
                            text.AlignmentPoint = text.AlignmentPoint.Flatten();
                    }
                    break;
                case MText mtext:
                    if (condition = mtext.Normal.IsParallelTo(Vector3d.ZAxis))
                        mtext.Location = mtext.Location.Flatten();
                    break;
                case Region region:
                    if (condition = region.Normal.IsParallelTo(Vector3d.ZAxis))
                    {
                        var extents = region.GeometricExtents;
                        double elevation = extents.MinPoint.Z;
                        if (elevation != 0.0)
                        {
                            region.TransformBy(Matrix3d.Displacement(Vector3d.ZAxis * -elevation));
                        }
                    }
                    break;
                default:
                    condition = false;
                    break;
            }
            return condition;
        }

        public static bool TryFlatten(this BlockTableRecord btr)
        {
            if (btr == null)
                throw new System.ArgumentNullException(nameof(btr));
            var tr = btr.Database.GetTopTransaction();

            bool flattened = true;
            foreach (ObjectId id in btr)
            {
                var entity = (Entity)tr.GetObject(id, OpenMode.ForWrite);
                if (!entity.TryFlatten()) flattened = false;
            }
            return flattened;
        }

        public static Transaction GetTopTransaction (this Database db)
        {
            if (db == null) throw new Exception(ErrorStatus.NoDatabase);
            var tr = db.TransactionManager.TopTransaction;
            if (tr == null) throw new Exception(ErrorStatus.NoActiveTransactions);
            return tr;
        }
    }
}

 

 

Test command example:

 

        [CommandMethod("TEST")]
        public void Test()
        {
            var doc = AcAp.DocumentManager.MdiActiveDocument;
            var db = doc.Database;
            using (var tr = db.TransactionManager.StartTransaction())
            {
                var bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId id in bt)
                {
                    var btr = (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                    if (!btr.IsAProxy && !btr.IsDependent && !btr.IsFromExternalReference && !btr.IsFromOverlayReference)
                    {
                        tr.GetObject(id, OpenMode.ForWrite);
                        btr.TryFlatten();
                    }
                }
                tr.Commit();
            }
        }

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 3 of 7

a.kouchakzadeh
Advocate
Advocate

Giles that was great. apart of the problem which was solved, your posts always contain some educational stuff. very appreciated sir.

now I have a couple of questions:

your code has two overloads for TryFlatten() right?

why dont you pass the transaction instead of getting the top transaction to TryFlatten()?

also in the test method, what happens if btr.TryFlatten() returns false? why does the TryFlatten has to return a boolean at all?

how can a progressMeter be added to a block table to let the user know the status when it takes too long. how should I define pm.setlimit?

how to add leaderlines and solid objects to the TryFlatten() method? 

 

and finally, if entity is a nested block reference, do we need to loop through each id in it and pass that sub entity to TryFlatten?

 

 

0 Likes
Message 4 of 7

_gile
Consultant
Consultant

Many questions...


@a.kouchakzadeh  a écrit :

your code has two overloads for TryFlatten() right?


Yes, one for the Entity type  and another one for the BlockTableRecord type. The second one calls the first one on all entities it contains.

 


@a.kouchakzadeh  a écrit :

why dont you pass the transaction instead of getting the top transaction to TryFlatten()?


Because I thaught these method should always be called within a Transaction, but you can easily change this if you need.

 


@a.kouchakzadeh  a écrit :

also in the test method, what happens if btr.TryFlatten() returns false? why does the TryFlatten has to return a boolean at all?


I thaught the caller should be notified if some entity in the BlockTableRecord could not be flattened. Same thing for the Entity.TryFlatten method, as some entites may not be flattened (i.e. if the entity type is not (yet) implemented or if the entity does not lies on a plane parallel to XY). My first thaught was to throw an exception in these cases but this  would require the caller to handle exceptions.

 


@a.kouchakzadeh  a écrit :

how can a progressMeter be added to a block table to let the user know the status when it takes too long. how should I define pm.setlimit?


It's a long time I didn't use a ProgressMeter. You could count the number of BlockTableRecords in the BlockTable, pass this number to SetLimits and call  MeterProgress at each BlockTableRecord.

 


@a.kouchakzadeh  a écrit :

how to add leaderlines and solid objects to the TryFlatten() method? 


The principle is the same as for other entities: check if the entity lies on a plane parallel to XY and move them to Z=0. The way to do these tasks depends on the entity properties, you can certainly get inspiration from the already implemented ones.

 


@a.kouchakzadeh  a écrit :

and finally, if entity is a nested block reference, do we need to loop through each id in it and pass that sub entity to TryFlatten?


While you call TryFlatten on every BlockTableRecord in the BlockTable, every block definition will be flattened.



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

0 Likes
Message 5 of 7

_gile
Consultant
Consultant

I simplify the Entity.TryFlatten method so that it would be easier to add new entity types.

I added someones: Leader, Mleader, Dimension, Solid, RasterImage, Mline, Ray and Xline.

using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

using static System.Math;

namespace FlattenLibrary
{
    public static class FlattenExtension
    {
        public static Point3d Flatten(this Point3d point) =>
            new Point3d(point.X, point.Y, 0.0);

        public static bool TryFlatten(this Entity entity)
        {
            if (entity == null)
                throw new System.ArgumentNullException(nameof(entity));

            bool condition;
            double elevation = 0.0;
            switch (entity)
            {
                case DBPoint dbPoint:
                    dbPoint.Position = dbPoint.Position.Flatten();
                    return true;
                case Arc arc:
                    if (condition = arc.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = arc.Center.Z;
                    break;
                case Circle circle:
                    if (condition = circle.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = circle.Center.Z;
                    break;
                case Ellipse ellipse:
                    if (condition = ellipse.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = ellipse.Center.Z;
                    break;
                case Polyline pline:
                    if (condition = pline.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = pline.Elevation;
                    break;
                case Polyline2d pline2d:
                    if (condition = pline2d.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = pline2d.Elevation;
                    break;
                case Line line:
                    if (condition = Abs(line.StartPoint.Z - line.EndPoint.Z) < 1E-12)
                        elevation = line.StartPoint.Z;
                    break;
                case Mline mline:
                    if (condition = mline.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = mline.VertexAt(0).Z;
                    break;
                case Ray ray:
                    if (condition = Abs(ray.UnitDir.Z) < 1E-12)
                        elevation = ray.BasePoint.Z;
                    break;
                case Xline line:
                    if (condition = Abs(line.UnitDir.Z) < 1E-12)
                        elevation = line.BasePoint.Z;
                    break;
                case BlockReference br:
                    if (condition = br.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = br.Position.Z;
                    break;
                case DBText text:
                    if (condition = text.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = text.Position.Z;
                    break;
                case MText mtext:
                    if (condition = mtext.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = mtext.Location.Z;
                    break;
                case Dimension dim:
                    if (condition = dim.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = dim.Elevation;
                    break;
                case Leader leader:
                    if (condition = leader.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = leader.StartPoint.Z;
                    break;
                case MLeader mleader:
                    if (condition = mleader.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = mleader.GetFirstVertex(0).Z;
                    break;
                case Region region:
                    if (condition = region.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = region.GetPlane().PointOnPlane.Z;
                    break;
                case Solid solid:
                    if (condition = solid.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = solid.GetPointAt(0).Z;
                    break;
                case Hatch hatch:
                    if (condition = hatch.Normal.IsParallelTo(Vector3d.ZAxis))
                        elevation = hatch.Elevation;
                    break;
                case RasterImage image:
                    if (condition = image.Orientation.Zaxis.IsParallelTo(Vector3d.ZAxis))
                        elevation = image.Position.Z;
                    break;
                default:
                    condition = false;
                    break;
            }
            if (condition && elevation != 0.0)
                entity.TransformBy(Matrix3d.Displacement(Vector3d.ZAxis * -elevation));
            return condition;
        }

        public static bool TryFlatten(this BlockTableRecord btr)
        {
            if (btr == null)
                throw new System.ArgumentNullException(nameof(btr));
            var tr = btr.Database.GetTopTransaction();

            bool flattened = true;
            foreach (ObjectId id in btr)
            {
                var entity = (Entity)tr.GetObject(id, OpenMode.ForWrite);
                if (!entity.TryFlatten()) flattened = false;
            }
            return flattened;
        }

        public static Transaction GetTopTransaction(this Database db)
        {
            if (db == null) throw new Exception(ErrorStatus.NoDatabase);
            var tr = db.TransactionManager.TopTransaction;
            if (tr == null) throw new Exception(ErrorStatus.NoActiveTransactions);
            return tr;
        }
    }
}

 



Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

Message 6 of 7

a.kouchakzadeh
Advocate
Advocate

Giles, you are great sir!

thank you very much.

0 Likes
Message 7 of 7

宇宙无敌帅
Explorer
Explorer

the batter way to set all Entitys to Z=0 is Move all Entitys from Point3d(0,0,0) To Point3d(0,0,1e99) ,then Move Them from Point3d(0,0,0) to Point3d(0,0,-1e99) again