To make entity selections, we need to collect some points to make a fence, window, polygon, or so.
We can specify the UseBasePoint, BasePoint, and UseDashedLine properties of the PromptPointOptions for the Editor.GetPoint() method to connect the current cursor position with the previous point input, as we did to the Fence Selection before, but it’s far from user friendly, especially for selection options like window and polygon.
We created a nice little coder, Point Collector, to collect points visually to form a window and a fence before. Let us extend it to support polygon point pick as well.
#region Namespaces
using System;
using System.Text;
using System.Linq;
using System.Xml;
using System.Reflection;
using System.ComponentModel;
using System.Collections;
using System.Collections.Generic;
using System.Windows;
using System.Windows.Media.Imaging;
using System.Windows.Forms;
using System.Drawing;
using System.IO;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Windows;
using MgdAcApplication = Autodesk.AutoCAD.ApplicationServices.Application;
using MgdAcDocument = Autodesk.AutoCAD.ApplicationServices.Document;
using AcWindowsNS = Autodesk.AutoCAD.Windows;
#endregion
using Autodesk.AutoCAD.GraphicsInterface;
namespace AcadNetAddinWizard_Namespace
{
public class PointCollector : IDisposable
{
public enum Shape
{
Window,
Fence,
Polygon,
}
private Shape mShape;
private Autodesk.AutoCAD.DatabaseServices.Polyline mTempPline;
private Point3d m1stPoint;
private double mDist;
private int mSegmentCount = 36;
public Point3dCollection CollectedPoints { get; set; }
public PointCollector(Shape shape)
{
mShape = shape;
CollectedPoints = new Point3dCollection();
}
private void Editor_PointMonitor(object sender, PointMonitorEventArgs e)
{
if (mTempPline != null)
TransientManager.CurrentTransientManager.EraseTransient(mTempPline, new IntegerCollection());
Point3d compPt = e.Context.ComputedPoint.TransformBy(MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.CurrentUserCoordinateSystem.Inverse());
if (mShape == Shape.Window)
{
BuildupWindowVertices(m1stPoint, compPt);
}
else if (mShape == Shape.Fence)
{
BuildupFenceVertices(compPt);
}
else if (mShape == Shape.Polygon)
{
BuildupPolygonVertices(compPt);
}
TransientManager.CurrentTransientManager.AddTransient(mTempPline, TransientDrawingMode.Main, 0, new IntegerCollection());
}
public void Collect()
{
if (mShape == Shape.Window)
{
CollectWindowPoints();
}
else if (mShape == Shape.Fence)
{
CollectFencePoints();
}
else if (mShape == Shape.Polygon)
{
CollectPolygonPoints();
}
}
private void CollectPolygonPoints()
{
PromptPointResult prPntRes1;
PromptPointOptions prPntOpt = new PromptPointOptions("\nPolygon point: (Enter to finish)");
prPntOpt.AllowNone = true;
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.PointMonitor += Editor_PointMonitor;
do
{
prPntRes1 = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.GetPoint(prPntOpt);
if (prPntRes1.Status == PromptStatus.OK)
{
m1stPoint = prPntRes1.Value;
CollectedPoints.Add(m1stPoint);
}
else if (prPntRes1.Status != PromptStatus.None)
{
throw new System.Exception("Polygon point picking failed!");
}
} while (prPntRes1.Status == PromptStatus.OK);
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.PointMonitor -= Editor_PointMonitor;
}
private void BuildupPolygonVertices(Point3d tempPt)
{
if (mTempPline != null && !mTempPline.IsDisposed)
{
mTempPline.Dispose();
mTempPline = null;
}
mTempPline = new Autodesk.AutoCAD.DatabaseServices.Polyline();
mTempPline.SetDatabaseDefaults();
mTempPline.Closed = true;
mTempPline.ColorIndex = 7;
foreach (Point3d pt in CollectedPoints)
{
mTempPline.AddVertexAt(mTempPline.NumberOfVertices, new Point2d(pt.X, pt.Y), 0, 1, 1);
}
mTempPline.AddVertexAt(mTempPline.NumberOfVertices, new Point2d(tempPt.X, tempPt.Y), 0, 1, 1);
mTempPline.TransformBy(MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.CurrentUserCoordinateSystem);
}
…
…
public void Dispose()
{
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.PointMonitor -= Editor_PointMonitor;
if( mTempPline != null )
TransientManager.CurrentTransientManager.EraseTransient(mTempPline, new IntegerCollection());
if (mTempPline != null && !mTempPline.IsDisposed)
mTempPline.Dispose();
CollectedPoints.Dispose();
}
}
}
Please note that the window pick and fence pick related code has been removed to save some space. Please refer to earlier posts for details about it.
Here is the test code and command.
[CommandMethod("PickPolygonPoints", CommandFlags.Modal)]
public void PickPolygonPoints_Method()
{
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
try
{
using (PointCollector ptCol = new PointCollector(PointCollector.Shape.Polygon))
{
ptCol.Collect();
int i = 0;
foreach (Point3d pt in ptCol.CollectedPoints)
{
ed.WriteMessage(string.Format("\nCollected point #{0}: {1}", ++i, pt.ToString()));
}
//Do anything else to the collected points
//...
}
}
catch (System.Exception ex)
{
ed.WriteMessage(Environment.NewLine + ex.Message);
}
}
Here is a screenshot to show how it behaves in AutoCAD.
As can be seen, the command runs well in a UCS, indicating our Point Collector honors UCS perfectly. As mentioned before, if some code/command works fine with UCS we can be sure that it works fine with WCS too as WCS is a special UCS, but vise is not versa! So to repeat one more time, it is a good practice to test code in UCS instead of WCS because nothing reasonable enough should prevent users from trying it in some UCS.
By the way, the points returned by the CollectedPoints collection of the PointCollector are in UCS. We can convert them into WCS with a little additional effort for sure when necessary.
Apart from this nice feature, a lot of other good stuffs in such a succinct but cool coder. Here are a few more.
• The PointCollector is disposable and when its Dispose() method is called things will be cleaned up properly in a single place.
• We’d better use using to create the PointCollector to better utilize the above feature as we always do to the AutoCAD .NET Transaction creations.
• Though the EntityJig or DrawJig can be used to jig the graphics, as we did a few times before, we use the Transient Graphics here to draw temporarily since it’s obvious that either the EntityJig or the DrawJig is over killed.
• To draw some transient graphics onto the AutoCAD drawing area is pretty simple, through calling the TransientManager.CurrentTransientManager.AddTransient() method which needs a Drawable object and a few other parameters to specify the drawing conditions.
• Please don’t forget to remove the Transient Graphics (through calling the method TransientManager.CurrentTransientManager.EraseTransient) in the next draw or when all are done. Otherwise AutoCAD may crash.
• An Editor.PointMonitor event is planted into the system at right time to monitor the cursor positions so as to draw the temporary (transient) graphics.
• The PointMonitorEventArgs.Context.ComputedPoint is used instead of the PointMonitorEventArgs.Context.RawPoint to honor object snapping, ortho-mode and so on.
• Though the Editor.GetPoint() call returns a point in UCS, the PointMonitorEventArgs.Context.ComputedPoint is in WCS. That is why we have to do some transformations to make them consistent with each other so that the Transient Graphics can appear good in both WCS and UCS.
• The APIs of removing and updating Polyline vertices are pretty tricky so we don’t use them at all.
• We only call the Polyline.AddVertexAt() method to both make our life a bit easier and avoid the database or the transient graphics system being messed up.
• The PointCollector currently supports Window, Fence and Polygon shapes.
• Last but not least, the PointCollector can be easily extended to support other shapes, e.g. Regular Polygon. We may demonstrate it in future. Please stay tuned.
The leading edge AutoCAD .NET Addin Wizard (AcadNetAddinWizard) provides project wizards in C#, VB.NET and CLI/Managed C++, and various item wizards such as Event Handlers, Command/LispFunction Definers, and Entity/Draw Jiggers in both C# and VB.NET, to help program AutoCAD addins.
Posted by: |