Author Topic: WblockCloneObjects - best way to change layer when using  (Read 4576 times)

0 Members and 1 Guest are viewing this topic.

Sheldon1874

  • Mosquito
  • Posts: 19
WblockCloneObjects - best way to change layer when using
« on: October 30, 2013, 10:07:56 AM »
Hi,

First post - so be gentle.

All I am after doing is to clone all contents of a layer in one dwg to another dwg on a different layer.  What I have so far is a csv file with layersFrom,layersTo info.  Active doc with the destination layers, a closed dwg with all the objects.

Using Wblockcloneobjects I can get all the objects copied across to the active doc no problem it just the change layer to destination bit I need.

Do commit the transaction then open then obj and then change?


WILL HATCH

  • Bull Frog
  • Posts: 450
Re: WblockCloneObjects - best way to change layer when using
« Reply #1 on: October 30, 2013, 11:57:29 AM »
Throw some code on the board and somebody will go over it for you.  If you just want a complete solution it's all here in peices...

Sheldon1874

  • Mosquito
  • Posts: 19
Re: WblockCloneObjects - best way to change layer when using
« Reply #2 on: October 30, 2013, 12:15:14 PM »
Thanks for the help.

Here is my code so far:

Public Function SwapLayerContents(ByVal acdocDest As Document, ByVal fname As String, ByVal strSourceLayer As String, ByVal strDestLayer As String, ByVal bCopyBlocks As Boolean) As Boolean
        Dim bDone = False
        Dim acDestDB As Database = acdocDest.Database
        Dim acSourceBlockTable As BlockTable
        Dim acSourceModelSpace As BlockTableRecord
        Dim acDestBlockTable As BlockTable
        Dim acDestModelSpace As BlockTableRecord
        Dim acSourceLayerTable As LayerTable
        Dim acDestLayerTable As LayerTable
        Dim acObjID_New As ObjectIdCollection = New ObjectIdCollection()
        Dim acIDMap As IdMapping = New IdMapping()


        Try
   'Lock Doc
            Using acDocLock As DocumentLock = acdocDest.LockDocument()
      'DBase
                Using acSourceDB As New Database(False, True)
      'Start Trans
                    Using acSourceTrans As Transaction = acSourceDB.TransactionManager.StartTransaction()
                        acSourceDB.ReadDwgFile(fname, IO.FileShare.Read, True, "")
         'Start Trans
                        Using acDestTrans As Transaction = acDestDB.TransactionManager.StartTransaction()
                           
            acSourceBlockTable = acSourceTrans.GetObject(acSourceDB.BlockTableId, OpenMode.ForRead)
                               acSourceModelSpace = acSourceTrans.GetObject(acSourceBlockTable(BlockTableRecord.ModelSpace), OpenMode.ForRead)
                               acSourceLayerTable = acSourceTrans.GetObject(acSourceDB.LayerTableId, OpenMode.ForRead)
                               acDestBlockTable = acDestTrans.GetObject(acDestDB.BlockTableId, OpenMode.ForWrite)

                           acDestModelSpace = acDestTrans.GetObject(acDestBlockTable(BlockTableRecord.ModelSpace), OpenMode.ForWrite)
                         acDestLayerTable = acDestTrans.GetObject(acDestDB.LayerTableId, OpenMode.ForRead)
            'Test Layers
                               If acSourceLayerTable.Has(strSourceLayer) And acDestLayerTable.Has(strDestLayer) Then

                                For Each acObjID As ObjectId In acSourceModelSpace

                                    Dim acEnt As Entity = acSourceTrans.GetObject(acObjID, OpenMode.ForRead)

                                    If acEnt.Layer = strSourceLayer Then

                                        acObjID_New.Add(acObjID)

                                    End If

                                Next acObjID

                                Dim myMap As New IdMapping

            'SDK docs seem to suggest that mapping can ignore the layer table here
                                acSourceDB.WblockCloneObjects(acObjID_New, acDestModelSpace.ObjectId, myMap, DuplicateRecordCloning.Ignore, False)

                                acDestTrans.Commit()

                                bDone = True
                            Else
                                bDone = False
                            End If
                        End Using
                    End Using
                End Using
            End Using
        Catch ex As Exception
            MsgBox("Error  " & ex.Message & vbCrLf & vbTab & ex.StackTrace, MsgBoxStyle.Critical)
            bDone = False
        End Try


        Return bDone
    End Function

<CommandMethod("swapLayer")> _
    Public Sub swapLayer()
        Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument
        Dim ed As Editor = doc.Editor
        Dim fname As String = "c:\FilesBin\Source.dwg"

        ed.WriteMessage(vbCr & "Result = ++{0}--", SwapLayerContents(doc, fname, "SUR_BANK_BOTTOM", "SUR_BANKS", False))

    End Sub

Seems to me that I must be missing something basic or coming at this from the totally wrong angle?

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: WblockCloneObjects - best way to change layer when using
« Reply #3 on: October 30, 2013, 01:23:27 PM »
Quote
'SDK docs seem to suggest that mapping can ignore the layer table here
                                acSourceDB.WblockCloneObjects(acObjID_New, acDestModelSpace.ObjectId, myMap, DuplicateRecordCloning.Ignore, False)

Objects will be created in the new drawing using the same layer they were cloned from.  Hence "cloning" makes an identical copy.  The IdMapping parameter will contain a reference of Original ObjectId to the Cloned ObjectId.  You could loop through the mapping and change the layer of each cloned object to the desired layer.
Revit 2019, AMEP 2019 64bit Win 10

dgorsman

  • Water Moccasin
  • Posts: 2437
Re: WblockCloneObjects - best way to change layer when using
« Reply #4 on: October 30, 2013, 01:38:58 PM »
Minor point on post formatting - code blocks:

Code: [Select]
Some code here;
Makes things easier to read.
If you are going to fly by the seat of your pants, expect friction burns.

try {GreatPower;}
   catch (notResponsible)
      {NextTime(PlanAhead);}
   finally
      {MasterBasics;}

owenwengerd

  • Bull Frog
  • Posts: 451
Re: WblockCloneObjects - best way to change layer when using
« Reply #5 on: October 30, 2013, 01:52:09 PM »
Maybe it would be easiest to rename the layer in the source drawing, then perform the clone, then revert the layer name after cloning is completed.

Sheldon1874

  • Mosquito
  • Posts: 19
Re: WblockCloneObjects - best way to change layer when using
« Reply #6 on: October 30, 2013, 02:20:41 PM »
Thing is I need to map something like 100 layers in the source to 20 in the destination.

owenwengerd

  • Bull Frog
  • Posts: 451
Re: WblockCloneObjects - best way to change layer when using
« Reply #7 on: October 30, 2013, 03:27:49 PM »
Thing is I need to map something like 100 layers in the source to 20 in the destination.

In that case, do the reverse. Clone one layer at a time, then change the cloned layer name in the destination to match the next source layer between each clone operation. At the end, all cloned objects will reference the same layer, and at that point you can rename the destination layer to whatever you want.

Sheldon1874

  • Mosquito
  • Posts: 19
Re: WblockCloneObjects - best way to change layer when using
« Reply #8 on: October 30, 2013, 04:13:15 PM »
Thanks.
Code: [Select]
        Using acDBObj As DBObject = acSourceTrans.GetObject(acObjID, OpenMode.ForWrite)
                                        Dim acEnt As Entity = CType(acDBObj, Entity)
                                        If acEnt.Layer = strDestLayer Then

                                            acEnt.Layer = strDestLayer
                                            acObjIDs.Add(acObjID)

                                        End If
       End Using
Seems to be all rather simple in theory but boy is it more complex/rigorous than VBA.

Think I have used the correct tagging for message format.

Jeff H

  • Needs a day job
  • Posts: 6150
Re: WblockCloneObjects - best way to change layer when using
« Reply #9 on: October 30, 2013, 04:32:56 PM »
I know this sounds arse-backwards, but depending on your situation and when I had just layer information in text, .csv, etc.. file, and had a drawing with new layers already defined, it seemed easier to make a map of old and new layer names.
 
Rename the new layers to the old layers name then clone the objects and then rename back to new names. Just so did not have to worry about setting all the properties.
 
And I just realized how I took an extra step and did not think through and should asked owenengerd.
Maybe it would be easiest to rename the layer in the source drawing, then perform the clone, then revert the layer name after cloning is completed.

 
Thing is I need to map something like 100 layers in the source to 20 in the destination.

Even if you had 1,000 layers to be mapped to 100 I would not worry about it unless someone will die if does not finish under a small fraction of a second.
 
 

kaefer

  • Guest
Re: WblockCloneObjects - best way to change layer when using
« Reply #10 on: October 30, 2013, 04:43:43 PM »
rename back

Don't. Abort the transaction.

WILL HATCH

  • Bull Frog
  • Posts: 450
Re: WblockCloneObjects - best way to change layer when using
« Reply #11 on: October 30, 2013, 05:13:52 PM »
here's my take on it:
Code - C#: [Select]
  1.         [CommandMethod("TestMapping")]
  2.         public void TestMapping()
  3.         {
  4.             Document doc = Application.DocumentManager.MdiActiveDocument;
  5.             Editor ed = doc.Editor;
  6.             Database db = doc.Database;
  7.             //avoiding a file I/O but each ReadLine would result in an item from this array
  8.             //assumes you create a .CSV file where the first layer will be the owner of the other layers
  9.             string[] lines = { "A,A1,A2,A3", "B,B1,B2,B3" };
  10.             string path = @"C:\test\Test.dwg";
  11.             using (Database ndb = new Database(false,true))
  12.             {
  13.                 ndb.ReadDwgFile(path, FileShare.Read, true, "");
  14.                 ndb.CloseInput(true);
  15.                 using (Transaction tr = db.TransactionManager.StartTransaction())
  16.                 using (Transaction ntr = ndb.TransactionManager.StartTransaction())
  17.                 {
  18.                     //get modelspace for both databases
  19.                     BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  20.                     BlockTableRecord btr = (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
  21.                     BlockTable nbt = (BlockTable)ntr.GetObject(ndb.BlockTableId, OpenMode.ForRead);
  22.                     BlockTableRecord nbtr = (BlockTableRecord)ntr.GetObject(nbt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
  23.                     //get layer table for side database and create id map for merging layers
  24.                     LayerTable nlt = (LayerTable)ntr.GetObject(ndb.LayerTableId, OpenMode.ForRead);
  25.                     Dictionary<ObjectId, ObjectId> LayerMap = new Dictionary<ObjectId, ObjectId>();
  26.                     foreach (string line in lines)
  27.                     {
  28.                         string[] parsedLine = line.Split(',');
  29.                         foreach (string name in parsedLine)
  30.                             try
  31.                             {
  32.                                 if (nlt.Has(name) && nlt.Has(parsedLine[0]))
  33.                                     LayerMap.Add(nlt[name], nlt[parsedLine[0]]);
  34.                                 else
  35.                                     ed.WriteMessage("\nLayer {0} not in source database", name);
  36.                             }
  37.                             catch { ed.WriteMessage("\nTried to add {0} to layer map more than once", name); }
  38.                     }
  39.                     //go through side database modelspace, merging layers and adding them to our list to clone
  40.                     ObjectIdCollection cloners = new ObjectIdCollection();
  41.                     foreach (ObjectId id in nbtr)
  42.                     {
  43.                         Entity ent = ntr.GetObject(id, OpenMode.ForRead) as Entity;
  44.                         if (LayerMap.ContainsKey(ent.LayerId))
  45.                         {
  46.                             ent.UpgradeOpen();
  47.                             ent.LayerId = LayerMap[ent.LayerId];
  48.                             cloners.Add(id);
  49.                         }
  50.                     }
  51.                     //do the deep clone
  52.                     IdMapping im = new IdMapping();
  53.                     ndb.WblockCloneObjects(cloners, btr.ObjectId, im, DuplicateRecordCloning.Ignore, false);
  54.                     tr.Commit();
  55.                     ntr.Commit();//yes we changed things in the database for our test drawing, but we are not saving so commit is faster
  56.                 }
  57.             }
  58.         }

fixo

  • Guest
Re: WblockCloneObjects - best way to change layer when using
« Reply #12 on: October 31, 2013, 02:46:32 AM »

Seems to be all rather simple in theory but boy is it more complex/rigorous than VBA.

Think I have used the correct tagging for message format.

Try this one, rename layers and source drawing name
Code: [Select]
   <CommandMethod("wbex", CommandFlags.Session Or CommandFlags.Redraw)> _
        Public Shared Sub testWblockFromExternal()
            InsertFromDwgFile("C:\Test\sourcelayers.dwg", "Layer1", "Layer2")' your source file name
        End Sub

        Public Shared Sub InsertFromDwgFile(fileName As String, oldLayer As String, newLayer As String)

            Try
                Dim doc As Document = Autodesk.AutoCAD.ApplicationServices.Application.DocumentManager.MdiActiveDocument

                Dim ed As Editor = doc.Editor

                Dim ptNew As Point3d = Point3d.Origin

                Using doc.LockDocument()
                    Dim currentDb As Database = doc.Database

                    Dim ids As New ObjectIdCollection()

                    Dim idMap As New IdMapping()

                    Using currTr As Transaction = doc.TransactionManager.StartTransaction()
                        Dim currLt As LayerTable = DirectCast(currTr.GetObject(currentDb.LayerTableId, OpenMode.ForRead), LayerTable)
                        If Not currLt.Has(newLayer) Then
                            Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(String.Format("Layer ""{0}"" does not exist in drawing: {1}", newLayer, doc.Name))
                            ' you may want to create this layer here instead of exiting a program
                            Return
                        End If

                        Dim currBt As BlockTable = DirectCast(currTr.GetObject(currentDb.BlockTableId, OpenMode.ForRead), BlockTable)
                        ' or get a current tab BlockTableRecord
                        Dim currBtr As BlockTableRecord = DirectCast(currTr.GetObject(currentDb.CurrentSpaceId, OpenMode.ForRead), BlockTableRecord)
                        ' or get a model space BlockTableRecord
                        ' BlockTableRecord currBtr = (BlockTableRecord)tr.GetObject(currBt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
                        Using sourceDb As New Database(False, False)
                            sourceDb.ReadDwgFile(fileName, FileOpenMode.OpenForReadAndAllShare, False, "")

                            Using sourcetr As Transaction = sourceDb.TransactionManager.StartTransaction()
                                Dim sourceLt As LayerTable = DirectCast(sourcetr.GetObject(sourceDb.LayerTableId, OpenMode.ForRead), LayerTable)

                                If Not sourceLt.Has(oldLayer) Then


                                    Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(String.Format("Layer ""{0}"" does not exist in drawing: {1}", oldLayer, fileName))

                                    Return
                                End If

                                Dim sourceIds As New ObjectIdCollection()

                                Using sourceBtr As BlockTableRecord = DirectCast(sourcetr.GetObject(sourceDb.CurrentSpaceId, OpenMode.ForRead), BlockTableRecord)
                                    '.Open(OpenMode.ForRead)

                                    If sourceBtr IsNot Nothing Then
                                        sourceIds = New ObjectIdCollection()
                                        For Each id As ObjectId In sourceBtr
                                            ' if (!id.IsNull && id.IsValid)//optional
                                            Dim obj As Entity = CType(id.GetObject(OpenMode.ForRead, False, False), Entity)
                                            If obj.Layer = oldLayer Then
                                                sourceIds.Add(id)
                                            End If
                                        Next
                                        sourcetr.Commit()
                                    End If
                                    If sourceIds.Count <> 0 Then
                                        idMap = New IdMapping()
                                    End If
                                    sourceDb.WblockCloneObjects(sourceIds, currBtr.ObjectId, idMap, DuplicateRecordCloning.Ignore, False)
                                End Using
                            End Using
                        End Using

                        For Each ip As IdPair In idMap
                            If ip.IsPrimary Then
                                ids.Add(ip.Value)
                            End If
                        Next

                        Dim copied As ObjectId() = New ObjectId(ids.Count - 1) {}

                        ids.CopyTo(copied, 0)

                        ed.SetImpliedSelection(copied)

                        Dim psr As PromptSelectionResult = ed.SelectImplied()
                        ' borrowed from Kean Walmsley:
                        Dim ppr As PromptPointResult = ed.Drag(psr.Value, vbLf & "Select new point position: ", _
                                                               Function(pt As Point3d, ByRef mtx As Matrix3d)
                                                                   If ptNew = pt Then
                                                                       Return SamplerStatus.NoChange
                                                                   Else
                                                                       mtx = Matrix3d.Displacement(ptNew.GetVectorTo(pt))
                                                                   End If
                                                                   Return SamplerStatus.OK
                                                               End Function)

                        If ppr.Status = PromptStatus.OK Then

                            Dim mtx As Matrix3d = Matrix3d.Displacement(ptNew.GetVectorTo(ppr.Value))
                            For Each sobj As SelectedObject In psr.Value
                                Dim en As Entity = TryCast(DirectCast(currTr.GetObject(sobj.ObjectId, OpenMode.ForRead, False), Entity), Entity)
                                If en IsNot Nothing Then
                                    en.UpgradeOpen()
                                    en.TransformBy(mtx)
                                    en.Layer = newLayer
                                    en.ColorIndex = 256
                                    ' color bylayer
                                    en.DowngradeOpen()
                                End If
                            Next
                        End If
                        ' unhighlight selection
                        ed.SetImpliedSelection(New ObjectId() {})

                        currTr.Commit()
                    End Using
                End Using

            Catch ex As Autodesk.AutoCAD.Runtime.Exception
                Autodesk.AutoCAD.ApplicationServices.Application.ShowAlertDialog(ex.Message + vbLf + ex.StackTrace)
            End Try
        End Sub

MexicanCustard

  • Swamp Rat
  • Posts: 705
Re: WblockCloneObjects - best way to change layer when using
« Reply #13 on: October 31, 2013, 07:48:32 AM »
What about not using WBlockClone?  Since you're stepping through the whole modelspace anyway.  Just Clone each object, assign the new layer and append it to the destination ModelSpace.
Revit 2019, AMEP 2019 64bit Win 10

Sheldon1874

  • Mosquito
  • Posts: 19
Re: WblockCloneObjects - best way to change layer when using
« Reply #14 on: November 01, 2013, 01:02:33 PM »
Thanks all for your input.

The main problem I have is two fold - .net is new to me and I'm a Topographic Land Surveyor.  That said I have a fair amount of VBA experience and a degree in communications (the engineering type).

Taking all your expert knowledge it would appear that my post should have started with "What is the best way to map objects from one layer to another?".  Using this method does allow us to create models with only specific sets of data - only utility traces with kerb lines for example.

Nice. The Swap "Gies it laldie"