I'm trying to write a method that will properly draw a block in AutoCAD. Right now this is the best I've got:
public static BlockReference DrawBlock(string name, Point3d position, string layerToInsertOn, List<string> attributeValues = null, Distance xScale = null, Distance yScale = null, Distance zScale = null)
{
LastPositionPoint = position;
LastDirectionPoint = position;
//Creating default distances if null is passed for the scales
if (xScale == null)
{
xScale = new Distance(DistanceType.Inch, 1);
}
if (yScale == null)
{
yScale = new Distance(DistanceType.Inch, 1);
}
if (zScale == null)
{
zScale = new Distance(DistanceType.Inch, 1);
}
ObjectId blkRecId = _generateBlockRecordId(name); //Generating ID for the block
BlockReference blkRef = null; //Reference of the block that will be inserted
using (Transaction tr = _database.TransactionManager.StartTransaction()) //Starting the transaction to insert the block into the drawing
using (DocumentLock docLock = _activeDocument.LockDocument())
{
blkRef = new BlockReference(position, blkRecId); //Creating the block reference
blkRef.SetDatabaseDefaults();
blkRef.ScaleFactors = new Scale3d(xScale.Inches, yScale.Inches, zScale.Inches); //Changing the scales to what the programmer specifies
blkRef.Layer = layerToInsertOn; //Assigning layer to draw the block on
Point start = Point.MakePointWithInches(position.X, position.Y, position.Z);
Angle a = new Angle(AngleType.Radian, 0); //Angle to rotate the block by
BlockTableRecord blkTblRec = tr.GetObject(_database.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;
blkTblRec.AppendEntity(blkRef); //Adding block referece to the block table of the drawing
tr.AddNewlyCreatedDBObject(blkRef, true); //Adding block to the drawing
//Assigning attributes of the block
BlockTableRecord btr = (BlockTableRecord)tr.GetObject(blkRef.BlockTableRecord, OpenMode.ForRead);
int attCounter = 0; //Counter to iterate through attributes
foreach (ObjectId objId in btr) //Checking each item in the BlockReference's records
{
DBObject obj = objId.GetObject(OpenMode.ForRead);
if (obj is AttributeDefinition) //If the object is an attribute, update it.
{
AttributeDefinition ad = obj as AttributeDefinition;
AttributeReference ar = new AttributeReference();
ar.SetAttributeFromBlock(ad, blkRef.BlockTransform);
ar.Position = ad.Position.TransformBy(blkRef.BlockTransform);
try
{
ar.TextString = attributeValues.ElementAt(attCounter);
}
catch (System.ArgumentOutOfRangeException)
{
ar.TextString = "";
}
catch (System.ArgumentNullException)
{
ar.TextString = "";
}
attCounter++;
blkRef.AttributeCollection.AppendAttribute(ar);
tr.AddNewlyCreatedDBObject(ar, true);
}
}
tr.Commit();
return blkRef;
}
}
/// <summary>
/// Method to generate a block record id for a block to be inserted
/// </summary>
private static ObjectId _generateBlockRecordId(string passedBlockName)
{
Transaction tr = _database.TransactionManager.StartTransaction();
DocumentLock docLock = _activeDocument.LockDocument();
using (tr)
using (docLock)
{
BlockTable blkTbl = tr.GetObject(_database.BlockTableId, OpenMode.ForRead) as BlockTable;
ObjectId blkRecId = ObjectId.Null;
if (blkTbl.Has(passedBlockName)) //Checking if the block exists
{
blkRecId = blkTbl[passedBlockName]; //If it does, getting the current id
}
else //If it doesn't exist create one
{
Database blkDb = new Database(false, true);
blkDb.ReadDwgFile(passedBlockName + ".dwg", System.IO.FileShare.Read, true, ""); //Reading block source file
_database.Insert(passedBlockName, blkDb, true);
blkRecId = blkTbl[passedBlockName];
}
return blkRecId;
}
}
There are a couple problems.
First: lets say I have a block that represents a door. The block is naturally one foot by one foot. If I want to draw a door that is 3' long I pass in 3 for the xScale so that it properly represents a 3' door. The result looks like this:
Which is what I want (the blue lines represent a wall the door goes in). However, if the wall is vertically oriented, the result is not so great..
How can I change the DrawBlock method so that it is irrelevant whether the wall is horizontal, vertical, or anywhere in between? I know I could just pass in the length as the yScale for vertical walls but I need a more generic solution for angled walls.
Also if anyone knows how I can eliminate the need for a helper method that'd be great too. Thanks!
I did a quick testing code to show what I meant... not fully tested or 100% safe, but should give you the general idea:
private static void AlignBlock(ObjectId lineId, ObjectId blockId)
{
if (lineId.ObjectClass != RXClass.GetClass(typeof(Line)))
throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "1st parameter must be a line");
if (blockId.ObjectClass != RXClass.GetClass(typeof(BlockReference)))
throw new Autodesk.AutoCAD.Runtime.Exception(ErrorStatus.InvalidInput, "2nd parameter must be a block");
Database db = lineId.Database;
using (Transaction trans = db.TransactionManager.StartTransaction())
{
Line line = trans.GetObject(lineId, OpenMode.ForRead) as Line;
BlockReference blockRef = trans.GetObject(blockId, OpenMode.ForWrite) as BlockReference;
// better use the center point, instead min/max
Extents3d blockExtents = blockRef.Bounds.Value;
Point3d minPoint = blockExtents.MinPoint;
Point3d maxPoint = blockExtents.MaxPoint;
Point3d pointOverLine = line.GetClosestPointTo(minPoint, false);
Matrix3d blockTranslate = Matrix3d.Displacement(minPoint.GetVectorTo(pointOverLine));
blockRef.TransformBy(blockTranslate); // move
// assuming a well behaved 2D block aligned with XY
Vector3d lineDirection = line.GetFirstDerivative(pointOverLine);
Vector3d blockDirection = minPoint.GetVectorTo(new Point3d(maxPoint.X, minPoint.Y, 0)); // basically a X vector
double angleToRotate = lineDirection.GetAngleTo(blockDirection);
Matrix3d blockRotate = Matrix3d.Rotation(angleToRotate, Vector3d.ZAxis, pointOverLine);
blockRef.TransformBy(blockRotate); // rotate
trans.Commit();
}
}
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.