Wednesday, June 22, 2011

Show/Hide Segments of a Polyline With Overrule

I saw a question posted in AutoCAD .NET forum asking "if it's possible to create a polyline with invisible segment" here. The question raised my curiosity of thinking how do I do it. Right away, I thought Overrule would be the easy approach to do it. Thus the code shows in this article.

Some explanations. To make things simple, I decide to draw a polyline with multiple segments (e.g. with more than 2 vertices) so that every other segment is visible; also, the Overrule would only apply to polylines that are on certain layer; and the Overrule itself can be turn on or off, regardless if there is other custom Overrule taking effect.

Here is the code:

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.GraphicsInterface;
 
[assembly: CommandClass(typeof(HidePartialPLine.MyCommands))]
 
namespace HidePartialPLine
{
    public class MyCommands 
    {
        private const string OR_LAYER="Overruled_PLine";
        private static MyPLineOverrule _myOR=null;
 
        [CommandMethod("MyOR")]
        public static void ToggleMyOverrule()
        {
            Document dwg = Application.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;
 
            if (_myOR==null) 
            {
                _myOR=new MyPLineOverrule();
                _myOR.StartOverrule();
                ed.WriteMessage("\nMyPLineOverrule is on.\n");
            }
            else
            {
                if (!_myOR.OverruleOn) 
                {
                    _myOR.StartOverrule();
                    ed.WriteMessage("\nMyPLineOverrule is on.\n");
                }
                else
                {
                    _myOR.StopOverrule();
                    ed.WriteMessage("\nMyPLineOverrule is off.\n");
                }
            }    
        }
 
        [CommandMethod("SetOR")]
        public static void SetMyOverruleToPolyline()
        {
            Document dwg = Application.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;
 
            ObjectId id = SelectPolyline(ed);
            if (id == ObjectId.Null)
            {
                ed.WriteMessage("\n*Cancel*\n");
                return;
            }
 
            SetPolylineLayer(id, OR_LAYER, dwg.Database);
        }
 
        #region private methods
 
        private static ObjectId SelectPolyline(Editor ed)
        {
            PromptEntityOptions opt=new PromptEntityOptions("\nPick a polyline");
            opt.SetRejectMessage("You must pick a polyline. ");
            opt.AddAllowedClass(
                typeof(Autodesk.AutoCAD.DatabaseServices.Polyline),true);
 
            PromptEntityResult res=ed.GetEntity(opt);
            if (res.Status==PromptStatus.OK)
            {
                return res.ObjectId;
            }
            else
            {
                return ObjectId.Null;
            }
        }
 
        private static void SetPolylineLayer(
            ObjectId entId, string layerName, Database db)
        {
            using (Transaction tran = db.TransactionManager.StartTransaction())
            {
                Entity ent = (Entity)tran.GetObject(entId, OpenMode.ForRead);
                if (ent.Layer.ToUpper() != OR_LAYER.ToUpper())
                {
                    ent.UpgradeOpen();
                    ent.Layer = OR_LAYER;
                }
                tran.Commit();
            }
        }
 
        #endregion
    }
 
    public class MyPLineOverrule : DrawableOverrule
    {
        private const string OR_LAYER = "Overruled_PLine";
        private bool _overruling=false;
        private bool _oldOverruling=false;
 
        public MyPLineOverrule()
        {
            _overruling=false;
        }
 
        public bool OverruleOn
        {
            get { return _overruling; }
        }
 
        public void StartOverrule()
        {
            _overruling=true;
            _oldOverruling = Overrule.Overruling;
 
            Overrule.AddOverrule(RXObject.GetClass(
                typeof(Autodesk.AutoCAD.DatabaseServices.Polyline)), 
                this, false);
 
            //Set custom filter, so that code in overriden IsApplicable() 
            //will run to do the filtering
            this.SetCustomFilter();
 
            Overrule.Overruling=true;
 
            Application.DocumentManager.MdiActiveDocument.Editor.Regen();
        }
 
        public void StopOverrule()
        {
            _overruling = false;
 
            //Cause Overrule redraw the polyline to its original shape
            //before the overrule is stopped
            Application.DocumentManager.MdiActiveDocument.Editor.Regen();
 
            Overrule.RemoveOverrule(RXObject.GetClass(
                typeof(Autodesk.AutoCAD.DatabaseServices.Polyline)), this);
 
            Overrule.Overruling=_oldOverruling;
        }
 
        #region Override overrule methods
 
        public override bool  IsApplicable(RXObject overruledSubject)
        {
            //Only apply this overrule to polyline on layer "Overruled_PLine"
            Entity ent=overruledSubject as Entity;
            return ent.Layer.ToUpper() == OR_LAYER.ToUpper();
        }
 
        public override bool  WorldDraw(Drawable drawable, WorldDraw wd)
        {
            Autodesk.AutoCAD.DatabaseServices.Polyline pl = drawable
                as Autodesk.AutoCAD.DatabaseServices.Polyline;
 
            //If the polyline only has one segment, draw itself
            if (pl.NumberOfVertices < 3)
            {
                return base.WorldDraw(drawable, wd);
            }
 
            if (_overruling)
            {
                //Get polyline's vertices
                Point3d[] pts = new Point3d[pl.NumberOfVertices];
                for (int i = 0; i < pl.NumberOfVertices; i++)
                {
                    pts[i] = pl.GetPoint3dAt(i);
                }
 
                //Draw every other segement
                int n = 0;
                while (n < pts.Length)
                {
                    wd.Geometry.WorldLine(pts[n], pts[n + 1]);
                    n += 2;
                }
 
                return true;
            }
            else
            {
                return base.WorldDraw(drawable, wd);
            }
        }
 
        #endregion
    }
}

The code pretty much explains itself. To test the code, I started AutoCAD, created a layer named as "Overruled_PLine". Then I drew a few polylines on layer 0. After the code being "netload"ed, I can toggle MyPLineOverrule on/off. When it is on, move polyline to layer "Overruled_PLine" will make the segments of a polyline visible/invisible alternately.

The effect can be watched in this video clip.

1 comment: