Pular para o conteúdo principal

Transformando o conteúdo de um Layer em Bloco

Esses dias do fim do ano uma pessoa me procurou por e-mail pedindo uma solução para transformar o conteúdo de um Layer em um bloco.

Resolvi ajuda-lo. Realmente nunca tinha me deparado com esse tipo de problema antes por isso fiz uma pesquisa nos blog’s oficiais da Autodesk por soluções parecidas com as que eu necessitava.

Dividi a busca em duas partes em que eu realmente sabia que iria encontrar: (i) criar um bloco e coloca-lo na tela e (ii) selecionar todos os objetos do Layer.

Buscando, encontrei estes dois artigos:

Creating an AutoCAD block using .NET


Neste post de 2010 Kean Walmsley apresenta um código plenamente funcional para criar alguns objetos na tela e os transforma em um bloco já com entradas personalizadas para um nome do bloco pela linha de comando.

Finding all the AutoCAD entities on a particular layer using .NET

Neste outro post de 2008 Kean Walmsley apresenta uma forma que selecionar todos os objetos de um Layer. Inclusive cirando uma função específica para essa finalidade o que é bem útil.

Executei os dois códigos e tive sucesso em executá-los então parti para a solução de juntar os dois códigos em uma solução: selecionando os objetos do Layer depois transformando-os em um bloco.


    public class Commands
    {

        private static ObjectIdCollection
           GetEntitiesOnLayer(string layerName)
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Editor ed = doc.Editor;

            // Build a filter list so that only entities
            // on the specified layer are selected

            TypedValue[] tvs = new TypedValue[1] { new TypedValue( (int)DxfCode.LayerName, layerName)
          };

            SelectionFilter sf = new SelectionFilter(tvs);
            PromptSelectionResult psr = ed.SelectAll(sf);

            if (psr.Status == PromptStatus.OK)
                return
                  new ObjectIdCollection(psr.Value.GetObjectIds());
            else
                return new ObjectIdCollection();
        }


        [CommandMethod("layer2block")]
        public void CreateBlock()
        {
            Document doc =
              Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            Transaction tr =
              db.TransactionManager.StartTransaction();
            using (tr)
            {
                // Get the block table from the drawing

                BlockTable bt =
                  (BlockTable)tr.GetObject(
                    db.BlockTableId,
                    OpenMode.ForRead
                  );

                // Check the block name, to see whether it's
                // already in use

                PromptStringOptions pso =
                  new PromptStringOptions("\nEntre com o Nome do Bloco: ");
                pso.AllowSpaces = true;

                // A variable for the block's name

                string blkName = "";

                do
                {
                    PromptResult pr = ed.GetString(pso);

                    // Just return if the user cancelled
                    // (will abort the transaction as we drop out of the using
                    // statement's scope)

                    if (pr.Status != PromptStatus.OK)
                        return;

                    try
                    {
                        // Validate the provided symbol table name

                        SymbolUtilityServices.ValidateSymbolName(
                          pr.StringResult,
                          false
                        );

                        // Only set the block name if it isn't in use

                        if (bt.Has(pr.StringResult))
                            ed.WriteMessage("\nEsse bloco já existe.");
                        else
                            blkName = pr.StringResult;
                    }
                    catch
                    {
                        // An exception has been thrown, indicating the
                        // name is invalid

                        ed.WriteMessage("\nNome do bloco Inválido.");
                    }

                } while (blkName == "");


                PromptResult pr1 = ed.GetString("\nEnter name of layer: ");

                ObjectIdCollection ents = GetEntitiesOnLayer(pr1.StringResult);

                // Create our new block table record...

                BlockTableRecord btr = new BlockTableRecord();

                // ... and set its properties

                btr.Name = blkName;

                // Add the new block to the block table

                bt.UpgradeOpen();
                ObjectId btrId = bt.Add(btr);
                tr.AddNewlyCreatedDBObject(btr, true);

                // Add some lines to the block to form a square
                // (the entities belong directly to the block)


                IdMapping mapping = new IdMapping();
                db.DeepCloneObjects(ents, btrId, mapping, false);


                // Add a block reference to the model space

                BlockTableRecord ms =
                  (BlockTableRecord)tr.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForWrite);

                BlockReference br =
                  new BlockReference(Point3d.Origin, btrId);

                ms.AppendEntity(br);
                tr.AddNewlyCreatedDBObject(br, true);

                // Commit the transaction

                tr.Commit();

                // Report what we've done

                ed.WriteMessage(
                  "\nCreated block named \"{0}\" containing {1} entities.",
                  blkName, ents.Count
                );
            }
        }



    }


Mantive exatamente a mesma estrutura inclusive com a solução de utilizar a função GetEntitiesOnLayer que já existia antes.

Criei uma nova entrada do prompt para que o usuário entre com o nome do Layer onde estão os objetos e removi a parte que construía os objetos.

Feito isso, a coleção de objetos do Layer passa para uma varável chamada ents que é adicionada ao BlockTableRecord do bloco criado.

O único inconveniente dessa solução é que DeepCloneObjects faz um clone dos dos objetos selecionados e os associa ao bloco criado com um ponto de base.

A outra forma de fazer isso sem esta função de clonagem seria criar o bloco, incluir os objetos internamente ao bloco e depois colocar uma Block Reference do bloco no desenho com o ponto de base escolhido. Daria bem mais trabalho e para fazer isso teríamos que abrir e fechar o Transaction várias vezes porque a primeira você cria o bloco e para ele aparecer no banco de dados precisa que a Transaction do Banco de Dados (Data Base) seja atualizada. Existe o recurso do Commit da Transaction porém nas versões mais antigas do AutoCAD realizar esses processos sistematicamente causavam colisões e violações de execução até mesmo causando o fechamento do AutoCAD.

A melhor alternativa seria depois de criar o bloco excluir os objetos que estão no Layer mas não encontrei ainda uma solução para isso.

Finalizando, fica então mais um código de referência, espero ter ajudado.

Comentários