Updating objects, annotations, and other struggles

Note that I changed the topic title…

I’m having the worst coding experience of my life (but almost past it).

I’m basically trying to create Leader shortcuts. There are two main goals I’m trying to achieve. The first being that I like having my text and leader separate:
image
I can have many leaders pointing to the same text. And for me at least it’s way faster.

The second is having different arrowheads programmed into each quick command. In this case, “Leader1”, “Leader2”, etc… I can take the “English Name” and turn it into a command alias.
image

So I’ve solved these problems, but… the “solution” is ugly. And knowing these things, they something stop working when you come back from a coffee break… we’ll have to see but fingers crossed.

I’m using a command script to draw the initial leader. From there I have to go through hell to update the leader… basically passing it through multiple objects. For whatever reason, objects become closed for editing and I have no idea why this is happening. New objects however allow me to edit them. Which is why I create new / delete old each cycle. I’d rather not do this. And if I absolutely have to, I wonder if the deleted objects are still in the database? And not being disposed of?

The last challenge has to do with how the leader previews.
image
QuickLeader Command Demo

I’m using a command script (I’ve tried everything to avoid this) to draw the leader. I previously tried to juggle dimension styles around (I’ll get to that later). I could get the leader to preview correctly but all hell would break loose. The dimension styles would either duplicate on me, the leader would stop updating, etc…

I had thought of a solution:

  • Store documents “active” dimension style in a variable to restore later,
  • Switch the active dimension style to a ‘place-holding’ dimension style stored in the drawing (create if null… use existing if not null… easy enough),
  • Edit the required properties in that dimension style to reflect the appearance I want from my leader (the Leader Arrow Type and Size) - this so that the leader will have it’s final appearance while being drawn,
  • Run the command,
  • Restore the previous active dimension style,
  • Switch the new leader’s dimension style from the ‘place-holding’ style to the active style.
  • And finally, override the leaders properties.

The second last step is important because my intent is that the ‘place holding’ style would be edited as needed. For example, if I drew a “Dot” leader, I could just change that dimension style’s leader arrow type… but… it also means that any objects set to that dimStyle would change, hence the need to set the leader’s dimStyle to something else (in this case the active/current) and then just override the properties. Things seem to get even more convoluted as apparently? You have to dink around with the object’s AnnotationBase? or AnnotationGeometry? Or something like that.

Whether it’s the dimension style or the objects themselves, whether or not updates are successful, and their results, are extremely unpredictable. I’ve never encountered this to such a magnitude. Maybe I just picked out something that RhinoCommon wasn’t actually built for? Maybe my code/skills is/are garbage? (they are but I should still be able to do this).

Sadly, this is the cleaned up version of my code; it’s still pretty gnarly. Not that I’ve taken out any attempts at toggling the dimension style (for the ‘preview’ part). I have that in another (very messy) file and might share later after having another go at that part of it. Also, I discovered that vertical leaders don’t work for whatever reason (but might have a solution):

[Rhino.Commands.CommandStyle(Rhino.Commands.Style.ScriptRunner)]
    public class QuickLeadersBaseCommand : Command
    {
        public QuickLeadersBaseCommand()
        {
            Instance = this;
        }

        public static QuickLeadersBaseCommand Instance { get; private set; }


        public override string EnglishName => "LEADER1";

        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            // ----------------- Variables --------------------- //

            Leader leader1;
            Leader leader2 = null;
            Leader leader3;

            Guid leader1Guid;
            Guid? leader2Guid = null;
            Guid leader3Guid;

            List<Point3d> leaderPointsList = new List<Point3d>();

            Point3d pt0;
            Point3d pt1;
            Point3d pt2;

            bool nextPoint = true;

            RhinoObject previousObject = doc.Objects.MostRecentObject();
            RhinoObject rhinoObject1;

            RhinoObject rhinoObject3;

            // ------------------ Execution --------------------- //

            // I need to use a script because I want to leader to 'preview' and move around as the user moves their mouse.
            // Maybe I can achieve this with the display pipeline but that looks really hard.

            Rhino.RhinoApp.RunScript("!_-Leader _Pause _Pause _EnterEnd", false);

            rhinoObject1 = doc.Objects.MostRecentObject();


            if (previousObject == null && rhinoObject1 == null)
                return Result.Failure;
            else if (previousObject.Id == rhinoObject1.Id)
                return Result.Failure;
            else if (previousObject.Id != rhinoObject1.Id)
            {
                leader1Guid = rhinoObject1.Id;
                leader1 = (Leader)doc.Objects.FindId(leader1Guid).Geometry;
                pt0 = leader1.Points3D[0];
                pt1 = leader1.Points3D[1];
                leaderPointsList.Add(pt0);
                leaderPointsList.Add(pt1);
                pt2 = pt1;
            }
            else
                return Result.Failure;

            while (nextPoint == true)
            {

                using (GetPoint gp = new GetPoint())
                {
                
                    gp.SetCommandPrompt("Please select the next point");
                    gp.SetBasePoint(pt2, true);
                    gp.DynamicDraw += (sender, e) => e.Display.DrawLine(pt2, e.CurrentPoint, Rhino.RhinoDoc.ActiveDoc.Layers.CurrentLayer.Color);
                    if (gp.Get() == GetResult.Point)
                    {
                        leaderPointsList.Add(gp.Point());
                        
                        pt2 = gp.Point();
                    }
                    else
                    {
                        nextPoint = false;
                    }
                }

                if (leaderPointsList.Count < 3)
                {
                    leader2Guid = doc.Objects.Duplicate(leader1Guid);
                    leader2 = (Leader)doc.Objects.FindId((Guid)leader2Guid).Geometry;
                }
                else
                {
                    leader2Guid = doc.Objects.AddLeader(leaderPointsList);
                    leader2 = (Leader)doc.Objects.FindId((Guid)leader2Guid).Geometry;
                }

                    
                doc.Objects.Delete(leader1Guid, false);

                leader1Guid = (Guid)leader2Guid;

                leader1 = leader2;

                // There's lots of juggling elements around... I tried EVERYTHING... objects just don't seem to update consistently.

            }

            if (leaderPointsList.Count < 3)
            {
                Point3d extraPoint = new Point3d(leaderPointsList.Last().X, leaderPointsList.Last().Y, leaderPointsList.Last().Z);
                leaderPointsList.Add(extraPoint);
            }

            leader3Guid = doc.Objects.AddLeader(leaderPointsList);
            rhinoObject3 = doc.Objects.FindId(leader3Guid);
            leader3 = (Leader)rhinoObject3.Geometry;



            doc.Objects.Delete((Guid)leader2Guid, false);


            // ----------- This method gets overridden to change leader types ------------ //

            ChangeLeaderProperties(doc, leader3, rhinoObject3);


            return Result.Success;


        }

        // ----------------------------------------------------------------------------------------------- //

        // ----------- Method Below will be overriden for various leader styles
        public virtual void ChangeLeaderProperties(RhinoDoc doc, Leader leader, RhinoObject rObj)
        {

            // Creating a dimension style JUST to override a couple properties seems a bit excessive,
            // I wish there was an easier way?
            DimensionStyle s = new DimensionStyle(); 

            s.LeaderArrowType = DimensionStyle.ArrowType.SolidTriangle;
            s.LeaderArrowLength = 0.125;

            leader.SetOverrideDimStyle(s);

            // It's so hard to "commit" the changes to an object consistently.
            // Any mods/changes and it seems to go into lock down.

            rObj.CommitChanges();

        }
        

    } // _____________________________________________________________________________________________________

Your question, I’m afraid, does not make much sense without more context. Please post some code and a 3dm file if needed.

Thanks Menno. I will. I might delete this topic and start a new one as I have a few questions. If I post the code it will make things more clear I think.

I did find a way around the original issue but it might cause other problems.

Edit: I guess I can’t delete a topic. I might just change this one then

Hi Keith, you might look at this example for the dynamic preview part of your question…
_
c.

1 Like

Thanks so much for that clement. It proved to be very helpful in more ways than one. It was both a good exercise translating the code (and well written code, as far as I can tell). And the final result is a WAY cleaner solution than my previous attempt. I still need to figure out the dimension switching but I have a feeling it will be easier to figure out starting from a much cleaner base point. I basically just ran out of time/energy opposed to being stuck.

I’ll share the code in case it helps anyone else out. Note that I probably made a compromise or two on cleanliness just to get it done:

using Rhino;
using Rhino.Commands;
using Rhino.Geometry;
using Rhino.DocObjects;
using Rhino.Input;
using Rhino.Input.Custom;
using System;
using System.Collections.Generic;

namespace kCs_LeaderTest2
{
    public class kCs_LeaderTestCommand2 : Command
    {
        public kCs_LeaderTestCommand2()
        {
            // Rhino only creates one instance of each command class defined in a
            // plug-in, so it is safe to store a refence in a static property.
            Instance = this;
        }

        ///<summary>The only instance of this command.</summary>
        public static kCs_LeaderTestCommand2 Instance { get; private set; }

        ///<returns>The command name as it appears on the Rhino command line.</returns>
        public override string EnglishName => "TEST3";

        // -------- Variables ---------- //
        List<Point3d> points {get; set;}
        Leader leaderGeometry { get; set; }
        Leader leaderToAdd { get; set; }

        static RhinoDoc doc { get; set; }

        Point3d[] pointsArray { get => points.ToArray(); }

        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {
            points = new List<Point3d>();
            Point3d pt0;
            using (GetPoint getPointAction = new GetPoint())
            {
                getPointAction.SetCommandPrompt("Please select the start point");
                if (getPointAction.Get() != GetResult.Point)
                {
                    RhinoApp.WriteLine("No start point was selected.");
                    return getPointAction.CommandResult();
                }
                pt0 = getPointAction.Point();
                points.Add(pt0);
            }


            using (GetPoint gp = new GetPoint())
            {
                gp.ConstrainToConstructionPlane(true);
                gp.PermitElevatorMode(0);
                gp.SetCommandPrompt("Please select another point");
                gp.SetBasePoint(pt0, true);

                gp.DynamicDraw += GetPointDynamicDraw;

                while (true)
                {
                    gp.Get();
                    if (gp.CommandResult() != Result.Success)
                    {
                        break;
                    }
                    else
                    {
                        gp.SetBasePoint(gp.Point(), false);
                        points.Add(gp.Point());
                    }
                }
            }

            
            MakeLeader(points);

            if (leaderGeometry != null)
            {
                RhinoDoc.ActiveDoc.Objects.AddLeader(leaderGeometry);
                RhinoDoc.ActiveDoc.Views.Redraw();
            }

            return Result.Success;
        }

        public void GetPointDynamicDraw(object sender, GetPointDrawEventArgs e)
        {
            try
            {
                List<Point3d> tempPoints = new List<Point3d>();
                foreach (Point3d p in points)
                {
                    tempPoints.Add(p);
                }
                tempPoints.Add(e.CurrentPoint);
                leaderGeometry = MakeLeader(tempPoints);
                if (leaderGeometry != null)
                {
                    e.Display.DrawAnnotation(leaderGeometry, System.Drawing.Color.OrangeRed);
                }
            }
            catch (Exception ex)
            {
                RhinoApp.WriteLine(ex.Message);
            }
        }

        public Leader MakeLeader(List<Point3d> points)
        {

            if (points.Count < 2 )
            {
                return null;
            }

            Plane plane = RhinoDoc.ActiveDoc.Views.ActiveView.ActiveViewport.ConstructionPlane();
            DimensionStyle style = RhinoDoc.ActiveDoc.DimStyles.Current;
            leaderGeometry = Leader.Create("", plane, style, points.ToArray());

            return leaderGeometry;

        }
    }
}
1 Like

Here’s the (almost) final updated code for those interested. From here you’d just have to add concrete? classes and override the EnglishName, and that one method for different leader styles:

using Rhino;
using Rhino.Commands;
using Rhino.Geometry;
using Rhino.DocObjects;
using Rhino.Input;
using Rhino.Input.Custom;
using System;
using System.Collections.Generic;

namespace kCs_LeaderTest2
{
    public class kCs_LeaderTestCommand2 : Command
    {
        public kCs_LeaderTestCommand2()
        {
            Instance = this;
        }

        ///<summary>The only instance of this command.</summary>
        public static kCs_LeaderTestCommand2 Instance { get; private set; }

        ///<returns>The command name as it appears on the Rhino command line.</returns>
        public override string EnglishName => "TEST3";

        // -------- Variables ---------- //
        List<Point3d> points {get; set;}
        Leader leaderGeometry { get; set; }
        Leader leaderToAdd { get; set; }

        static RhinoDoc doc { get; set; }

        Point3d[] pointsArray { get => points.ToArray(); }

        // ---------------- Command Method Below ----------------- //

        protected override Result RunCommand(RhinoDoc doc, RunMode mode)
        {

            //-----------------------------------------------------------------//

            DimensionStyle activeDimStyle = doc.DimStyles.Current;

            ChangeDimSyleProps(doc, activeDimStyle);

            if (doc.DimStyles.FindName("X-kCs-QuickLeaders-DoNotDelete") != null)
            {
                doc.DimStyles.SetCurrent(doc.DimStyles.FindName("X-kCs-QuickLeaders-DoNotDelete").Index, true);
            }
            //-----------------------------------------------------------------//

            points = new List<Point3d>();
            Point3d pt0;
            using (GetPoint getPointAction = new GetPoint())
            {
                getPointAction.SetCommandPrompt("Please select the start point");
                if (getPointAction.Get() != GetResult.Point)
                {
                    RhinoApp.WriteLine("No start point was selected.");
                    return getPointAction.CommandResult();
                }
                pt0 = getPointAction.Point();
                points.Add(pt0);
            }


            using (GetPoint gp = new GetPoint())
            {
                gp.ConstrainToConstructionPlane(true);
                gp.PermitElevatorMode(0);
                gp.SetCommandPrompt("Please select another point");
                gp.SetBasePoint(pt0, true);

                gp.DynamicDraw += GetPointDynamicDraw;

                while (true)
                {
                    gp.Get();
                    if (gp.CommandResult() != Result.Success)
                    {
                        break;
                    }
                    else
                    {
                        gp.SetBasePoint(gp.Point(), false);
                        points.Add(gp.Point());
                    }
                }
            }

            doc.DimStyles.SetCurrent(activeDimStyle.Index, true);

            MakeLeader(points);

            Guid? newLeaderGuid = null;

            if (leaderGeometry != null)
            {
                newLeaderGuid = doc.Objects.AddLeader(leaderGeometry);
                doc.Views.Redraw();
            }

            if (newLeaderGuid != null && doc.DimStyles.FindName("X-kCs-QuickLeaders-DoNotDelete") != null)
            {
                RhinoObject leaderRhinoObject = doc.Objects.FindId((Guid)newLeaderGuid);
                var annotation = leaderRhinoObject.Geometry as AnnotationBase;

                DimensionStyle tempDimStyle = new DimensionStyle();

                ChangeLeaderProps(doc, tempDimStyle);

                annotation.SetOverrideDimStyle(tempDimStyle);

                leaderRhinoObject.CommitChanges();
            }

            

            return Result.Success;
        }

        // ---------------- Helper Methods ----------------- //

        public void GetPointDynamicDraw(object sender, GetPointDrawEventArgs e)
        {
            try
            {
                List<Point3d> tempPoints = new List<Point3d>();
                foreach (Point3d p in points)
                {
                    tempPoints.Add(p);
                }
                tempPoints.Add(e.CurrentPoint);
                leaderGeometry = MakeLeader(tempPoints);
                if (leaderGeometry != null)
                {
                    e.Display.DrawAnnotation(leaderGeometry, System.Drawing.Color.OrangeRed);
                }
            }
            catch (Exception ex)
            {
                RhinoApp.WriteLine(ex.Message);
            }
        }

        public Leader MakeLeader(List<Point3d> points)
        {

            if (points.Count < 2 )
            {
                return null;
            }

            Plane plane = RhinoDoc.ActiveDoc.Views.ActiveView.ActiveViewport.ConstructionPlane();
            DimensionStyle style = RhinoDoc.ActiveDoc.DimStyles.Current;
            leaderGeometry = Leader.Create("", plane, style, points.ToArray());

            return leaderGeometry;

        }

        // ------------------------------------------------------------------------------------ //

        // ---------------- Dimension Style Related Methods ----------------- //

        public void ChangeDimSyleProps(RhinoDoc doc, DimensionStyle activeDimStyle)
        {

            DimensionStyle dimStyle;

            if (doc.DimStyles.FindName("X-kCs-QuickLeaders-DoNotDelete") == null)
            {
                doc.DimStyles.Add("X-kCs-QuickLeaders-DoNotDelete");
            }

            dimStyle = doc.DimStyles.FindName("X-kCs-QuickLeaders-DoNotDelete");

            dimStyle.DimensionScale = activeDimStyle.DimensionScale;

            ChangeLeaderProps(doc, dimStyle);

            doc.DimStyles.Modify(dimStyle, dimStyle.Index, false);

        }

        // ******************* OVERRIDE THIS METHOD WHEN CREATING NEW LEADER STYLES *********************** //

        public virtual void ChangeLeaderProps(RhinoDoc doc, DimensionStyle dimStyle)
        {
            bool userLeaderArrow = true;

            if (userLeaderArrow)
            {
                var instanceDefinition = doc.InstanceDefinitions.Find("X-kCs-Leader-TestArrow");
                if (instanceDefinition != null)
                {
                    dimStyle.LeaderArrowBlockId = instanceDefinition.Id;
                    dimStyle.LeaderArrowType = DimensionStyle.ArrowType.UserBlock;
                }
            }

            //dimStyle.LeaderArrowType = DimensionStyle.ArrowType.SolidTriangle;

            dimStyle.LeaderArrowLength = 0.25;

        }
    } // END OF BASE CLASS ________________________________________________________________________________________
}