.NET
cancel
Showing results for 
Show  only  | Search instead for 
Did you mean: 

Creating a Solid Block from a Group of Lines in AutoCAD Programmatically

10 REPLIES 10
SOLVED
Reply
Message 1 of 11
nickg_Clearspan
2108 Views, 10 Replies

Creating a Solid Block from a Group of Lines in AutoCAD Programmatically

I'm trying to draw a solid 3D shape in AutoCAD programmatically. I can get the shape to draw as a wireframe. Here is what my code currently draws

 

enter image description here

 

The object is a bunch of AutoCAD lines grouped together into a Group object. What I want is a method that takes a Group as an argument and turns that group into a solid blockreference. Here is my attempt at it.

public static void DefineBlockFromGroup(Group groupToCreateBlockFrom, string blockName, Point3d origin, bool replaceChosenEntities = false)
{
    List<ObjectId> idsOfEntitiesInGroup = groupToCreateBlockFrom.GetAllEntityIds().ToList(); //Getting the entities that will be used to create the block
    CreateBlockDefinition(idsOfEntitiesInGroup, blockName, origin, replaceChosenEntities);
}

public static void CreateBlockDefinition(List<ObjectId> entities, string blockName, Point3d origin, bool replaceChosenEntities = false)
{
    BlockTableRecord record = new BlockTableRecord(); //Will become record of new block type
    ObjectId blockId = ObjectId.Null; //Will become id of new block type    using (Transaction tr = _database.TransactionManager.StartTransaction())    using (DocumentLock docLock = _activeDocument.LockDocument())
    {
        BlockTable bt = (BlockTable)tr.GetObject(_database.BlockTableId, OpenMode.ForRead); //Getting block table to put new record in        bt.UpgradeOpen();        record = new BlockTableRecord(); //New record will contain all entities which will make up the block        record.Name = blockName; //Setting name of the block        record.Origin = origin; //Setting origin point of the block        bt.Add(record);        tr.AddNewlyCreatedDBObject(record, true);        blockId = bt[record.Name];        tr.Commit();
    }

    //copy the select entities to block by using deepclone.
    ObjectIdCollection collection = new ObjectIdCollection(entities.ToArray());
    IdMapping mapping = new IdMapping();    _database.DeepCloneObjects(collection, blockId, mapping, false);

    if (replaceChosenEntities)
    {        using (Transaction tr = _database.TransactionManager.StartTransaction())        using (DocumentLock docLock = _activeDocument.LockDocument())
        {
            Entity baseEntity = tr.GetObject(entities[0], OpenMode.ForWrite) as Entity;
            foreach (ObjectId id in entities)
            {
                EraseSingleObjectFromDrawingWithId(id);
            }
            DrawBlockFacingAPoint(record.Name, record.Origin, record.Origin, baseEntity.Layer, null);            tr.Commit();
        }
    }
}

public static void EraseSingleObjectFromDrawingWithId(ObjectId idOfObjectToErase)
{
    // Start a transaction    using (Transaction tr = _database.TransactionManager.StartTransaction())
    {
        DBObject objToErase = tr.GetObject(idOfObjectToErase, OpenMode.ForWrite);
        // Check to make sure a valid SelectedObject object was returned
        if (objToErase != null)
        {            objToErase.Erase();
        }        tr.Commit();
    }
}
public static BlockReference DrawBlockFacingAPoint(string name, Point3d position, Point3d direction, string layerToInsertOn, List<string> attributeValues = null, Distance xScale = null, Distance yScale = null, Distance zScale = null)
{
    LastPositionPoint = position;
    LastDirectionPoint = direction;
    //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); //Generating first AutoCAD point
        Point end = Point.MakePointWithInches(direction.X, direction.Y, direction.Z); //Generating second AutoCAD point
        GeometryClassLibrary.Line line = new GeometryClassLibrary.Line(start, end); //Creating a line to mirror a block over if necessary
        Angle a = new Angle(AngleType.Radian, 0); //Angle to rotate  the block by

        //Assigning angle based on direction point
        if (position.Y != direction.Y)
        {            a = line.AngleBetween(GeometryClassLibrary.Line.XAxis);
        }
        if (direction.Y < position.Y)
        {            blkRef.Rotation = a.Negate().GetValue(AngleType.Radian); //Allowing for rotations beyond 180 degrees
        }
        else
        {            blkRef.Rotation = a.GetValue(AngleType.Radian);
        }

        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 (ArgumentNullException)
                {                    ar.TextString = "";
                }
                catch (ArgumentOutOfRangeException)
                {                    ar.TextString = "";
                }                attCounter++;                blkRef.AttributeCollection.AppendAttribute(ar);                tr.AddNewlyCreatedDBObject(ar, true);
            }
        }        tr.Commit();
        return blkRef;
    }
}


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;
    }
}

Sorry there's so many methods involved. I think the problem lies in actually drawing the block but I included all the involved methods just in case. Anyways, the methods succeed in creating a block definition and drawing an instance of that block in place of the group of lines. Problem is the block is still a wireframe. If anyone knows how to change this approach to draw solid blocks I will be wicked grateful!

10 REPLIES 10
Message 2 of 11

I believe that the problem you are going to have is that you cannot create a solid from a group of lines.  None of the constructors for a solid3d take a line or a group of lines as a parameter.  You can grab the endpoints of your lines and create a solid as a box and plug in the coordinates of the endpoints.  That might get you what you are after but you will need to do some work to determine the points to use.

 

Here is a link to the help file that shows how to create a 3d solid as a wedge.  You should be able to use it to create a solid as a box also.

 

http://help.autodesk.com/view/ACD/2016/ENU/?guid=GUID-A5DEE210-347C-4FB7-A0D7-C48F3B949225

 

Message 3 of 11

Thanks for the link! I got solid3d objects drawing now thanks to this method

 

public static ObjectId DrawPolyhedron(Polyhedron polyhedronToDraw, string layerToInsertOn = "0")
{
Solid3d polyhedronToReturn = new Solid3d();
using (Transaction tr = _database.TransactionManager.StartTransaction())
{
// Open the Block table record for read
BlockTable acBlkTbl = tr.GetObject(_database.BlockTableId, OpenMode.ForRead) as BlockTable;

// Open the Block table record Model space for write
BlockTableRecord acBlkTblRec = tr.GetObject(acBlkTbl[BlockTableRecord.ModelSpace], OpenMode.ForWrite) as BlockTableRecord;

polyhedronToReturn.CreateBox(polyhedronToDraw.FindLength().Inches, polyhedronToDraw.FindWidth().Inches, polyhedronToDraw.FindHeight().Inches);

// Add the new object to the block table record and the transaction
acBlkTblRec.AppendEntity(polyhedronToReturn);
tr.AddNewlyCreatedDBObject(polyhedronToReturn, true);

_editor.Regen();
tr.Commit();

return polyhedronToReturn.Id;
}
}

 

The problem is that it draws them at the origin. How can I specify where I want the location of the solid to be?

Message 4 of 11
LE3
Advocate
in reply to: nickg_Clearspan

get a displacement matrix and move your solid, maybe (not tested) - hth:

 

var location = polyhedronLocation - Point3d.Origin;

var displacement = Matrix3d.Displacement(location);

solid.TransformBy(displacement);

Message 5 of 11
nickg_Clearspan
in reply to: LE3

Isn't var - Point3d.Origin = var since Point3d.Origin = {0, 0, 0}?

Message 6 of 11

No need to get fancy with getting the displacement vector.  Just do this.

 

polyhedronToReturn.TransformBy(polyhedronToReturn.CenterPoint.GetAsVector())

You should be able to calculate the solid center point from the line points.  I believe that the box is centered upon the origin when created so you only need to transform it to the center point of the original box defined by lines.

Message 7 of 11

I see, so there's no way to create a solid object (even say just a solid rectangle) from a list of points or faces?

Message 8 of 11

If you notice in the solid.CreateBox method it asks for a length, width, and height.  No where does it ask for a location.  This is because it will always create it centered on the origin.  

 

You are free of course to wrap the CreateBox method into one of your own methods where you pass the location in and it will automatically transform the box.  Something like this.

 

Public Solid3d CreateBoxWithLocation(double Length, double, Width, double Height, Point3d Location)
{
    var solid = New Solid3d()
    solid.CreateBox(Length, Width, Height)
    solid.TransformBy(Matrix3d.Displacement(location.GetAsVector()))

    return solid
}

 

I wrote that off the top of my head so the syntax might be incorrect but hopefully you get the point. 

 

p.s.  You will notice in the post above when I was showing how to tranform the solid that I forgot to add the Matrix3d.  This post shows the correct method.  I am writing this from my phone so not easy to double check code syntax.

 

Message 9 of 11

Gotcha, thanks for all your help Keith!
Message 10 of 11

Here is an extension method that I use from my library.  It overloads the CreateBox method with one that also accepts a point3d for the location.

 

namespace KAB.Tools.GeometryExtensions
{
    using Autodesk.AutoCAD.DatabaseServices;
    using Autodesk.AutoCAD.Geometry;
    using Autodesk.AutoCAD.Runtime;

    using AcRx = Autodesk.AutoCAD.Runtime;

    public static class Solid3DExtensions
    {
        private const double Epsilon = 0.005;

        public static Solid3d CreateBox(this Solid3d solid, double length, double width, double height, Point3d location)
        {
            if (solid == null)
            {
                throw new AcRx.Exception(ErrorStatus.NullEntityPointer, "The solid cannot be null!");
            }

            if ((length < Epsilon) || (width < Epsilon) || (height < Epsilon))
            {
                throw new AcRx.Exception(ErrorStatus.OutOfRange, "The parameter is too small!");
            }

            solid.CreateBox(length, width, height);
            solid.TransformBy(Matrix3d.Displacement(location.GetAsVector()));

            return solid;
        }
    }
}   
Message 11 of 11
dgorsman
in reply to: nickg_Clearspan

Something else to consider when dealing with long regular objects (pipe, ducting, many steel shapes) is you would normally have the cross-sectional shape and a length.  From that you can construct an outline in the appropriate plane and location then extrude it to the required solid.

----------------------------------
If you are going to fly by the seat of your pants, expect friction burns.
"I don't know" is the beginning of knowledge, not the end.


Can't find what you're looking for? Ask the community or share your knowledge.

Post to forums  

Autodesk DevCon in Munich May 28-29th


Autodesk Design & Make Report

”Boost