hi there
The following code is untested, so please use with caution. Also note that there are assumptions made about the polylines (light weight) and i'm also assuming that the polylines aren't doing anything crazy, like overlapping itself a million times and that they are closed.
This is something that I whipped up in great haste, but I still think it will be of benefit to you. You can tailor/tweak according to your own taste.
I was thinking of football players (block references) within pitches (closed polylines) if you were wondering about the names:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Colors;
using Autodesk.AutoCAD.EditorInput;
using System;
using System.Collections.Generic;
using System.IO;
namespace BKForums.Exercises
{
internal class BlockInXref
{
private string xrefNameMustContain = "nameOfRelevantXref";
private string nameOfPlayerBlock = "nameOfPlayerBlock";
private List<ObjectId> closedPolylines;
public BlockInXref()
{
Document doc = Application.DocumentManager.MdiActiveDocument;
closedPolylines = getClosedPolylines(doc);
}
public List<Player> getPlayersInClosedPolylines(Document doc)
{
Database db = doc.Database;
List<Player> players = new List<Player>();
using (Transaction tr = db.TransactionManager.StartTransaction())
{
foreach (Player player in getPlayers(tr, db))
{
foreach (ObjectId polylineId in closedPolylines)
{
List<Point2d> polylinePoints = getPolylinePoints(polylineId, tr);
if (IsPointInPolygon(polylinePoints, player.Position))
{
players.Add(player);
}
}
}
tr.Commit();
}
return players;
}
private List<Point2d> getPolylinePoints(ObjectId polylineId, Transaction tr)
{
List<Point2d> pointsInPolyline = new List<Point2d>();
// Use a for loop to get each vertex, one by one
Polyline pl = tr.GetObject(polylineId, OpenMode.ForRead) as Polyline;
int vn = pl.NumberOfVertices;
for (int i = 0; i < vn; i++)
{
// Could also get the 3D point here
Point2d pt = pl.GetPoint2dAt(i);
if (!pointsInPolyline.Contains(pt))
{
pointsInPolyline.Add(pt);
}
}
return pointsInPolyline;
}
private List<Player> getPlayers(Transaction tr, Database db)
{
// https://forums.autodesk.com/t5/net/find-nested-blocks-in-xrefs/td-p/4833475
List<Player> players = new List<Player>();
db.ResolveXrefs(true, false);
XrefGraph graph = db.GetHostDwgXrefGraph(true);
GraphNode root = graph.RootNode;
for (int i = 0; i < root.NumOut; i++)
{
XrefGraphNode node = (XrefGraphNode)root.Out(i);
if (node.XrefStatus == XrefStatus.Resolved)
{
if (node.Name.ToUpper().Contains(xrefNameMustContain))
{
Database xrefDatabase = node.Database;
BlockTable xrefBlockTable = tr.GetObject(xrefDatabase.BlockTableId, OpenMode.ForRead) as BlockTable;
string xRefBlockName = node.Name + "|" + nameOfPlayerBlock;
if (xrefBlockTable.Has(xRefBlockName))
{
BlockTableRecord playerBlockTableRecord = tr.GetObject(xrefBlockTable[xRefBlockName], OpenMode.ForRead) as BlockTableRecord;
foreach (ObjectId id in playerBlockTableRecord.GetBlockReferenceIds(true, false))
{
BlockReference playerBlockReference = tr.GetObject(id, OpenMode.ForRead) as BlockReference;
Player player = new Player(playerBlockReference, tr);
players.Add(player);
}
}
}
}
}
return players;
}
public class Player
{
private Dictionary<string, string> attributes;
private Point2d _position;
public Point2d Position
{
get { return _position; }
}
public Player(BlockReference playerBlockReference, Transaction tr)
{
attributes = getAttributes(playerBlockReference, tr);
_position = new Point2d(playerBlockReference.Position.X, playerBlockReference.Position.Y);
}
private Dictionary<string, string> getAttributes(BlockReference playerBlockReference, Transaction tr)
{
Dictionary<string, string> attributes = new Dictionary<string, string>();
foreach (ObjectId id in playerBlockReference.AttributeCollection)
{
AttributeReference attributeReference = tr.GetObject(id, OpenMode.ForRead) as AttributeReference;
if (attributeReference != null)
{
if (!string.IsNullOrWhiteSpace(attributeReference.TextString))
{
if (!attributes.ContainsKey(attributeReference.Tag))
{
attributes.Add(attributeReference.Tag, attributeReference.TextString);
}
}
}
}
return attributes;
}
}
private List<ObjectId> getClosedPolylines(Document doc)
{
List<ObjectId> ids = new List<ObjectId>();
using (Transaction tr = new OpenCloseTransaction())
{
BlockTableRecord ms = (BlockTableRecord)tr.GetObject(SymbolUtilityServices.GetBlockModelSpaceId(doc.Database), OpenMode.ForRead);
ids = ms.Cast<ObjectId>()
.Where(id => id.ObjectClass == RXClass.GetClass(typeof(Polyline)))
.Select(id => tr.GetObject(id, OpenMode.ForRead) as Polyline)
.Where(polyline => polyline != null)
.Select(polyline => polyline.ObjectId)
.ToList();
tr.Abort();
}
return ids;
}
private static bool IsPointInPolygon(List<Point2d> polygon, Point2d point)
{
bool isInside = false;
for (int i = 0, j = polygon.Count - 1; i < polygon.Count; j = i++)
{
if (((polygon[i].Y > point.Y) != (polygon[j].Y > point.Y)) &&
(point.X < (polygon[j].X - polygon[i].X) * (point.Y - polygon[i].Y) / (polygon[j].Y - polygon[i].Y) + polygon[i].X))
{
isInside = !isInside;
}
}
return isInside;
}
}
}