C# Entity Clone method

I have AutoCAD code that uses the Clone() method on an Entity.

AcDb.Entity copiedEnt = ent.Clone() as AcDb.Entity;

This fails in BricsCAD. Rather spectacularly actually. BricsCAD crashes.

Is this a bug, or is there a subtley in the way I have to use it?

Comments

  • Sakko
    edited December 2022
    have a look at this thread and try the source code...
    I make use of the ent.clone() method without any problems !
    https://forum.bricsys.com/discussion/37398/testing-the-net-solid3d-createsculptedsolid-method-via-the-implemented-surfsculptx-command
  • Thanks Konstantin.

    I had a look at the article, and I can't see anything that I'm doing that is amazingly different.

    When I get the selected entities, I'm using much the same code as you, just with an additional 'false' parameter for openErased. Necessary/unnecessary...? Shrug.

    AcDb.Entity ent = tr.GetObject(id, AcDb.OpenMode.ForRead, false) as AcDb.Entity;

    From each of those Entity objects, I then use a slightly different structure, but it should have the same effect.

    AcDb.Entity copiedEnt = ent.Clone() as AcDb.Entity;

    I'm doing a couple of things to copiedEnt once I've got it. Hence I'm not adding it straight to the return list.

    This is for that old chestnut Halo, that gets created/borrowed/begged/stolen at some point.

    I've attached the code and the supporting methods it uses. Just in case I'm doing something I shouldn't be doing.

    If you get this one to work as well (re: multileader)... :|
  • I have a suspicion this is related...

    I've moved onto another piece of code that also makes use of the GetSelectedEntities method in the Halo_Code.txt file above.

    In the foreach ObjectId loop, the entities are being retrieve using OpenMode.ForRead. They're showing up as "IsReadEnabled = true" right up until "return ents;".

    I added the tr.Commit() in case it made some sort of difference. It didn't.

    Multiple object Ids:
    ObjectIds

    Entity IsReadEnabled = true:
    ent_IsReadEnabled_true

    Entity List IsReadEnabled = true:


    Return Entity List IsReadEnabled = false:


    This does not happen when the same code is run in AutoCAD.

    The problem with them coming back as IsReadEnabled = false is that you can't get properties off them, like Area or Handle.

    And BricsCAD eventually crashes out.
  • It's definitely related. A similar thing happens in Halo. I've noticed that the switch from true to false can happen at different times, but it still happens.

    GetSelectedEntities might return them as true, but by the time I do something like ids.Add(ent.ObjectId), IsReadEnabled is back to false, ent.ObjectId fails and there's a lot of this going on in ent:

    IsReadEnabled_false_Exceptions
  • bad code, everything in your ents list is dead as it belongs to the transation. just use id.open
  • Sakko
    edited December 2022
    like Daniel says, you should use the object ids of the selected objects, in one transaction having entities selected and cloned open for all operations !
    At the bottom you find the function GetSelectedEntities(), which returns an array of selected object ids...not a list of closed entities !
    Try the following code...it works for me
    this is the result dwg, original text white, cloned red....




    __________________________________Command HaloHalo _______________________________________________
    public class MRHalo
    {
    [AcRt2.CommandMethod("HaloHalo")]
    public static void HaloHalo()
    {
    string haloLayer = "halotext255";

    AcAp.Document doc = AcAp.Application.DocumentManager.MdiActiveDocument;
    AcDb.Database db = doc.Database;
    AcEd.Editor ed = doc.Editor;
    Transaction tr = db.TransactionManager.StartTransaction();

    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead, false);
    BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite, false);


    bool cancelled;
    AcEd.SelectionSet ss = Utilities.SelTextMText(out cancelled);

    if (!cancelled)
    {
    LayerTools.AddALayer(haloLayer, 255, "Continuous", 8, "White_200", "Halo background");

    //List originalEnts = Utilities.GetSelectedEntities(ss);
    ObjectId[] originalEnts = Utilities.GetSelectedEntities(ss);
    AcAp.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n Number of selected entities: {0}", originalEnts.Length);

    //List copiedEnts = Utilities.CopySelectionSet(originalEnts, true, haloLayer);

    List copiedEnts = new List();

    for (int i = 0; i < originalEnts.Length; i++)
    {
    // open the original entity (Text or Mtext)...
    Entity orgEnt = tr.GetObject(originalEnts[i], OpenMode.ForRead) as Entity;
    AcDb.Entity copiedEnt = orgEnt.Clone() as AcDb.Entity;

    btr.AppendEntity(copiedEnt);
    tr.AddNewlyCreatedDBObject(copiedEnt, true);

    copiedEnt.Layer = haloLayer;
    copiedEnt.LineWeight = AcDb.LineWeight.ByLayer;
    copiedEnt.ColorIndex = 1;

    copiedEnts.Add(copiedEnt);
    }

    AcDb.DrawOrderTable dot = GetModelSpaceDrawOrderTable(db);

    for (int cei = 0; cei < copiedEnts.Count; cei++)
    {
    AcDb.Entity ent = copiedEnts[cei];
    AcDb.ObjectIdCollection ids = new AcDb.ObjectIdCollection();
    ids.Add(ent.ObjectId);
    dot.MoveBelow(ids, originalEnts[cei]);
    }

    tr.Commit();

    AcAp.Application.DocumentManager.MdiActiveDocument.Editor.WriteMessage("\n Number of cloned entities: {0}", copiedEnts.Count);

    }

    Utilities.SendACommand("Regenall ");
    }

    static AcDb.DrawOrderTable GetModelSpaceDrawOrderTable(AcDb.Database CurrDBase)
    {
    AcDb.DrawOrderTable dot = null;
    using (AcDb.Transaction t = CurrDBase.TransactionManager.StartTransaction())
    {
    AcDb.BlockTable bt = t.GetObject(CurrDBase.BlockTableId, AcDb.OpenMode.ForRead) as AcDb.BlockTable;
    AcDb.BlockTableRecord btr = t.GetObject(bt[AcDb.BlockTableRecord.ModelSpace], AcDb.OpenMode.ForRead) as AcDb.BlockTableRecord;
    dot = t.GetObject(btr.DrawOrderTableId, AcDb.OpenMode.ForWrite) as AcDb.DrawOrderTable;
    t.Commit();
    }
    return dot;
    }

    }
    _________________________________ GetSelectedEntities _________________________________________________

    public static ObjectId[] GetSelectedEntities(AcEd.SelectionSet ObjectSelection)
    {
    AcDb.ObjectId[] ids = ObjectSelection.GetObjectIds();
    // return an objectid array of selected entities!
    return ids;
    }
  • Thanks to you both. The nested transaction hint was helpful.

    If anything I'm learning that either AutoCAD is lazier, or BricsCAD is more fussy. Not sure which way to lean there.
  • Its_Alive
    edited December 2022
    It’s in AutoCAD the docs.
    “All open objects opened during a transaction are closed at the end of the transaction.”
    I do this, I just make sure to dispose of the list. doing this a lot? Create a disposable entity list, container

    public static List<_AcDb.Entity> getSelectionForRead(_AcEd.SelectionSet sel)
    {
    List<_AcDb.Entity> ents = new List<_AcDb.Entity>();
    foreach(_AcDb.ObjectId id in sel)
    {
    var ent = id.Open(OpenMode.ForRead, false) as _AcDb.Entity;
    if (ent != null)
    ents.Add(ent);
    }
    return ents;
    }