2

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:

enter image description here

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..

enter image description here

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!

Nick Gilbert
  • 4,159
  • 8
  • 43
  • 90
  • 1
    That's tricky as AutoCAD doesn't understand those lines as a 'wall', but just some lines. So a question for you: what's the user input here? Click on the wall line? If so, at the clicked point, call GetFirstDerivative(), which is the line direction angle, then rotate the 'door' block. – Augusto Goncalves Aug 12 '15 at 18:23
  • The user is prompted to pick a point to insert the block at and it draws it there with my drawblock method – Nick Gilbert Aug 13 '15 at 13:47
  • is this point over a wall line? or can be anywhere? as far as I understand, we cannot calculate the angle without the wall line. – Augusto Goncalves Aug 13 '15 at 15:56
  • I have the front line of the wall stored as a variable in my wall class – Nick Gilbert Aug 13 '15 at 16:48
  • Hi, where in your code is the rotation of the BlockRef set? I didn't see any line for it. The BlockReference Object itself has a Property Rotation, which is a Double (Angle in Radians), so no transformation needed for this... You may consider using a Dinamyc Block (with variable Length Parameter) then you don't have to handle Scales... You may also try [Entityjig](http://through-the-interface.typepad.com/through_the_interface/2015/05/jigging-an-autocad-block-with-attributes-using-net-redux.html) for placing the Block. – dba Sep 06 '16 at 10:47

1 Answers1

0

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();
  }
}
Augusto Goncalves
  • 8,493
  • 2
  • 17
  • 44