TransientManager Trouble

Anonymous

TransientManager Trouble

Anonymous
Not applicable

I've added a TransientManager during a point selection routine so it will draw a red dot every time a user picks a point.  Unfortunately, It seeems to randomly crash AutoCAD with no warning.  Sometimes on the first point, sometines after seven or so.  Any ideas?

 

Also, where can I find a reference to the parameters of the AddTransient routine?  Samples I've found online vary greatly with little explanation.

 

      Private Sub drawDot(dwg As Document, tm As TransientManager, pt As Point3d)
        Dim ed As Editor = dwg.Editor

        Try
          Dim Size As Double = 2.5#
          Dim Bulge As Double = 1.0#
          Dim Width As Double = 5.0#
          Dim oPl As New Autodesk.AutoCAD.DatabaseServices.Polyline
          oPl.ColorIndex = 1

          oPl.AddVertexAt(0, New Point2d(pt.X - Size, pt.Y), Bulge, Width, Width)
          oPl.AddVertexAt(1, New Point2d(pt.X + Size, pt.Y), Bulge, Width, Width)
          oPl.Closed = True

          If dwg.Database.TileMode Then
            ' Add transient geometry
            tm.AddTransient(oPl, TransientDrawingMode.Main, 128, New IntegerCollection)
          End If
        Catch ex As Exception
          Resources.Errors.ErrorManager.ReportError(ex, ActiveValues)
        End Try
      End Sub

 

0 Likes
Reply
Accepted solutions (2)
2,807 Views
6 Replies
Replies (6)

norman.yuan
Mentor
Mentor
Accepted solution

Not seeing all the relevant code (how the TrabsientManager obtained, how the point is picked...), it not easy to accurately tell what goes wrong. However, just seeing the code you showed here, it is evidently enough to say that the code would fail, usually not immediately, but would fail at some points when user repeatedly picking points, or run your commands repeatedly. Why, because the code creates non-DB residing entities (Polyline in your case) and never cleans them up. That is, the drawable polyline for TransientGraphics your code generated should be disposed after you are done with the TransientGraphics.

 

So, it is not good to declare entity variable in a scope of private method and points it to a newly created entity, and then pass the reference over to a object that has bigger scope (TransientManager). This way your code lose the track of the entity it has created and would not be able to clean it up.

 

Following is the pseudo code I woruld do:

 

public Point3dCollection PickPointsWithVisualHint(Editor ed, TransientManager tran)

{

    Point3dCollection points=new Point3dCollection();

    DBObjectCollection drawables=new DbObjectCollection();

 

    while(true)

    {

        //Set PromptPointOptions

        PromptPointResult res=ed.getPoint(options);

        if (res.Status==PromptStatus.OK)

        (

            points.Add(res.Value);

 

            //Create polyline here

            Drawable pl=CreatePolyline(res.Value);

 

            //Add the reference to the drawables collection for later clean-up

            drawables.Add(pl);

 

            //Draw TransientGraphic

            tran.AddTransient(....);

        )

        else

             break;

    }

 

    //Clear TransientGraphics after user finishes picking

    tran.Erase.....

   

    //Dispose the polylines used for TransientGraphics

    if (drawables.Count>0)

    {

        foreeach (DBObject obj in drawables)

          obj.Dispose();

    }

 

    return points;

}

 

Of course, after all points are picked if you still want to keep the TransientGraphic for later operation, you need to delare the "drawables" outside the method and pass it into this method.

Norman Yuan

Drive CAD With Code

EESignature

Anonymous
Not applicable

Norman, thank you for the quick reply.  You were absolutely right.  No clue why I wasn't cleaning up after myself.

 

However, after adding in some tidying the transient geometry isn't displaying any more.  Mind helping me once more?

 

      Private _markers As DBObjectCollection = Nothing

...

      Private Sub ClearTransientGraphics()
        Dim tm As TransientManager = TransientManager.CurrentTransientManager
        Dim intCol As New IntegerCollection()

        If _markers IsNot Nothing Then
          For Each marker As DBObject In _markers
            tm.EraseTransient(marker, intCol)
            marker.Dispose()
          Next
        End If
      End Sub

      Private Function GetPoints(dwg As Document, wkId As String) As Integer
        Dim FeatureResult As Integer = -1
        Dim Features As New List(Of DataItem)

        ' Get the current color, for our temp graphics
        Dim oDb As Database = dwg.Database
        Dim col As Color = dwg.Database.Cecolor
        Dim oEd As Editor = dwg.Editor
        Dim pts As New Point3dCollection

        Dim Size As Double = 2.5#
        Dim Bulge As Double = 1.0#
        Dim Width As Double = 5.0#

        ClearTransientGraphics()
        _markers = New DBObjectCollection()

        Try
          Dim opt As New PromptPointOptions(vbLf & "Select points: ")
          opt.AllowNone = True
          Dim oPPR As PromptPointResult = oEd.GetPoint(opt)

          While oPPR.Status = PromptStatus.OK
            ' Add the selected point to the list
            pts.Add(oPPR.Value)

            ' Add the transient geometry dot
            Dim oPl As New Autodesk.AutoCAD.DatabaseServices.Polyline
            oPl.ColorIndex = 1

            oPl.AddVertexAt(0, New Point2d(oPPR.Value.X - Size, oPPR.Value.Y), Bulge, Width, Width)
            oPl.AddVertexAt(1, New Point2d(oPPR.Value.X + Size, oPPR.Value.Y), Bulge, Width, Width)
            oPl.Closed = True

            _markers.Add(oPl)

            ' Add transient geometry
            Dim tm As TransientManager = TransientManager.CurrentTransientManager
            tm.AddTransient(oPl, TransientDrawingMode.Main, 0, New IntegerCollection)

            ' Continue to select points
            oPPR = oEd.GetPoint(opt)
          End While

          Select Case oPPR.Status ' Result.Status
            Case PromptStatus.None
...
          End Select

        Catch ex As Exception
          Resources.Errors.ErrorManager.ReportError(ex, ActiveValues)
        Finally
          ' Clean up transient geometry
          ClearTransientGraphics()
          oEd.Regen()
        End Try

        Return FeatureResult
      End Function

 

0 Likes

norman.yuan
Mentor
Mentor
Accepted solution

I do not know why the TransientGraphics now showing with your code. I tried the code below (in C#, but basically it is identical to yours), which works with my AutoCAD 2012 perfectly:

 

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.GraphicsInterface;

[assembly: CommandClass(typeof(PickingPointWithVisualHint.MyCommands))]

namespace PickingPointWithVisualHint
{
    public class MyCommands
    {
        private static DBObjectCollection _markers = null;

        [CommandMethod("MyPick")]
        public static void RunMyCommand()
        {
            Document dwg = Application.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;

            _markers = new DBObjectCollection();
            Point3dCollection points = PickPoints(ed);

            if (points.Count > 0)
            {
                ed.WriteMessage("\nPoints picked.");
            }
            else
            {
                ed.WriteMessage("\n*Picking cancelled*");
            }

            Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
        }

        private static Point3dCollection PickPoints(Editor ed)
        {
            Point3dCollection points = new Point3dCollection();
            
            try
            {
                while (true)
                {
                    PromptPointOptions opt = new PromptPointOptions("\nSelect a point:");
                    PromptPointResult res = ed.GetPoint(opt);
                    if (res.Status==PromptStatus.OK)
                    {
                        points.Add(res.Value);

                        Autodesk.AutoCAD.DatabaseServices.Polyline c = 
                            new Autodesk.AutoCAD.DatabaseServices.Polyline(2);
                        c.AddVertexAt(0, new Point2d(res.Value.X - 2.5, res.Value.Y), 1.0, 5.0, 5.0);
                        c.AddVertexAt(1, new Point2d(res.Value.X + 2.5, res.Value.Y), 1.0, 5.0, 5.0);
                        c.Closed = true;
                        c.ColorIndex = 1;
                        _markers.Add(c);

                        TransientManager tm = TransientManager.CurrentTransientManager;
                        tm.AddTransient(c, TransientDrawingMode.Main, 0, new IntegerCollection());
                    }
                    else
                    {
                        break;
                    }
                }
            }
            finally
            {
                ClearTransientGraphics();
                ed.Regen();
            }

            return points;
        }

        private static void ClearTransientGraphics()
        {
            TransientManager tm = TransientManager.CurrentTransientManager;
            
            if (_markers == null) return;

            foreach (DBObject marker in _markers)
            {
                tm.EraseTransient(marker, new IntegerCollection());
                marker.Dispose();
            }

            _markers = null;
        }
    }
}

 

Norman Yuan

Drive CAD With Code

EESignature

Anonymous
Not applicable
Thanks again for your help.
0 Likes

wesbird
Collaborator
Collaborator

Hi Norman:

 

  Great code. I change a bit to fit my need, I use Hatch instead of Pline; I want leave the Transient object after command finish and REDRAW will remove it. It crashed once I remove the while. please see the code:

 

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Colors;
using AcDb = Autodesk.AutoCAD.DatabaseServices;

[assembly: CommandClass(typeof(PickPlineVisualHatch.MyCommands))]

namespace PickPlineVisualHatch
{
    public class MyCommands
    {

        [CommandMethod("MyPickPlineCrash")]
        public static void RunMyCommand_Pline_Crash()
        {
            Document dwg = Application.DocumentManager.MdiActiveDocument;
            Editor ed = dwg.Editor;

            try
            {
                // one remove while, crash 
//                while (true)
//                {
                    PromptEntityOptions peo = new PromptEntityOptions("Select a polyline : ");
                    peo.SetRejectMessage("Not a polyline");
                    peo.AddAllowedClass(typeof(AcDb.Polyline), true);
                    PromptEntityResult per = ed.GetEntity(peo);
                    if (per.Status == PromptStatus.OK)
                    {
                        Hatch c = CreateHatch(per.ObjectId);

                        TransientManager tm = TransientManager.CurrentTransientManager;
                        tm.AddTransient(c, TransientDrawingMode.Main, 0, new IntegerCollection());
                    }
                    else
                    {
//                        break;
                    }
//                }
            }
            finally
            {
                //ClearTransientGraphics();
                //ed.Regen();
            }

            Autodesk.AutoCAD.Internal.Utils.PostCommandPrompt();
        }

        private static Hatch CreateHatch(ObjectId oid)
        {

            ObjectIdCollection ObjIds = new ObjectIdCollection();
            ObjIds.Add(oid);

            // Create our hatch
            Hatch hat = new Hatch();
            hat.SetDatabaseDefaults();

            // Solid fill of our auto-incremented colour index
            hat.SetHatchPattern(HatchPatternType.PreDefined, "SOLID");
            hat.ColorIndex = 1;

            // Set our transparency to 25% (=127)
            // Alpha value is Truncate(255 * (100-n)/100)
            hat.Transparency = new Transparency(63);

            // Add the hatch loop
            hat.AppendLoop(HatchLoopTypes.Default, ObjIds);

            // Complete the hatch
            hat.EvaluateHatch(true);

            return hat;

        }
    }
}

 

I really appreciate if you can have a look. 

 

 

Thank you so much

Wes

Windows 10 64 bit, AutoCAD (ACA, Map) 2023
0 Likes

norman.yuan
Mentor
Mentor

I am not sure what is your purpose to use TransientGraphic, but it is likely the crash was caused because of the Hatch you created not being database-residing and not being disposed.

 

Whatever your intention is, TransientGraphics may not be suitable to your need (creating it and leaving it there while other CAD operation could be performed and only be gone when AutoCAD does "REDRAW"). It is particular bad that you haave to create non-database-residing entities and leave it in memory, not dispose them. Actually, in your code, there is simply no way to dispose the Hatch.

 

If you have to achieve the similar visisual effect and clear it at later time, using DrawableOverrule might be much better solution. In your can simply override the WorldDraw() method to fill  the polyline with desired color. You set the Overrule's custom filter to user picked polyline.

 

You can remove the Overrule by a cusom command, or handling CommandWillStart/CommandEnd against particular commands (Redraw/RedrawAll/...).

Norman Yuan

Drive CAD With Code

EESignature

0 Likes