To make entity selections, we need to collect some points first to make a fence, window, polygon, and so on. We presented a nice little coder a few times before, PointCollector, which can help pick AutoCAD entities from a fence, window, polygon, regular polygon, and even circle visually and friendly.
However, the first version of the PointCollector does not support the background color and the transparency for the selection area, which has been provided in AutoCAD for quite some time. From this post on, we are going to create and demonstrate a nicer but still concise enough version of the point collector, PointCollector2.
Here is it for Window Pick for now.
#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 PointCollector2 : IDisposable
{
public enum Shape
{
Window,
Fence,
Polygon,
RegularPolygon,
Circle,
Box,
}
private Shape mShape;
private bool mIsCrossing;
private Autodesk.AutoCAD.DatabaseServices.Hatch mTempHatch;
private short mHatchColorIndex = 119;
private byte mHatchTransparency = 50;
private TransientDrawingMode mHatchDrawMode;
private Autodesk.AutoCAD.DatabaseServices.Polyline mTempPline;
private short mBoundaryColorIndex = 7;
private TransientDrawingMode mBoundaryDrawMode;
private Point3d m1stPoint;
public Point3dCollection CollectedPoints { get; private set; }
public PromptStatus SelectionStatus { get; private set; }
public PointCollector2(Shape shape, bool isCrossing)
{
mShape = shape;
mIsCrossing = isCrossing;
mTempPline = new Autodesk.AutoCAD.DatabaseServices.Polyline();
mTempPline.SetDatabaseDefaults();
mTempPline.ColorIndex = mBoundaryColorIndex;
mTempHatch = new Hatch();
mTempHatch.SetDatabaseDefaults();
mTempHatch.ColorIndex = mHatchColorIndex;
mTempHatch.SetHatchPattern(HatchPatternType.PreDefined, "SOLID");
mTempHatch.Transparency = new Autodesk.AutoCAD.Colors.Transparency(mHatchTransparency);
mHatchDrawMode = TransientDrawingMode.Main;
if (mIsCrossing)
{
mHatchColorIndex = 119;
mBoundaryDrawMode = TransientDrawingMode.Highlight;
}
else
{
mHatchColorIndex = 172;
mBoundaryDrawMode = TransientDrawingMode.DirectTopmost;
}
CollectedPoints = new Point3dCollection();
SelectionStatus = PromptStatus.OK;
}
private void Editor_PointMonitor(object sender, PointMonitorEventArgs e)
{
Cleanup();
mTempPline = new Autodesk.AutoCAD.DatabaseServices.Polyline();
mTempPline.SetDatabaseDefaults();
mTempPline.ColorIndex = mBoundaryColorIndex;
mTempHatch = new Hatch();
mTempHatch.SetDatabaseDefaults();
mTempHatch.ColorIndex = mHatchColorIndex;
mTempHatch.SetHatchPattern(HatchPatternType.PreDefined, "SOLID");
mTempHatch.Transparency = new Autodesk.AutoCAD.Colors.Transparency(mHatchTransparency);
Point3d compPt = e.Context.ComputedPoint.TransformBy(MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.CurrentUserCoordinateSystem.Inverse());
if (mShape == Shape.Window)
{
BuildupWindowVertices(m1stPoint, compPt);
}
mTempPline.TransformBy(MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.CurrentUserCoordinateSystem);
mTempHatch.Normal = mTempPline.Normal;
HatchLoop loop = new HatchLoop(HatchLoopTypes.Default);
for (int i = 0; i < mTempPline.NumberOfVertices; i++)
{
loop.Curves.Add(mTempPline.GetLineSegment2dAt(i));
}
mTempHatch.AppendLoop(loop);
mTempHatch.EvaluateHatch(true);
TransientManager.CurrentTransientManager.AddTransient(mTempPline, mBoundaryDrawMode, 0, new IntegerCollection());
TransientManager.CurrentTransientManager.AddTransient(mTempHatch, mHatchDrawMode, 0, new IntegerCollection());
}
public Point3dCollection Collect()
{
if (mShape == Shape.Window)
{
CollectWindowPoints();
}
return CollectedPoints;
}
private void CollectWindowPoints()
{
PromptPointResult prPntRes1 = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.GetPoint("\nSpecify first corner");
if (prPntRes1.Status != PromptStatus.OK)
{
SelectionStatus = prPntRes1.Status;
return;
}
m1stPoint = prPntRes1.Value;
CollectedPoints.Add(m1stPoint);
BuildupWindowVertices(m1stPoint, m1stPoint);
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.PointMonitor += Editor_PointMonitor;
PromptPointResult prPntRes2 = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.GetPoint("\nSpecify opposite corner");
if (prPntRes2.Status != PromptStatus.OK)
{
SelectionStatus = prPntRes2.Status;
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.PointMonitor -= Editor_PointMonitor;
return;
}
CollectedPoints.Add(prPntRes2.Value);
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.PointMonitor -= Editor_PointMonitor;
}
private void BuildupWindowVertices(Point3d corner1, Point3d corner2)
{
mTempPline.Closed = true;
mTempPline.AddVertexAt(mTempPline.NumberOfVertices, new Point2d(corner1.X, corner1.Y), 0, 1, 1);
mTempPline.AddVertexAt(mTempPline.NumberOfVertices, new Point2d(corner2.X, corner1.Y), 0, 1, 1);
mTempPline.AddVertexAt(mTempPline.NumberOfVertices, new Point2d(corner2.X, corner2.Y), 0, 1, 1);
mTempPline.AddVertexAt(mTempPline.NumberOfVertices, new Point2d(corner1.X, corner2.Y), 0, 1, 1);
}
public void Dispose()
{
MgdAcApplication.DocumentManager.MdiActiveDocument.Editor.PointMonitor -= Editor_PointMonitor;
Cleanup();
CollectedPoints.Dispose();
}
private void Cleanup()
{
if (mTempPline != null && !mTempPline.IsDisposed)
{
TransientManager.CurrentTransientManager.EraseTransient(mTempPline, new IntegerCollection());
mTempPline.Dispose();
mTempPline = null;
}
if (mTempHatch != null && !mTempHatch.IsDisposed)
{
TransientManager.CurrentTransientManager.EraseTransient(mTempHatch, new IntegerCollection());
mTempHatch.Dispose();
mTempHatch = null;
}
}
}
}
Here is some test code and command.
[CommandMethod("SelectWindow2", CommandFlags.Modal | CommandFlags.Redraw)]
public static void SelectWindow2_Method()
{
Editor ed = MgdAcApplication.DocumentManager.MdiActiveDocument.Editor;
try
{
using (PointCollector2 pc = new PointCollector2(PointCollector2.Shape.Window, false))
{
Point3dCollection points = pc.Collect();
if (pc.SelectionStatus == PromptStatus.OK)
{
PromptSelectionResult prSelRes = ed.SelectWindow(points[0], points[1]);
if (prSelRes.Status == PromptStatus.OK)
{
using (SelectionSet ss = prSelRes.Value)
{
if (ss != null && ss.Count > 0)
{
ed.SetImpliedSelection(ss.GetObjectIds().ToArray());
}
}
}
}
}
}
catch (System.Exception ex)
{
ed.WriteMessage(Environment.NewLine + ex.ToString());
}
}
Here is a video to show how it behaves in AutoCAD.
As can be seen, the command behaves almost the same as the AutoCAD Select command with the Windows option. It also runs well in UCS instead of WCS only, indicating the Point Collector 2 horrors UCS perfectly. By the way, the points returned by the CollectedPoints collection of the PointCollector2 are in UCS. Apart from these nice features, a lot of other good stuffs are in such a succinct but cool coder. Here are a few more.
• The PointCollector2 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 PointCollector2 to better utilize the above feature as we always do to the AutoCAD .NET Transaction creations.
• The Transient Graphics here is used to draw the rectangle and the filled region temporarily.
• The boundary is still drawn as a polyline, the same as before. In the version 2 of the Point Collector, an extra hatch is drawn inside the boundary to show the selection area more friendlily.
• It is necessary to remove the Transient Graphics (through calling the method TransientManager.CurrentTransientManager.EraseTransient) before the next draw.
• 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.
• The consistency has been addressed that the Editor.GetPoint() call returns a point in UCS but the PointMonitorEventArgs.Context.ComputedPoint is in WCS.
• The APIs of removing and updating Polyline vertices are pretty tricky so we don’t use them at all. Instead, 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.
• Last but not least, the PointCollector2 can be easily extended to support other shapes, e.g. Polygon. We will 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: |