I owe Chris Kratz a huge thanks for inspiring me to do more with LINQ. This post uses LINQ to provide more elegant solutions to a couple of problems described in previous posts.
The code in today’s post is probably my second brief foray into the world of LINQ. The comments Chris posted on the last post prompted me to spend a little time looking at LINQ over the weekend, and I really like it. I’d heard before that “LINQ is really just functional programming”, and in many ways I see that more clearly now, myself: the set of higher-order functions for manipulating data feel a lot like the capabilities provided in F#, for instance.
Here’s the C# code I put together:
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using System.Collections.Generic;
using System.Linq;
using System;
namespace PointSort
{
public class Commands
{
public void PrintPoints(Editor ed, IEnumerable<Point3d> pts)
{
foreach (Point3d pt in pts)
{
ed.WriteMessage("{0}\n", pt);
}
}
[CommandMethod("PTS")]
public void PointSort()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Editor ed = doc.Editor;
// We'll use a random number generator
Random rnd = new Random();
// Generate the random set of points
var pts =
from n in Enumerable.Range(1, 20)
select
new Point3d(
(2 * rnd.NextDouble() - 1) * 1000,
(2 * rnd.NextDouble() - 1) * 1000,
(2 * rnd.NextDouble() - 1) * 1000
);
// Order them by X then Y then Z
var sorted =
from Point3d pt in pts
orderby pt.X, pt.Y, pt.Z
select pt;
ed.WriteMessage("\nPoints before sort:\n\n");
PrintPoints(ed, pts);
ed.WriteMessage("\n\nPoints after sort:\n\n");
PrintPoints(ed, sorted);
}
[CommandMethod("CG")]
public void CompleteGraph()
{
Document doc =
Application.DocumentManager.MdiActiveDocument;
Database db = doc.Database;
Editor ed = doc.Editor;
// Generate points, put them in a list
double alpha = Math.PI * 2 / 19;
var pts =
(from n in Enumerable.Range(0, 19)
select new Point3d(
Math.Cos(n * alpha) * 5, Math.Sin(n * alpha) * 5, 0
)
).ToList();
// Generate lines from points
var lns =
from Point3d a in pts
from Point3d b in pts
where pts.IndexOf(b) > pts.IndexOf(a)
select new Line(a, b);
// Add them to the current space in the active drawing
Transaction tr = db.TransactionManager.StartTransaction();
using (tr)
{
BlockTableRecord btr =
(BlockTableRecord)tr.GetObject(
db.CurrentSpaceId,
OpenMode.ForWrite
);
foreach (Line ln in lns)
{
btr.AppendEntity(ln);
tr.AddNewlyCreatedDBObject(ln, true);
}
tr.Commit();
}
}
}
}
The above code actually solves a couple of problems from previous posts.
Let’s start by looking at the PTS command:
- This now generates the random points directly, without the need for a set of helper functions
- The sort is now really easy, and (for the sake of it) has been modified to sort on X, then Y, then Z
- The code is shorter, also, as I’ve removed the Point2d solution
I decided to go back to this previous post and rework that, also, resulting in the CG command:
- This code generates a Complete Graph in AutoCAD (much more succinctly than before)
- One statement generates the points, one generates the lines between them
- The point generation does so with a redundant range enumeration (which we replace with points, without using the original enumeration)
- Someone may have a better (meaning more elegant – efficiency isn’t likely to be a major concern) way to do this, but this way does work
I won’t show the results again: they are as before in the respective, original posts.
In the next post we’ll use LINQ once more, this time to provide a solution to a request for a future Plugin of the Month (and this may prove to be the most succinct plugin published, to date, but we’ll see).