Create cursor with an rectangle

Create cursor with an rectangle

trindelhaven
Participant Participant
2,140 Views
6 Replies
Message 1 of 7

Create cursor with an rectangle

trindelhaven
Participant
Participant

Hello,

my Problem is: I wrote a workflow in .NET, that allows the user to create new pointfeatures by selecting a point from the Map. These points are meant to be midpoints of an rectangle, and the feature holds information about width and height of this rectangle. Now, I would like to preview the rectangle during the selection process by adding an rectangle with the given height an width to the cursor.

 

I read an blog about this and tried to use Autodesk.AUTOCAD.EditorInput.Editor.Drag - and it works fine. The rectangle is right there under my cursor. BUT: This function does what dragging often does: The moment I cancel the point selection with rectangle on my cursor, zoom of the map jumpes back to where it started. That's not what I want!

 

So, my question: Is there a way to stop the drag-function from jumping back on cancel OR does anyone know a better way to change the appearence of the cursor in .NET?

 

Thank you in advance!

 

Here is the code with 'Drag' i used so far:

 

    Private Function GetPoint(pDocument As Autodesk.Map.IM.Forms.Document, pHeight As Double, pWidht As Double) As Point
      Dim result As Point = Nothing
      Dim vDocument As Document = Core.Application.DocumentManager.MdiActiveDocument
      Dim vDataBase As Database = vDocument.Database
      Dim vEditor As Editor = vDocument.Editor
      Dim originPoint As Point3d = Point3d.Origin

      Dim vTrans As Transaction = vDataBase.TransactionManager.StartTransaction()
        Using vTrans
          Dim rect = CreateDragRectangle(Point2d.Origin, pHeight, pWidht)
          Dim vBlockTable As BlockTable = DirectCast(vTrans.GetObject(vDataBase.BlockTableId, OpenMode.ForRead), BlockTable)
          Dim vTableRecord As BlockTableRecord = DirectCast(vTrans.GetObject(vBlockTable(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)

          Dim rectID = vTableRecord.AppendEntity(rect)
          vTrans.AddNewlyCreatedDBObject(rect, True)
          Dim psr As PromptSelectionResult = vEditor.SelectLast()

          If psr.Status = PromptStatus.OK Then
          Dim ppr As PromptPointResult = vEditor.Drag(psr.Value, vbLf & "Select next point: ", Function(pt As Point3d, ByRef mat As Matrix3d) As SamplerStatus
                                                                                                 If originPoint = pt Then Return SamplerStatus.NoChange
                                                                                                 mat = Matrix3d.Displacement(Point3d.Origin.GetVectorTo(pt))
                                                                                                 Return SamplerStatus.OK
                                                                                               End Function)

          If ppr.Status = PromptStatus.OK Then
              result = New Point(ppr.Value.X, ppr.Value.Y)
            End If
          End If
        End Using

      Return result
    End Function
0 Likes
Accepted solutions (2)
2,141 Views
6 Replies
Replies (6)
Message 2 of 7

Norman_Yuan
Mentor
Mentor

I am not very sure I understand your question correctly, but let me try: during a selecting entity/entities (the said feature point/points) process, you want visually present the data represented by the feature points, that is, a rectangle around the each point; once the selecting process done (for further command executing, or whatever purpose), the rectangles should go away. It is similar to entity tooltips when mouse hovers on top of entities, only in your case, you want the rectangles show up when said feature points is selected.

 

If my assumption here is what you want, I do not think that using Editor.Drag() as your code shows is a good solution, where you unnecessarily create rectangles and add then into the database with transaction, and only need to erase them later.

 

I would think using DrawableOverrule could very simple solution in this case:

 

1. You enable the custom DrawableOverrule when your specific selecting process starts;

2. The Oeverrule only applies selected feature points (i.e. the the Overrule uses custom filter);

3. The Oeverrule drawing an rectangle (or whatever you want) around each applied point;

4. When your selecting process is done, remove the custom DrawableOverrule, the rectangles around the feature points is gone automatically.

 

With DrawableOverrule, the drawing database is not changed, only the entity (feature point)'s presentation in AutoCAD's editor is altered, so that a point looks like a rectangle (or whatever you want it to look).

 

If your workflow requires SelectFirst (i.e. user could select the feature points before they decide to run your custom commands), then you just need to decide when to enable the custom DrawableOverrule and when to disbale it.

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 3 of 7

trindelhaven
Participant
Participant

Hm, let me try to correct my explanation:

 

The process of selecting a point means... you go to the Map and click at any point there - it has not to be a existing pointfeature. You click anywhere. Then I create my new object (a featureitem with pointgeometry right at that point). This new feature holds a height and a width property given from the start and represents a rectangle around your selected point. To make the selection of that point more easy and to provide a preview, I want to premanently show the rectangle that would be created from the point where your cursor locates with the given width and height. So, my first idea was to change the cursor to always 'drag a rectangle'.

 

Why a point that has to represent a rectangle? I am coding a plot sequence - the user selects the points in map, my tool than creates plots with the viewport of that rectangle. So I want the user to see the rectangle to know what part of the map will be ploted.

 

I hope this explains it a little better.

I don't know DrawableOverrul yet, do you still consider it a solution here?

0 Likes
Message 4 of 7

Norman_Yuan
Mentor
Mentor

Ok, the workflow, as I understand, could be like this:

 

1. User is asked to pick any point in the drawing;

2. Once user picked, you want to show a rectangle around the picked point as a preview of the feature

3. user then can either accept the picked point, or reject it (to re-pick), or move the mouse around (the preview rectangle should follow the move) for a more suitable position; If the point is picked, a feature point is created and added into database.

4. The picking process may be a loop (repeated picking) until user decide the picking is done. During the picking look, the preview rectangle could be either shown only for currently picked location, or all the preview rectangles remain until the picking process is done.

 

I'd say, yes, you can still use Overrule. The benefit of using Overrule is not only providing visual hind during picking/creating the feature. You can also use the same Overrule to review the feature points in the drawing visually.

 

However, of you only care the visual hind during the picking/creating, then you can use more customizable Drag API EntityJig/DrawJig. Or you can use TransientGraphics. That point is, the preview rectangle is only for visual hint and you do not need to mess around drawing DB with these temporary visuals. Using Overrule, or Jig, or TransientGraphics are all easy enough to do for your case. I'll post here a quick sample later today.

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 5 of 7

Norman_Yuan
Mentor
Mentor
Accepted solution

Here is an example, using Transient graphics, which is rather simple:

 

A class that does the work of creating a feature point (I simply used DBPoint instead), and showing a rectangle around the point being picked/created:

 

using System.Collections.Generic;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using CadDb = Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.GraphicsInterface;

namespace MovingRectangle
{
    public class MyFeatureCreator
    {
        private TransientManager _tsManager = 
            TransientManager.CurrentTransientManager;
        private List<Drawable> _rectangles = new List<Drawable>();
        private Drawable _movingRectangle = null;
        private Document _dwg = null;

        public MyFeatureCreator()
        {
            
        }

        public IEnumerable<ObjectId> CreateFeaturePoints(Document dwg)
        {
            _dwg = dwg;
            return PickFeaturePoints();
        }

        #region private methods

        private IEnumerable<ObjectId> PickFeaturePoints()
        {
            var ptIds = new List<ObjectId>();

            try
            {
                _dwg.Editor.PointMonitor += Editor_PointMonitor;
                int count = 0;
                while (true)
                {
                    ObjectId id;
                    string msg = string.Format(
                        "\nPlease select a point ({0} selected):", count);
                    if (PickFeaturePoint(msg, out id))
                    {
                        count++;
                    }
                    else
                    {
                        break;
                    }
                }
            }
            finally
            {
                ClearTransients();
                _dwg.Editor.PointMonitor -= Editor_PointMonitor;
            }

            return ptIds;
        }

        private void Editor_PointMonitor(object sender, PointMonitorEventArgs e)
        {
            ClearMovingrectagle();
            var point = e.Context.RawPoint;
            _movingRectangle = CreateRectangle(point, true);
            _tsManager.AddTransient(
                _movingRectangle, 
                TransientDrawingMode.DirectShortTerm, 
                128, 
                new IntegerCollection());
        }

        private bool PickFeaturePoint(string msg, out ObjectId ptId)
        {
            ptId = ObjectId.Null;

            var opt = new PromptPointOptions(msg);
            opt.AllowNone = false;
            var res = _dwg.Editor.GetPoint(opt);
            if (res.Status==PromptStatus.OK)
            {
                ptId = AddPointToDB(res.Value);

                //Create preview visual
                var drawable = CreateRectangle(res.Value, false);
                _rectangles.Add(drawable);
                RedrawVisuals();

                return true;
            }
            else
            {
                return false;
            }
        }

        private ObjectId AddPointToDB(Point3d point)
        {
            var id = ObjectId.Null;
            using (var tran = _dwg.TransactionManager.StartTransaction())
            {
                var model = (BlockTableRecord)tran.GetObject(
                    SymbolUtilityServices.GetBlockModelSpaceId(
                        _dwg.Database), OpenMode.ForWrite);
                var pt = new DBPoint(point);
                pt.SetDatabaseDefaults(_dwg.Database);

                id = model.AppendEntity(pt);
                tran.AddNewlyCreatedDBObject(pt, true);
                tran.Commit();
            }
            return id;
        }

        private Drawable CreateRectangle(Point3d point, bool isMovingRect)
        {
            Point2d pt1, pt2, pt3, pt4;
            pt1 = new Point2d(point.X - 10.0, point.Y - 10.0);
            pt2 = new Point2d(point.X + 10.0, point.Y - 10.0);
            pt3 = new Point2d(point.X + 10.0, point.Y + 10.0);
            pt4 = new Point2d(point.X - 10.0, point.Y + 10.0);

            var pl = new CadDb.Polyline(4);
            pl.AddVertexAt(0, pt1, 0.0, 0.0, 0.0);
            pl.AddVertexAt(0, pt2, 0.0, 0.0, 0.0);
            pl.AddVertexAt(0, pt3, 0.0, 0.0, 0.0);
            pl.AddVertexAt(0, pt4, 0.0, 0.0, 0.0);
            pl.Closed = true;
            pl.LineWeight = LineWeight.LineWeight050;
            pl.ColorIndex = isMovingRect ? 1 : 2;

            return pl;
        }

        private void ClearMovingrectagle()
        {
            if (_movingRectangle!=null)
            {
                _tsManager.EraseTransient(
                    _movingRectangle, new IntegerCollection());
                _movingRectangle.Dispose();
                _movingRectangle = null;
            }
        }

        private void ClearTransients(bool clearDrawables=true)
        {
            ClearMovingrectagle();
            if (_rectangles.Count>0)
            {
                _tsManager.EraseTransients(
                    TransientDrawingMode.DirectShortTerm, 
                    128, 
                    new IntegerCollection());
                if (clearDrawables)
                {
                    foreach (var item in _rectangles)
                    {
                        item.Dispose();
                    }
                    _rectangles.Clear();
                }
            }
        }

        private void RedrawVisuals()
        {
            ClearTransients(false);
            foreach (var drawable in _rectangles)
            {
                _tsManager.AddTransient(
                    drawable, 
                    TransientDrawingMode.DirectShortTerm, 
                    128, 
                    new IntegerCollection());
            }
        }

        #endregion
    }
}

Then here is the command class to run it:

 

using Autodesk.AutoCAD.Runtime;
using CadApp = Autodesk.AutoCAD.ApplicationServices.Application;

[assembly: CommandClass(typeof(MovingRectangle.Commands))]

namespace MovingRectangle
{
    public class Commands
    {
        [CommandMethod("MyPt")]
        public static void RunDocCmd()
        {
            var dwg = CadApp.DocumentManager.MdiActiveDocument;
            var ed = dwg.Editor;

            try
            {
                var mytool = new MyFeatureCreator();
                mytool.CreateFeaturePoints(dwg);
            }
            catch (System.Exception ex)
            {
                ed.WriteMessage("Error: {0}", ex.Message);
                ed.WriteMessage("\n*Cancel*");
            }

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

        }
    }
}

See this screencast:

 

http://autode.sk/2pYqVBi

 

HTH

 

Norman Yuan

Drive CAD With Code

EESignature

0 Likes
Message 6 of 7

ActivistInvestor
Mentor
Mentor
Accepted solution

The reason your transparent view changes are being undone when you cancel out, is because you are starting a transaction, but not committing it. When your transaction is disposed (by the 'End Using' statement). the transaction is aborted, and all changes to the database (including view changes) are 'undone'.

 

To solve the problem, add the following line just before the End Using:

 

      

vTrans.Commit()

 


@trindelhaven wrote:

Hello,

my Problem is: I wrote a workflow in .NET, that allows the user to create new pointfeatures by selecting a point from the Map. These points are meant to be midpoints of an rectangle, and the feature holds information about width and height of this rectangle. Now, I would like to preview the rectangle during the selection process by adding an rectangle with the given height an width to the cursor.

 

I read an blog about this and tried to use Autodesk.AUTOCAD.EditorInput.Editor.Drag - and it works fine. The rectangle is right there under my cursor. BUT: This function does what dragging often does: The moment I cancel the point selection with rectangle on my cursor, zoom of the map jumpes back to where it started. That's not what I want!

 

So, my question: Is there a way to stop the drag-function from jumping back on cancel OR does anyone know a better way to change the appearence of the cursor in .NET?

 

Thank you in advance!

 

Here is the code with 'Drag' i used so far:

 

    Private Function GetPoint(pDocument As Autodesk.Map.IM.Forms.Document, pHeight As Double, pWidht As Double) As Point
      Dim result As Point = Nothing
      Dim vDocument As Document = Core.Application.DocumentManager.MdiActiveDocument
      Dim vDataBase As Database = vDocument.Database
      Dim vEditor As Editor = vDocument.Editor
      Dim originPoint As Point3d = Point3d.Origin

      Dim vTrans As Transaction = vDataBase.TransactionManager.StartTransaction()
        Using vTrans
          Dim rect = CreateDragRectangle(Point2d.Origin, pHeight, pWidht)
          Dim vBlockTable As BlockTable = DirectCast(vTrans.GetObject(vDataBase.BlockTableId, OpenMode.ForRead), BlockTable)
          Dim vTableRecord As BlockTableRecord = DirectCast(vTrans.GetObject(vBlockTable(BlockTableRecord.ModelSpace), OpenMode.ForWrite), BlockTableRecord)

          Dim rectID = vTableRecord.AppendEntity(rect)
          vTrans.AddNewlyCreatedDBObject(rect, True)
          Dim psr As PromptSelectionResult = vEditor.SelectLast()

          If psr.Status = PromptStatus.OK Then
          Dim ppr As PromptPointResult = vEditor.Drag(psr.Value, vbLf & "Select next point: ", Function(pt As Point3d, ByRef mat As Matrix3d) As SamplerStatus
                                                                                                 If originPoint = pt Then Return SamplerStatus.NoChange
                                                                                                 mat = Matrix3d.Displacement(Point3d.Origin.GetVectorTo(pt))
                                                                                                 Return SamplerStatus.OK
                                                                                               End Function)

          If ppr.Status = PromptStatus.OK Then
              result = New Point(ppr.Value.X, ppr.Value.Y)
            End If
          End If
vTrans.Commit() End Using Return result End Function



 

0 Likes
Message 7 of 7

trindelhaven
Participant
Participant

@Ativist_Investor: I intentionally skipped the commit to avoid the insertion of the rectangle into database. But you're right, adding the vTrans.Commit solved the problem with the view changes. I just added two lines to remove the rectangle before commiting and now it works! Thank You!

@Norman_Yuan: Your code worked like a charm! Thats exactly what I need! Thank you for your efforts!

0 Likes