eNotApplicable error while doing deep clone.

ShricharanaB
Advocate
Advocate

eNotApplicable error while doing deep clone.

ShricharanaB
Advocate
Advocate

Hi,

I'm using the code below (which was posted on this forum, I forgot who posted this.)

When I try to deep clone some blocks, I'm getting eNotApplicable error at line cloned.TransformBy(transform) for some blocks.

 

I've noticed that this does not happen to all blocks.

When the code works, the loop foreach (IdPair pair in mapping) iterates only one time.

When the code fails, foreach loops many more times in one of which it fails. (result view shown when it fails in below image (Image 1). 

I've also shown the cloned object which fails.

 

Could any one please help?

 

public static void DoDatabaseDeepClone(Transaction tran,
ObjectId entId, Matrix3d transform)
{                
    //using (var tran =
    //entId.Database.TransactionManager.StartTransaction())
    //{
    var ent = (Entity)tran.GetObject(entId, OpenMode.ForRead);

    ObjectId ownerId = ent.OwnerId;
    var mapping = new IdMapping();
    ObjectIdCollection ids = new ObjectIdCollection(    
        new ObjectId[] { entId });
    entId.Database.DeepCloneObjects(ids, ownerId, mapping, false);
    foreach (IdPair pair in mapping)
    {
        if (pair.IsCloned)
        {
            var cloned = tran.GetObject(
                pair.Value, OpenMode.ForRead) as Entity;
            if (cloned != null)
            {
                cloned.UpgradeOpen();
                cloned.TransformBy(transform);                            
            }
        }
    }

    // tran.Commit();
    //}
}

 

I'm calling the method like this.

if (bt.Has(blockName))
{
    BlockReference original = BlockMethods.InsertBlock(blockName, startPoint, modelSpace, db, trans);
    //modelSpace.AppendEntity(original);
   // trans.AddNewlyCreatedDBObject(original, true);
    Point3d insertPoint = startPoint;

    for (int i = 0; i < columns; i++)
    {
        insertPoint = startPoint + new Vector3d(1, 0, 0) * 2800 * i;
        Vector3d moveVector = startPoint.GetVectorTo(insertPoint);
        if (i != 0)
        {
            Matrix3d translation = Matrix3d.Displacement(moveVector);
            BlockMethods.DoDatabaseDeepClone(trans, original.ObjectId, translation);
        }

        for (int j = 1; j < rows; j++)
        {
            //insertTower(blockName, insertPoint, modelSpace, db, trans);
            insertPoint = insertPoint + new Vector3d(0, 1, 0) * 950;
            Vector3d moveVec = startPoint.GetVectorTo(insertPoint);
            Matrix3d tranmat = Matrix3d.Displacement(moveVec);
            BlockMethods.DoDatabaseDeepClone(trans, original.ObjectId, tranmat);
        }

    }

 

Image 1

ShricharanaB_0-1712914424182.png

Image 2 

ShricharanaB_1-1712914572080.png

 

Thanks in advance!

 

0 Likes
Reply
Accepted solutions (2)
553 Views
5 Replies
Replies (5)

_gile
Mentor
Mentor
Accepted solution

Here's an example of doing an array by deep cloning an existing block reference.

public static void BlockArray(
    BlockReference source,
    Transaction tr,
    int numRows,
    double rowHeight, 
    int numColumns, 
    double columnWidth)
{
    var db = source.Database;
    var sourceId = source.ObjectId;
    var ownerid = source.OwnerId;
    var ids = new ObjectIdCollection { sourceId };
    var displaceX = Vector3d.XAxis * columnWidth;
    var displaceY = Vector3d.YAxis * rowHeight;
    for (int i = 0; i < numRows; i++)
    {
        for (int j = 0; j < numColumns; j++)
        {
            if (i == 0 && j == 0) continue;
            var mapping = new IdMapping();
            db.DeepCloneObjects(ids, ownerid, mapping, false);
            var br = (BlockReference)tr.GetObject(mapping[sourceId].Value, OpenMode.ForWrite);
            br.TransformBy(Matrix3d.Displacement(i * displaceY + j * displaceX));
        }
    }
}

 

But, if as you show in your code, you insert the first block just before doing the array, you do not need to deep clone it simply insert all the block references at different insertion points.

public static void BlockArray(
    Database db, 
    string blockName, 
    Point3d startPoint,
    int numRows,
    double rowHeight, 
    int numColumns, 
    double columnWidth)
{
    using (var tr = db.TransactionManager.StartTransaction())
    {
        var blockTable = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
        if (!blockTable.Has(blockName)) return;
        var btrId = blockTable[blockName];
        var displaceX = Vector3d.XAxis * columnWidth;
        var displaceY = Vector3d.YAxis * rowHeight;
        var curSpace = (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
        for (int i = 0; i < numRows; i++)
        {
            var startRowPoint = startPoint + i * displaceY;
            for (int j = 0; j < numColumns; j++)
            {
                var insertionPoint = startRowPoint + j * displaceX;
                var br = new BlockReference(insertionPoint, btrId);
                curSpace.AppendEntity(br);
                tr.AddNewlyCreatedDBObject(br, true);
            }
        }
        tr.Commit();
    }
}


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub

ActivistInvestor
Advisor
Advisor
Accepted solution

Although you should follow @_gile 's advice for your specific purpose, it might help to explain what your code is doing wrong. First, a deep clone operation can clone more than just entities, and can clone other objects along with the ones you specified to be cloned.

 

It can also clone extension dictionaries, and anything they contain that's attached to the object you're trying to clone, so you can't simply assume that every entry in an IdMapping is an entity, or one of the entities that you're deep cloning. You have to look at the IsPrimary property to determine if the entry represents one of the objects whose Id was passed to the DeepCloneObjects() method, and act only if that property's value is true (and/or take some other action for non-primary objects that were incidentally cloned along with the primary objects). The 'primary' objects are the ones whose Ids were passed to DeepCloneObjects(), and non-primary objects are the ones that went along for the ride.

 

    foreach (IdPair pair in mapping)
    {
        if (pair.IsCloned)
        {
 

 

Should be:

 

    foreach (IdPair pair in mapping)
    {
        if (pair.IsCloned && pair.IsPrimary)
        {
 

 

ShricharanaB
Advocate
Advocate

@_gile Thank you for the reply.

 

I had first tried creating new block reference for each required position. But, creating a new block reference for each position works fast with static blocks but for dynamic blocks I found it significantly slow, since I'll have to modify the parameters for each new block reference (in my case all the dynamic blocks will have same parameter values). This modifying of parameters for each instance makes it very slow compared to deep cloning. That is why I switched to deep cloning. 

These blocks are attributed as well. 

 

 

0 Likes

ShricharanaB
Advocate
Advocate

Hi @ActivistInvestor ,

 

Thank you for your reply.

 

I'm not sure what the code is doing wrong. As I said in the main post, for some blocks,  it is working and for some it gives eNotApplicable error when applying transform (in which case the mapping has multiple values as opposed to cases when the code works - in this case the mapping has only one value). Both are dynamic and has attributes either direct or through nested attributed blocks. 

 

if (pair.IsCloned && pair.IsPrimary)
        {

 

This seems to have helped in one case where I was getting the error. I'll have to check for other blocks as well.

 

I'll get back after some testing.

 

 

0 Likes

_gile
Mentor
Mentor

@ShricharanaB wrote:

@_gile Thank you for the reply.

 

I had first tried creating new block reference for each required position. But, creating a new block reference for each position works fast with static blocks but for dynamic blocks I found it significantly slow, since I'll have to modify the parameters for each new block reference (in my case all the dynamic blocks will have same parameter values). This modifying of parameters for each instance makes it very slow compared to deep cloning. That is why I switched to deep cloning. 

These blocks are attributed as well. 

 

 


The first code I posted does deep cloning and uses mapping[sourceId].Value to get the ObjectId of the cloned block reference as there's one object cloned at once.

var mapping = new IdMapping();
db.DeepCloneObjects(ids, ownerid, mapping, false);
var br = (BlockReference)tr.GetObject(mapping[sourceId].Value, OpenMode.ForWrite);
br.TransformBy(Matrix3d.Displacement(i * displaceY + j * displaceX));


Gilles Chanteau
Programmation AutoCAD LISP/.NET
GileCAD
GitHub