Thursday, November 18, 2010

Dragging a Line in Certain Angle

This article is inspired by a question posted in the Autodesk's Visual Basic Customization user forum. Baiscally, while drawing a line, after picking the start point, the user wants the ghost line only stretch in certain direction/angle, similar effect as the Ortho-On mode. Well, as a programmer, not a drafter/designer, I am not very sure how often this kind of fuctionality is desired in AutoCAD use. If one wants to draw a line that he knows the line's start/end point, or start point, lenght and direction/angle, he can alway enter them easily at command line. However I can imagine that during designing (nt drafting) process, the designer may want to draw a line, starting at a known point and she'd like it to be stretched at certain angle with undecided length.

Regardless it possible use/benefit an AutoCAD user may find, here is the code to do this. Yes, as you may have guessed, I used TransientGraphics again.

Here is the class that do the dynamic dragging. At the end of AngledDrag() call, the class provides two points (Point3d) - StartPoint and EndPoint as public read-only properties for the calling procedure to use.

using System;

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

namespace AngleLockedDrag
{
    public class AngledDrag
    {
        private Document _dwg;
        private Database _db;
        private Editor _editor;

        private Point3d _startPoint = new Point3d(0.0, 0.0, 0.0);
        private Point3d _endPoint = new Point3d(0.0, 0.0, 0.0);

        private double _dragAngle = 45.0;

        private Line _dragLine = null;
        private int _colorIndex = 1;

        public AngledDrag(Document dwg)
        {
            _dwg = dwg;
            _db = dwg.Database;
            _editor = dwg.Editor;
        }

        public Point3d StartPoint
        {
            get { return _startPoint; }
        }

        public Point3d EndPoint
        {
            get { return _endPoint; }
        }

        #region public methods

        public bool DragAtAngle()
        {
            _endPoint = _startPoint;

            _editor.PointMonitor += 
                new PointMonitorEventHandler(Editor_PointMonitor);

            try
            {
                //Get end point
                if (GetEndPoint())
                {
                    return true;
                }
                else
                {
                    return false;
                }
            }
            finally
            {
                ClearTransientGraphics();
                _editor.PointMonitor -= Editor_PointMonitor;
            }
        }

        #endregion

        #region private methods

        private void Editor_PointMonitor(
            object sender, PointMonitorEventArgs e)
        {
            DrawDragLine(e.Context.RawPoint);
            if (_dragLine != null)
            {
                e.AppendToolTipText("Angle: " + 
                    _dragAngle.ToString() + "\nLength: " + 
                    _dragLine.Length.ToString());
            }
            else
            {
                e.AppendToolTipText("");
            }
        }

        private void DrawDragLine(Point3d mousePoint)
        {
            ClearTransientGraphics();

            Point3d pt = CalculateEndPoint(mousePoint);

            _dragLine = new Line(_startPoint, pt);
            _dragLine.SetDatabaseDefaults(_db);
            _dragLine.ColorIndex = _colorIndex;

            IntegerCollection col = new IntegerCollection();
            TransientManager.CurrentTransientManager.AddTransient(
                _dragLine, TransientDrawingMode.Highlight, 128, col);

            //whenever the dragged line updated, reset _endPoint
            _endPoint = pt;
        }

        private void ClearTransientGraphics()
        {
            if (_dragLine != null)
            {
                IntegerCollection col = new IntegerCollection();
                TransientManager.CurrentTransientManager.
                    EraseTransient(_dragLine, col);

                _dragLine.Dispose();
                _dragLine = null;
            }
        }

        private Point3d CalculateEndPoint(Point3d mousePoint)
        {
            Point3d pt = mousePoint;

            if (_dragAngle <= 90.0 || _dragAngle >= 270.0)
            {
                if (mousePoint.X <= _startPoint.X)
                {
                    pt = _startPoint;
                }
                else
                {
                    if (_dragAngle <= 45.0 || _dragAngle >= 315.0)
                    {
                        double y = (mousePoint.X - _startPoint.X) * 
                            Math.Tan(_dragAngle * Math.PI / 180);
                        pt = new Point3d(
                            mousePoint.X, _startPoint.Y + y, 0.0);
                    }
                    else
                    {
                        if (_dragAngle > 45.0 && _dragAngle <= 90.0)
                        {
                            if (mousePoint.Y < _startPoint.Y)
                            {
                                pt = _startPoint;
                            }
                            else
                            {
                                double x = (mousePoint.Y - _startPoint.Y) / 
                                    Math.Tan(_dragAngle * Math.PI / 180);
                                pt = new Point3d(
                                    _startPoint.X + x, mousePoint.Y, 0.0);
                            }
                        }
                        else
                        {
                            if (mousePoint.Y > _startPoint.Y)
                            {
                                pt = _startPoint;
                            }
                            else
                            {
                                double x = (mousePoint.Y - _startPoint.Y) / 
                                    Math.Tan(_dragAngle * Math.PI / 180);
                                pt = new Point3d(
                                    _startPoint.X + x, mousePoint.Y, 0.0);
                            }
                        }
                    }

                    return pt;
                }
            }

            if (_dragAngle >= 90.0 && _dragAngle <= 270.0)
            {
                if (mousePoint.X >= _startPoint.X)
                {
                    pt = _startPoint;
                }
                else
                {
                    if (_dragAngle >= 135.0 && _dragAngle <= 225.0)
                    {
                        double y = (mousePoint.X - _startPoint.X) * 
                            Math.Tan(_dragAngle * Math.PI / 180);
                        pt = new Point3d(
                            mousePoint.X, _startPoint.Y + y, 0.0);
                    }
                    else
                    {
                        if (_dragAngle >=90.0 && _dragAngle < 135.0)
                        {
                            if (mousePoint.Y <= _startPoint.Y)
                            {
                                pt = _startPoint;
                            }
                            else
                            {
                                double x = (mousePoint.Y - _startPoint.Y) / 
                                    Math.Tan(_dragAngle * Math.PI / 180);
                                pt = new Point3d(
                                    _startPoint.X + x, mousePoint.Y, 0.0);
                            }
                        }
                        else
                        {
                            if (mousePoint.Y >= _startPoint.Y)
                            {
                                pt = _startPoint;
                            }
                            else
                            {
                                double x = (mousePoint.Y - _startPoint.Y) / 
                                    Math.Tan(_dragAngle * Math.PI / 180);
                                pt = new Point3d(
                                    _startPoint.X + x, mousePoint.Y, 0.0);
                            }
                        }
                        
                    }

                    return pt;
                }
            }

            return pt;
        }

        private bool GetEndPoint()
        {
            //endPoint = new Point3d();

            bool go = true;
            bool picked = false;

            while (go)
            {
                PromptPointOptions opt = new PromptPointOptions("\nPick point:");
                //opt.BasePoint = _startPoint;
                //opt.UseBasePoint = true;
                opt.Keywords.Add("Start point");
                opt.Keywords.Add("Angle");
                opt.Keywords.Add("End point");
                opt.Keywords.Add("eXit");
                opt.Keywords.Default = "End point";
                opt.AppendKeywordsToMessage = true;
                opt.AllowArbitraryInput = false;
                opt.AllowNone = false;

                PromptPointResult res = _editor.GetPoint(opt);
                if (res.Status == PromptStatus.Cancel)
                {
                    go = false; ;
                }
                else
                {
                    switch (res.Status)
                    {
                        case PromptStatus.Keyword:
                            //_editor.WriteMessage("\n" + res.StringResult);
                            if (res.StringResult.StartsWith("Start"))
                            {
                                SetStartPoint();
                                go = true;
                            }
                            if (res.StringResult.StartsWith("Angle"))
                            {
                                SetAngle();
                                go = true;
                            }
                            if (res.StringResult.StartsWith("eXit"))
                            {
                                go = false;
                            }
                            break;
                        case PromptStatus.OK:
                            //endPoint = res.Value;
                            picked = true;
                            go = false;
                            break;
                        default:
                            go = true;
                            break;
                    }
                }
            }

            return picked;
        }

        private void SetStartPoint()
        {
            ClearTransientGraphics();
            _editor.PointMonitor -= Editor_PointMonitor;

            PromptPointOptions opt = 
                new PromptPointOptions("\nStart point:");
            PromptPointResult res = _editor.GetPoint(opt);
            if (res.Status == PromptStatus.OK)
            {
                _startPoint = res.Value;
            }

            _editor.PointMonitor += 
                new PointMonitorEventHandler(Editor_PointMonitor);
        }

        private void SetAngle()
        {
            ClearTransientGraphics();
            _editor.PointMonitor -= Editor_PointMonitor;

            PromptDoubleOptions opt = 
                new PromptDoubleOptions("\nEnter drag-angle in degree [" + 
                    _dragAngle.ToString() + "]: ");
            opt.AllowNegative = false;
            opt.AllowZero = true;
            opt.AllowNone = true;

            PromptDoubleResult res = _editor.GetDouble(opt);

            if (res.Status == PromptStatus.OK)
            {
                _dragAngle = res.Value;
                if (_dragAngle > 360.0) _dragAngle -= 360.0;
            }

            _editor.PointMonitor += 
                new PointMonitorEventHandler(Editor_PointMonitor);
        }

        #endregion
    }
}

Here is the command class that uses the AngleLockedDrag class to draw a Line in AutoCAD.

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

[assembly: CommandClass(typeof(AngleLockedDrag.DragCommand))]

namespace AngleLockedDrag
{
    public class DragCommand
    {
        [CommandMethod("AngledDrag")]
        public void RunThisMethod()
        {
            Document dwg = Autodesk.AutoCAD.ApplicationServices.
                Application.DocumentManager.MdiActiveDocument;

            AngledDrag drag = new AngledDrag(dwg);
            try
            {
                if (drag.DragAtAngle())
                {
                    GenerateLine(dwg, drag.StartPoint, drag.EndPoint);

                    dwg.Editor.WriteMessage("\nMyCommand executed.");
                }
            }
            catch (Autodesk.AutoCAD.Runtime.Exception ex)
            {
                dwg.Editor.WriteMessage("\nError: {0}\n", ex.Message);
            }
        }

        private static void GenerateLine(
            Document dwg, Point3d startPt, Point3d endPt)
        {
            using (Transaction tran = 
                dwg.Database.TransactionManager.StartTransaction())
            {
                BlockTableRecord br = (BlockTableRecord)tran.GetObject(
                    dwg.Database.CurrentSpaceId, OpenMode.ForWrite);
                
                Line line = new Line(startPt, endPt);
                line.SetDatabaseDefaults(dwg.Database);

                br.AppendEntity(line);
                tran.AddNewlyCreatedDBObject(line, true);
                tran.Commit();
            }
        }
    }
}

Here is the video clip that shows the "angled dragging" effect.

No comments:

Followers

About Me

My photo
After graduating from university, I worked as civil engineer for more than 10 years. It was AutoCAD use that led me to the path of computer programming. Although I now do more generic business software development, such as enterprise system, timesheet, billing, web services..., AutoCAD related programming is always interesting me and I still get AutoCAD programming tasks assigned to me from time to time. So, AutoCAD goes, I go.