Show Posts

This section allows you to view all posts made by this member. Note that you can only see posts made in areas you currently have access to.


Messages - (gile)

Pages: 1 2 3 [4] 5 6
46
Blocks / Block attributes extraction
« on: November 06, 2011, 10:51:54 AM »
Hi,

Here's a little example for attributes extraction.

I try to write it in a "declarative" style using the Linq extension methods and some others defined in the Extension class (thanks to Thorsten 'kaefer' @ TheSwamp for its nice 'evil' ones).

The extraction is collected in a System.Data.DataTable so that it can be easily converted to an AutoCAD table, an Exel sheet and so on.
The DataTable.WriteXls() extension method uses late binding while interacting with Excel application to avoid version reference dependency.

The BlockAttribute class which defines a new Type which main properties are:
- Block:  the block (effective) name ;
- Attributes: a dictionary for the block attributes where Key is the tag and Value the text string.
The BlockAttributeEqualityComparer class implements the IEqualityComparer<BlockAttribute> interface so that BlockAttribute collections can be used with some Linq extension methods as GroupBy().
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace AttributeExtraction
{
    public class BlockAttribute
    {
        private string _name;
        private Dictionary<string, string> _atts;

        // Public read only properties
        public string Name
        {
            get { return _name; }
        }

        public Dictionary<string, string> Attributes
        {
            get { return _atts; }
        }

        public string this[string key]
        {
            get { return _atts[key.ToUpper()]; }
        }

        // Constructors
        public BlockAttribute(BlockReference br)
        {
            SetProperties(br);
        }

        public BlockAttribute(ObjectId id)
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            using (Transaction tr = doc.TransactionManager.StartTransaction())
            {
                SetProperties(tr.GetObject(id, OpenMode.ForRead) as BlockReference);
            }
        }

        // Public method
        new public string ToString()
        {
            if (_atts != null && _atts.Count > 0)
                return string.Format("{0}: {1}",
                    _name,
                    _atts.Select(a => string.Format("{0}={1}", a.Key, a.Value))
                        .Aggregate((a, b) => string.Format("{0}; {1}", a, b)));
            return _name;
        }

        // Private method
        private void SetProperties(BlockReference br)
        {
            if (br == null) return;
            _name = br.GetEffectiveName();
            _atts = new Dictionary<string, string>();
            br.AttributeCollection
                .GetObjects<AttributeReference>()
                .Iterate(att => _atts.Add(att.Tag.ToUpper(), att.TextString));
        }
    }

    public class BlockAttributeEqualityComparer : IEqualityComparer<BlockAttribute>
    {
        public bool Equals(BlockAttribute x, BlockAttribute y)
        {
            return
                x.Name.Equals(y.Name, StringComparison.CurrentCultureIgnoreCase) &&
                x.Attributes.SequenceEqual(y.Attributes);
        }

        public int GetHashCode(BlockAttribute obj)
        {
            return base.GetHashCode();
        }
    }
}

The Extensions class provides extension methods that allows to write code in a more "declarative" style
and methods to build a DataTable from a BlockAttribute collection and convert this DataTable into xls, csv files or AutoCAD Table.
Code: [Select]
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;
using AcDataTable = Autodesk.AutoCAD.DatabaseServices.DataTable;
using Autodesk.AutoCAD.Runtime;

namespace AttributeExtraction
{
    public static class Extensions
    {
        // Thorsten's 'evil' extension methods (thanks again to him)
        public static T GetObject<T>(this ObjectId id) where T : DBObject
        {
            return id.GetObject<T>(OpenMode.ForRead);
        }

        public static T GetObject<T>(this ObjectId id, OpenMode openMode) where T : DBObject
        {
            return id.GetObject(openMode, false, false) as T;
        }

        public static IEnumerable<T> GetObjects<T>(this IEnumerable ids) where T : DBObject
        {
            return ids.GetObjects<T>(OpenMode.ForRead);
        }

        public static IEnumerable<T> GetObjects<T>(this IEnumerable ids, OpenMode openMode) where T : DBObject
        {
            RXClass rxc = RXObject.GetClass(typeof(T));
            return ids
                .Cast<ObjectId>()
                .Where(id => id.ObjectClass == rxc || id.ObjectClass.IsDerivedFrom(rxc))
                .Select(id => (T)id.GetObject(openMode, false, false));
        }

        // Mimics the F# Seq.iter function
        public static void Iterate<T>(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (T item in collection) action(item);
        }

        // Mimics the F# Seq.iteri function
        public static void Iterate<T>(this IEnumerable<T> collection, Action<T, int> action)
        {
            int i = 0;
            foreach (T item in collection) action(item, i++);
        }

        // Gets the block effective name (anonymous dynamic blocs)
        public static string GetEffectiveName(this BlockReference br)
        {
            if (br.IsDynamicBlock)
                return br.DynamicBlockTableRecord.GetObject<BlockTableRecord>().Name;
            return br.Name;
        }

        // Creates a Datatable from a BlockAttribute collection
        public static System.Data.DataTable ToDataTable(this IEnumerable<BlockAttribute> blockAtts, string name)
        {
            System.Data.DataTable dTable = new System.Data.DataTable(name);
            dTable.Columns.Add("Name", typeof(string));
            dTable.Columns.Add("Quantity", typeof(int));
            blockAtts
                .GroupBy(blk => blk, (blk, blks) => new { Block = blk, Count = blks.Count() }, new BlockAttributeEqualityComparer())
                .Iterate(row =>
                {
                    System.Data.DataRow dRow = dTable.Rows.Add(row.Block.Name, row.Count);
                    row.Block.Attributes.Iterate(att =>
                    {
                        if (!dTable.Columns.Contains(att.Key))
                            dTable.Columns.Add(att.Key);
                        dRow[att.Key] = att.Value;
                    });
                });
            return dTable;
        }

        // Converts a Datatble into a two dimensional object array
        public static object[,] ToArray(this System.Data.DataTable dTable)
        {
            int numRows = dTable.Rows.Count;
            int numColoumns = dTable.Columns.Count;
            object[,] result = new object[numRows + 1, numColoumns];
            string[] colNames = dTable.GetColumnNames().ToArray();
            for (int i = 0; i < numColoumns; i++)
            {
                result[0, i] = colNames[i];
            }
            for (int i = 0; i < numRows; i++)
            {
                for (int j = 0; j < numColoumns; j++)
                {
                    result[i + 1, j] = dTable.Rows[i][j];
                }
            }
            return result;
        }

        // Gets the column names collection of the Datatable
        public static IEnumerable<string> GetColumnNames(this System.Data.DataTable dataTbl)
        {
            return dataTbl.Columns.Cast<System.Data.DataColumn>().Select(col => col.ColumnName);
        }

        // Writes a xls file from the datatable
        public static void WriteXls(this System.Data.DataTable dataTbl, string filename, string sheetName, bool visible)
        {
            object mis = Type.Missing;
            object xlApp = LateBinding.GetOrCreateInstance("Excel.Application");
            xlApp.Set("DisplayAlerts", false);
            object workbooks = xlApp.Get("Workbooks");
            object workbook, worksheet;
            if (File.Exists(filename))
                workbook = workbooks.Invoke("Open", filename);
            else
                workbook = workbooks.Invoke("Add", mis);
            if (string.IsNullOrEmpty(sheetName))
                worksheet = workbook.Get("Activesheet");
            else
            {
                object worksheets = workbook.Get("Worksheets");
                try
                {
                    worksheet = worksheets.Get("Item", sheetName);
                    worksheet.Get("Cells").Invoke("Clear");
                }
                catch
                {
                    worksheet = worksheets.Invoke("Add", mis);
                    worksheet.Set("Name", sheetName);
                }
            }
            object range = worksheet.Get("Range",
                worksheet.Get("Cells", 1, 1),
                worksheet.Get("Cells", dataTbl.Rows.Count + 1, dataTbl.Columns.Count));
            range.Set("NumberFormat", "@");
            range.Set("Value2", dataTbl.ToArray());
            xlApp.Set("DisplayAlerts", true);
            if (visible)
            {
                xlApp.Set("Visible", true);
            }
            else
            {
                if (File.Exists(filename))
                    workbook.Invoke("Save");
                else
                {
                    int fileFormat =
                        string.Compare("11.0", (string)xlApp.Get("Version")) < 0 &&
                        filename.EndsWith(".xlsx", StringComparison.CurrentCultureIgnoreCase) ?
                        51 : -4143;
                    workbook.Invoke("Saveas", filename, fileFormat, string.Empty, string.Empty, false, false, 1, 1);
                }
                workbook.Invoke("Close");
                workbook = null;
                xlApp.Invoke("Quit");
                xlApp.ReleaseInstance();
                xlApp = null;
            }
        }

        // Writes a csv file from the datatable
        public static void WriteCsv(this System.Data.DataTable dataTbl, string filename)
        {
            using (StreamWriter writer = new StreamWriter(filename))
            {
                writer.WriteLine(dataTbl.GetColumnNames().Aggregate((s1, s2) => string.Format("{0},{1}", s1, s2)));
                dataTbl.Rows
                    .Cast<DataRow>()
                    .Select(row => row.ItemArray.Aggregate((s1, s2) => string.Format("{0},{1}", s1, s2)))
                    .Iterate(line => writer.WriteLine(line));
            }
        }

        // Creates an AutoCAD Table from the datatable
        public static Table ToAcadTable(this System.Data.DataTable dataTbl, double rowHeight, double columnWidth)
        {
            Table tbl = new Table();
            tbl.Rows[0].Height = rowHeight;
            tbl.Columns[0].Width = columnWidth;
            tbl.InsertColumns(0, columnWidth, dataTbl.Columns.Count - 1);
            tbl.InsertRows(0, rowHeight, dataTbl.Rows.Count + 1);
            tbl.Cells[0, 0].Value = dataTbl.TableName;
            dataTbl.GetColumnNames()
                .Iterate((name, i) => tbl.Cells[1, i].Value = name);
            dataTbl.Rows
                .Cast<DataRow>()
                .Iterate((row, i) =>
                    row.ItemArray.Iterate((item, j) =>
                        tbl.Cells[i + 2, j].Value = item));
            return tbl;
        }
    }
}

The LateBinding class provides helpers to write late binding instructions in a more friendly style (thanks to Thorsten and Tony T).
Code: [Select]
using BF = System.Reflection.BindingFlags;

namespace AttributeExtraction
{
    public static class LateBinding
    {
        public static object GetInstance(string appName)
        {
            return System.Runtime.InteropServices.Marshal.GetActiveObject(appName);
        }

        public static object CreateInstance(string appName)
        {
            return System.Activator.CreateInstance(System.Type.GetTypeFromProgID(appName));
        }

        public static object GetOrCreateInstance(string appName)
        {
            try { return GetInstance(appName); }
            catch { return CreateInstance(appName); }
        }

        public static object Get(this object obj, string propName, params object[] parameter)
        {
            return obj.GetType().InvokeMember(propName, BF.GetProperty, null, obj, parameter);
        }

        public static void Set(this object obj, string propName, params object[] parameter)
        {
            obj.GetType().InvokeMember(propName, BF.SetProperty, null, obj, parameter);
        }

        public static object Invoke(this object obj, string methName, params object[] parameter)
        {
            return obj.GetType().InvokeMember(methName, BF.InvokeMethod, null, obj, parameter);
        }

        public static void ReleaseInstance(this object obj)
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
        }
    }
}

A testing command
Code: [Select]
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace AttributeExtraction
{
    public class Commands
    {
        [CommandMethod("Test")]
        public void Test()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            TypedValue[] filter = { new TypedValue(0, "INSERT") };
            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
            if (psr.Status != PromptStatus.OK) return;
            PromptPointResult ppr = ed.GetPoint("\nInsertion point: ");
            if (ppr.Status != PromptStatus.OK) return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                System.Data.DataTable dataTable = psr.Value.GetObjectIds()
                    .Select(id => new BlockAttribute(id.GetObject<BlockReference>()))
                    .ToDataTable("Extraction");
                Table tbl = dataTable.ToAcadTable(9.0, 40.0);
                tbl.Position = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
                BlockTableRecord btr = db.CurrentSpaceId.GetObject<BlockTableRecord>(OpenMode.ForWrite);
                btr.AppendEntity(tbl);
                tr.AddNewlyCreatedDBObject(tbl, true);
                try
                {
                    string filename = (string)AcAp.GetSystemVariable("dwgprefix") + "Extraction.xls";
                    dataTable.WriteXls(filename, null, true);
                }
                catch
                {
                    AcAp.ShowAlertDialog("Failed to open Excel");
                }
                tr.Commit();
            }
        }
    }
}

47
Math and Geometry / Re: Geometry extensions
« on: November 03, 2011, 07:28:23 AM »
Hi frits432,

You're welcome.
I'm glad you like it.
Unfortunately, I do not have any experience in inertia calculation.
Maybe the use of a region or solid MassProperties is the easier route.

48
Math and Geometry / Re: Geometry extensions
« on: October 16, 2011, 04:12:18 PM »
Hi,

The GeometryExtension librairy keeps on growing.

The main new things are Polyline, Polyline2d and Polyline3d extensions methods : GetProjectedPolyline and GetOrthoProjectedPolyline.

These methods create a new Polyline object and may replace the GetProjectedCurve and GetOrthoProjectedCurve methods which do not give expected results with Polyline and Polyline2d entities.
The elliptical arcs resulting of the polyline arcs projection are approximated using the PolylineSegmentCollection(Ellipse ellipse) constructor. This constructor uses the same geometric method as the 'old' AutoCAD native approximation of ellipses (i.e. PELLIPSE = 1).

Attachment in the first post and documentation have been updated.

49
Math and Geometry / Re: Geometry extensions
« on: September 11, 2011, 01:48:59 PM »
Hi,

I expanded a little the GeometryExtensions library.
There're two more classes: PolylineSegment and PolylineSegmentCollection (which inherits from List<PolylineSegment>) and a new extension method: Ellipse.ToPolyline().
I hope these classes make easier some work with polylines or to create polylines from other entities.

Here's a quick and dirty example (will give unexpected result with entities which don't lies on the WCS XY plane).
The MJOIN command joins the selected entities: line, arc, polyline, ellipse and spline (A2010 or later for spline) into one or more polyline(s).



Code: [Select]
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using GeometryExtensions;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace GeometryExtensionsTestCommands
{
    public class MjoinCommand
    {
        [CommandMethod("mjoin")]
        public void PolyJoin()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            TypedValue[] filter = {   
                new TypedValue(-4,"<OR"),
                new TypedValue(0, "ARC,ELLIPSE,LINE,LWPOLYLINE"),
                new TypedValue(-4, "<AND"),
                new TypedValue(0, "SPLINE"),
                new TypedValue(-4, "&"),
                new TypedValue(70, 8),
                new TypedValue(-4, "AND>"),
                new TypedValue(-4, "OR>")};
            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
            if (psr.Status != PromptStatus.OK) return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTableRecord btr =
                    (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                PolylineSegmentCollection psc = new PolylineSegmentCollection();
                Plane plane = new Plane(Point3d.Origin, Vector3d.ZAxis);
                foreach (ObjectId id in psr.Value.GetObjectIds())
                {
                    Entity ent = (Entity)tr.GetObject(id, OpenMode.ForRead);
                    switch (ent.GetType().Name)
                    {
                        case "Arc":
                            Arc arc = (Arc)ent;
                            psc.Add(new PolylineSegment(
                                new CircularArc2d(
                                    arc.Center.Convert2d(plane),
                                    arc.Radius,
                                    arc.StartAngle,
                                    arc.EndAngle,
                                    Vector2d.XAxis,
                                    false)));
                            break;
                        case "Ellipse":
                            Ellipse el = (Ellipse)ent;
                            psc.AddRange(new PolylineSegmentCollection(el));
                            break;
                        case "Line":
                            Line l = (Line)ent;
                            psc.Add(new PolylineSegment(
                                new LineSegment2d(
                                    l.StartPoint.Convert2d(plane),
                                    l.EndPoint.Convert2d(plane))));
                            break;
                        case "Polyline":
                            Polyline pl = (Polyline)ent;
                            psc.AddRange(new PolylineSegmentCollection(pl));
                            break;
                        case "Spline":
                            try
                            {
                                Spline spl = (Spline)ent;
                                psc.AddRange(new PolylineSegmentCollection((Polyline)spl.ToPolyline()));
                            }
                            catch { }
                            break;
                        default:
                            break;
                    }
                }
                foreach (PolylineSegmentCollection segs in psc.Join())
                {
                    Polyline pline = segs.ToPolyline();
                    btr.AppendEntity(pline);
                    tr.AddNewlyCreatedDBObject(pline, true);
                }
                tr.Commit();
            }
        }
    }
}

50
3D entities / Re: Creating 3D Solids programmatically
« on: August 25, 2011, 08:49:19 PM »
Hi,

Look at the Autodesk.AutoCAD.DatabaseServices.Solid3d class in the ObjectARX doc.

Anyway, here's an example:
Code: [Select]
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace ExtrudedSolidSample
{
    public class Commands
    {
        [CommandMethod("Test")]
        public void Test()
        {
            Database db = HostApplicationServices.WorkingDatabase;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                // Create a closed polyline (it have to be disposed because it won't be added to the database)
                using (Polyline pline = new Polyline(12))
                {
                    pline.AddVertexAt(0, new Point2d(100.0, 100.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(200.0, 100.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(200.0, 130.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(180.0, 130.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(180.0, 170.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(200.0, 170.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(200.0, 200.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(100.0, 200.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(100.0, 170.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(120.0, 170.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(120.0, 130.0), 0.0, 0.0, 0.0);
                    pline.AddVertexAt(0, new Point2d(100.0, 130.0), 0.0, 0.0, 0.0);
                    pline.Closed = true;
                    DBObjectCollection curves = new DBObjectCollection { pline };

                    // Create a region (it have to be disposed because it won't be added to the database)
                    using(DBObjectCollection regions = Region.CreateFromCurves(curves))
                    {
                        // Create a solid 3d (it will be added to the database)
                        Solid3d solid = new Solid3d();
                        solid.Extrude((Region)regions[0], 500.0, 0.0);
                        BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                        BlockTableRecord btr =
                            (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);
                        btr.AppendEntity(solid);
                        tr.AddNewlyCreatedDBObject(solid, true);
                       
                        // Create a slicing plane
                        Plane plane = new Plane(new Point3d(200.0, 200.0, 500.0), new Vector3d(0.0, 1.0, -1.0));

                        // Slice the solid 3d with the plane
                        solid.Slice(plane, true);
                    }
                }
                tr.Commit();
            }
        }
    }
}

51
.NET newbies / Re: My First Revit Plug-in
« on: August 12, 2011, 08:23:38 PM »
Hi,

There's already one for AutoCAD: AutoCAD.Net Training.zip.
This last release is to be used with AutoCAD 2011 (VS 2008) or 2012 (VS 2012).

Previous versions:
- autocad_2007_.net_training.zip
- autocad_2010.net_training.zip

52
Blocks / Re: Beginner: Block Insert Redefinition
« on: July 12, 2011, 10:17:48 PM »
Hi,

You can see in this thread at TheSwamp a 'step by step' to deal with block insertions. The first topics are about inserting a dwg using the Database.ReadDwgFile() and Database.Insert() methods.
Codes are provided in C#, F# and VB.

53
Math and Geometry / Re: Geometry extensions
« on: July 10, 2011, 03:55:06 PM »
Hi,

Here's a 'msdn like' documentation for the GeometryExtension namespace.
I builded it with Sandcastle Help File Builder from the comments within the code.

[attach=1]

Doc.zip contains the GeomExtDoc.chm.

I Updated the attachement in the first post.

54
.NET newbies / Re: Hi. reciving location info about objects
« on: June 04, 2011, 07:11:57 PM »
A way to this is:
- make a selection set (Editor.SelectAll() method) with a SelectionFilter to get all DBText objects on the specified layer.
- instanciate a System.IO.StreamWriter with the file you want to create.
- within a Transaction, open every seleted object and write a line (WriteLine() method) in the file with the text TextString and the text Position.X and Position.Y.

Post what you've written yet so that it'll be easier to help you.

55
.NET newbies / New AutoCAD .NET Training
« on: May 07, 2011, 10:49:08 AM »
Hi,

Autodesk provides a new 'AutoCAD .NET Training' for A2011 and A2012 (usable with Visual Studio 2008 or 2010).

Anouncement on Kean's blog: Through the Interface.

Download on Autodesk Developer Center.

56
Math and Geometry / Re: Convex Hull
« on: April 25, 2011, 09:26:17 PM »
I added a C# code using Linq: a more 'functional style' with C# (can be done with VB too).

57
Math and Geometry / Re: Convex Hull
« on: April 18, 2011, 10:06:52 AM »
Hi,

There's no similar function in AutoCAD, maybe in MAP or Civil, I don't know.

I thaught it was intersting to show how to use a functional style with F# and an imperative* one with C# (or VB) to solve the same problem.

* even if the use of a delegate with the Sort method is more a functional feature.

58
Math and Geometry / Convex Hull
« on: April 16, 2011, 04:43:08 PM »
Here're a C# and a F# implementation of the 'Graham's scan' algorythm to get the convex hull of a 2d points collection.

[EDIT: added a C# implementation using Linq]

C# (targeting NET Framework 2.0 which is the default for A2007 -> A2009).
Code: [Select]
using System.Collections.Generic;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace ConvexHull
{
    public class Commands
    {
        private Point2d _p0;

        private bool Clockwise(Point2d p1, Point2d p2, Point2d p3)
        {
            return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-9;
        }

        private int ComparePoints(Point2d p1, Point2d p2)
        {
            if (p1.IsEqualTo(p2)) return 0;
            double d1 = _p0.GetDistanceTo(p1);
            double d2 = _p0.GetDistanceTo(p2);
            if (d1 == 0.0) return -1;
            if (d2 == 0.0) return 1;
            double cos = (p2.X - _p0.X) / d2 - (p1.X - _p0.X) / d1;
            if (cos < -1e-9) return -1;
            if (cos > 1e-9) return 1;
            return d1.CompareTo(d2);
        }

        private List<Point2d> ConvexHull(List<Point2d> pts)
        {
            _p0 = pts[0];
            for (int i = 1; i < pts.Count; i++)
            {
                Point2d pt = pts[i];
                if (pt.Y < _p0.Y || (pt.Y == _p0.Y && pt.X < _p0.X))
                    _p0 = pt;
            }
            pts.Sort(ComparePoints);
            for (int i = 1; i < pts.Count - 1; i++)
            {
                while (i > 0 && Clockwise(pts[i - 1], pts[i], pts[i + 1]))
                {
                    pts.RemoveAt(i);
                    i--;
                }
            }
            return pts;
        }

        [CommandMethod("ch", CommandFlags.UsePickSet)]
        public void testCh()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            TypedValue[] filter = new TypedValue[1] { new TypedValue(0, "POINT") };
            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
            if (psr.Status != PromptStatus.OK) return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            using (Polyline pline = new Polyline())
            {
                List<Point2d> pts = new List<Point2d>();
                foreach (SelectedObject so in psr.Value)
                {
                    DBPoint dbPt = (DBPoint)tr.GetObject(so.ObjectId, OpenMode.ForRead);
                    pts.Add(new Point2d(dbPt.Position.X, dbPt.Position.Y));
                }
                for (int i = 0; i < ConvexHull(pts).Count; i++)
                {
                    pline.AddVertexAt(i, pts[i], 0.0, 0.0, 0.0);
                }
                pline.Closed = true;
                pline.SetDatabaseDefaults();
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                btr.AppendEntity(pline);
                tr.AddNewlyCreatedDBObject(pline, true);
                tr.Commit();
            }
        }
    }
}

C# (using Linq, NET Framework 3 or upper required)
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace LinqConvexHull
{
    public class Commands
    {
        private Point2d _p0;

        private bool Clockwise(Point2d p1, Point2d p2, Point2d p3)
        {
            return ((p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X)) < 1e-9;
        }

        private double Cosine(Point2d pt)
        {
            double d = _p0.GetDistanceTo(pt);
            return d == 0.0 ? 1.0 : Math.Round((pt.X - _p0.X) / d, 9);
        }

        private List<Point2d> ConvexHull(List<Point2d> pts)
        {
            _p0 = pts.OrderBy(p => p.Y).ThenBy(p => p.X).First();
            pts = pts.OrderByDescending(p => Cosine(p)).ThenBy(p => _p0.GetDistanceTo(p)).ToList();
            for (int i = 1; i < pts.Count - 1; i++)
            {
                while (i > 0 && Clockwise(pts[i - 1], pts[i], pts[i + 1]))
                {
                    pts.RemoveAt(i);
                    i--;
                }
            }
            return pts;
        }

        [CommandMethod("Test")]
        public void Test()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            TypedValue[] filter = new TypedValue[1] { new TypedValue(0, "POINT") };
            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
            if (psr.Status != PromptStatus.OK) return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            using (Polyline pline = new Polyline())
            {
                List<Point2d> pts = new List<Point2d>();
                foreach (SelectedObject so in psr.Value)
                {
                    DBPoint dbPt = (DBPoint)tr.GetObject(so.ObjectId, OpenMode.ForRead);
                    pts.Add(new Point2d(dbPt.Position.X, dbPt.Position.Y));
                }
                pts = ConvexHull(pts);
                for (int i = 0; i < pts.Count; i++)
                {
                    pline.AddVertexAt(i, pts[i], 0.0, 0.0, 0.0);
                }
                pline.Closed = true;
                pline.SetDatabaseDefaults();
                BlockTableRecord btr = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
                btr.AppendEntity(pline);
                tr.AddNewlyCreatedDBObject(pline, true);
                tr.Commit();
            }
        }
    }
}

F#
Code: [Select]
module ConvexHull

open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime

type AcAp = Autodesk.AutoCAD.ApplicationServices.Application

let clockwise (p1:Point2d) (p2:Point2d) (p3:Point2d) =
    (p2.X - p1.X) * (p3.Y - p1.Y) - (p2.Y - p1.Y) * (p3.X - p1.X) < 1e-9
   
let convexHull (pts : Point2d seq) =
    let rec fill acc pt =
        match acc with
        | a :: b :: _ when clockwise b a pt -> fill acc.Tail pt
        | _ -> pt :: acc
    let p0 = pts
             |> Seq.reduce (fun p1 p2 ->
                if p2.Y < p1.Y || (p1.Y = p2.Y && p2.X < p1.X) then p2 else p1)
    pts
    |> List.ofSeq
    |> List.sortBy (fun p ->
        let d = p0.GetDistanceTo(p)
        (Math.Round((p0.X - p.X) / d, 8), d))
    |> List.fold fill []
    |> List.rev

[<CommandMethod("ch")>]
let Test() =
    let doc = AcAp.DocumentManager.MdiActiveDocument
    let db = doc.Database
    let ed = doc.Editor
    let psr = ed.GetSelection(new SelectionFilter([| new TypedValue(0, "POINT") |]))
    if psr.Status = PromptStatus.OK then
        use tr = db.TransactionManager.StartTransaction()
        use pl = new Polyline()
        psr.Value
        |> Seq.cast<_>
        |> Seq.map (fun (so : SelectedObject) ->
            let pt = tr.GetObject(so.ObjectId, OpenMode.ForRead) :?> DBPoint
            new Point2d(pt.Position.X, pt.Position.Y))
        |> convexHull
        |> List.fold(fun i p ->  pl.AddVertexAt(i, p, 0.0, 0.0, 0.0); i + 1) 0
        |> ignore
        pl.Closed <- true
        pl.SetDatabaseDefaults()
        let btr = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) :?> BlockTableRecord
        btr.AppendEntity(pl) |> ignore
        tr.AddNewlyCreatedDBObject(pl, true)
        tr.Commit()

target audience:{advanced}

59
Blocks / Get block references in layouts only
« on: March 24, 2011, 11:05:28 PM »
Hi

The BlockTableRecord.GetBlockReferencesIds() method returns all inserted references, even nested in other blocks.

A way to get only references inserted in layout is to filter the ObjectIdCollection returned by GetBlockReferencesIds() with the reference owner.

The following F# and C# snippets define a function/method which require as arguments the database and the block name.
They return respectively an ObjectId array and an ObjectIdCollection which contain all block references directly inserted in layouts (even dynamic/anonymous blocks).

C#
Code: [Select]
        private ObjectIdCollection GetReferences(Database db, string bName)
        {
            ObjectIdCollection result = new ObjectIdCollection();
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt =
                    (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                if (bt.Has(bName))
                {
                    BlockTableRecord btr =
                        (BlockTableRecord)tr.GetObject(bt[bName], OpenMode.ForRead);
                    foreach (ObjectId refId in btr.GetBlockReferenceIds(true, false))
                    {
                        BlockReference br =
                            (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
                        BlockTableRecord owner =
                            (BlockTableRecord)tr.GetObject(br.OwnerId, OpenMode.ForRead);
                        if (owner.IsLayout)
                            result.Add(br.ObjectId);
                    }
                    foreach (ObjectId id in btr.GetAnonymousBlockIds())
                    {
                        BlockTableRecord anon =
                            (BlockTableRecord)tr.GetObject(id, OpenMode.ForRead);
                        foreach (ObjectId refId in anon.GetBlockReferenceIds(true, false))
                        {
                            BlockReference br =
                                (BlockReference)tr.GetObject(refId, OpenMode.ForRead);
                            BlockTableRecord owner =
                                (BlockTableRecord)tr.GetObject(br.OwnerId, OpenMode.ForRead);
                            if (owner.IsLayout)
                                result.Add(br.ObjectId);
                        }
                    }
                }
                tr.Commit();
            }
            return result;
        }

F#
Code: [Select]
let GetBlockReferences (db : Database) (bName : string) =
    use tr = db.TransactionManager.StartTransaction()
    let bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) :?> BlockTable
    if bt.Has(bName) then
        let btr = tr.GetObject(bt.[bName], OpenMode.ForRead) :?> BlockTableRecord
        btr.GetAnonymousBlockIds()
        |> Seq.cast<_>
        |> Seq.map (fun id -> tr.GetObject(id, OpenMode.ForRead) :?> BlockTableRecord)
        |> Seq.append (Seq.singleton btr)
        |> Seq.collect (fun b ->
            b.GetBlockReferenceIds(true, false)
            |> Seq.cast<_>
            |> Seq.filter (fun id ->
                let br = tr.GetObject(id, OpenMode.ForRead)
                let owner = tr.GetObject(br.OwnerId, OpenMode.ForRead) :?> BlockTableRecord
                owner.IsLayout))
        |> Seq.toArray
    else Array.empty

60
.NET newbies / Re: How can i view an ACAD file using DOTNET
« on: February 10, 2011, 11:47:08 AM »
Hi,

You have to to reference at least acdbmgd.dll and acmgd.dll
Have a look at:
http://docs.autodesk.com/ACD/2010/ENU/AutoCAD%20.NET%20Developer%27s%20Guide/index.html


Pages: 1 2 3 [4] 5 6