Author Topic: .NET BLOCK Routines  (Read 16755 times)

0 Members and 2 Guests are viewing this topic.

Kerry

  • Mesozoic relic
  • Seagull
  • Posts: 10681
  • class keyThumper<T>:ILazy<T>
.NET BLOCK Routines
« on: January 27, 2010, 03:05:23 am »
LIBRARY THREAD for  AutoCAD BLOCKS
 Members are encouraged to post any functions, methods, snips regarding
AutoCAD BLOCKS in .NET : C# ,  VB , F# , Python , etc

Feel free to include comments, descriptive notes, limitations,  and images to document your post.

Please post questions in a regular thread.
« Last Edit: January 27, 2010, 03:12:17 am by Kerry Brown »
Perfection is not optional.
Everything will work just as you expect it to, unless your expectations are incorrect.
Discipline: None at all.

--> Donate<--

fixo

  • Swamp Rat
  • Posts: 856
  • My location is not in the Hitleropa
Re: .NET BLOCK Routines
« Reply #1 on: January 28, 2010, 07:15:10 am »
Here is a very basic example how to create block correct I hope :)
Code: [Select]

using System;
using System.Collections.Generic;
using System.Text;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.GraphicsInterface;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.ApplicationServices;
using AcadApp = Autodesk.AutoCAD.ApplicationServices.Application;
using Autodesk.AutoCAD.EditorInput;
................................................. 
      public static ObjectId CreateLabelBlock(string blockname)
        {
            ObjectId blkid = ObjectId.Null;
            try
            {

                Database db = HostApplicationServices.WorkingDatabase;
                using (Transaction tr = db.TransactionManager.StartTransaction())
                {

                    BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForWrite, false);
                    if (bt.Has(blockname))
                    {
                        blkid = bt[blockname];
                    }
                    else
                    {

                        BlockTableRecord btr = new BlockTableRecord();
                        btr.Name = blockname;
                        btr.Explodable = true;
                        btr.Origin = new Point3d(0, 0, 0);
                        btr.Units = UnitsValue.Inches;
                        btr.BlockScaling = BlockScaling.Any;
                        Line line = new Line(new Point3d(-2, -0.4, 0), new Point3d(2, -0.4, 0));
                        line.Layer = "0";
                        line.ColorIndex = 14;
                        btr.AppendEntity(line);
                        line = new Line(new Point3d(-2, 0.4, 0), new Point3d(2, 0.4, 0));
                        line.Layer = "0";
                        line.ColorIndex = 14;
                        btr.AppendEntity(line);
                        line = new Line(new Point3d(2, -0.4, 0), new Point3d(2, 0.4, 0));
                        line.Layer = "0";
                        line.ColorIndex = 14;
                        btr.AppendEntity(line);
                        line = new Line(new Point3d(-2, -0.4, 0), new Point3d(-2, 0.4, 0));
                        line.Layer = "0";
                        line.ColorIndex = 14;
                        btr.AppendEntity(line);
                        AcadApp.SetSystemVariable("CECOLOR", "ByBlock");
                       
                        AttributeDefinition ad = new AttributeDefinition();
                        ad.Layer = "0";
                        //by default applied a current text style
                        ad.Tag = "DETAIL";
                        ad.TextString = "X-Ladder";
                        ad.Prompt = "Detail name";

                        ad.Position = new Point3d(0, 0, 0);
                        ad.HorizontalMode = TextHorizontalMode.TextMid;
                        ad.VerticalMode = TextVerticalMode.TextVerticalMid;
                        ad.AlignmentPoint = ad.Position;
                        btr.AppendEntity(ad);

                        blkid = bt.Add(btr);
                        AcadApp.SetSystemVariable("CECOLOR", "ByLayer");
                        tr.AddNewlyCreatedDBObject(btr, true);
                    }
                    tr.Commit();
                }

            }
            catch (Autodesk.AutoCAD.Runtime.Exception ex)
            {
                AcadApp.ShowAlertDialog("ERROR: " + "\n" + ex.Message + "\nSOURCE: " + ex.StackTrace);
            }
            return blkid;
        }

~'J'~
\\\"Always drink upstream from the herd."\\\ - Will Rogers, was died in 1935 plane crash

--> Donate to TheSwamp <--

T.Willey

  • Needs a day job
  • Posts: 4964
Re: .NET BLOCK Routines
« Reply #2 on: January 28, 2010, 10:59:26 am »
Here is one for inserting block within a layout, by layout name.  I would change it a little now, but a good example.

Code: [Select]
using System;
using System.Drawing;
using System.Windows.Forms;
using System.Collections;
using System.IO;

using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.Runtime;
using Autodesk.AutoCAD.Geometry;

public static ObjectId InsertBlock ( Database db, string loName, string blkName, Point3d insPt ) {
    ObjectId RtnObjId = ObjectId.Null;
    using ( Transaction Trans = db.TransactionManager.StartTransaction( ) ) {
        DBDictionary LoDict = Trans.GetObject( db.LayoutDictionaryId, OpenMode.ForRead ) as DBDictionary;
        foreach ( DictionaryEntry de in LoDict ) {
            if ( string.Compare( ( string )de.Key, loName, true ).Equals( 0 ) ) {
                Layout Lo = Trans.GetObject( ( ObjectId )de.Value, OpenMode.ForWrite ) as Layout;
                BlockTable BlkTbl = Trans.GetObject( db.BlockTableId, OpenMode.ForRead ) as BlockTable;
                BlockTableRecord LoRec = Trans.GetObject( Lo.BlockTableRecordId, OpenMode.ForRead ) as BlockTableRecord;
                ObjectId BlkTblRecId = GetNonErasedTableRecordId( BlkTbl.Id, blkName );
                if ( BlkTblRecId.IsNull ) {
                    string BlkPath = HostApplicationServices.Current.FindFile( blkName + ".dwg", db, FindFileHint.Default );
                    if ( string.IsNullOrEmpty( BlkPath ) )
                        return RtnObjId;
                    BlkTbl.UpgradeOpen( );
                    using ( Database tempDb = new Database( false, true ) ) {
                        tempDb.ReadDwgFile( BlkPath, FileShare.Read, true, null );
                        db.Insert( blkName, tempDb, false );
                    }
                    BlkTblRecId = GetNonErasedTableRecordId( BlkTbl.Id, blkName );
                }
                LoRec.UpgradeOpen( );
                BlockReference BlkRef = new BlockReference( insPt, BlkTblRecId );
                LoRec.AppendEntity( BlkRef );
                Trans.AddNewlyCreatedDBObject( BlkRef, true );
                BlockTableRecord BlkTblRec = Trans.GetObject( BlkTblRecId, OpenMode.ForRead ) as BlockTableRecord;
                if ( BlkTblRec.HasAttributeDefinitions ) {
                    foreach ( ObjectId objId in BlkTblRec ) {
                        AttributeDefinition AttDef = Trans.GetObject( objId, OpenMode.ForRead ) as AttributeDefinition;
                        if ( AttDef != null ) {
                            AttributeReference AttRef = new AttributeReference( );
                            AttRef.SetAttributeFromBlock( AttDef, BlkRef.BlockTransform );
                            BlkRef.AttributeCollection.AppendAttribute( AttRef );
                            Trans.AddNewlyCreatedDBObject( AttRef, true );
                        }
                    }
                }
                Trans.Commit( );
            }
        }
    }
    return RtnObjId;
}

Thanks Oleg.  Forgot to add this sub by Tony T.
Code: [Select]
public static ObjectId GetNonErasedTableRecordId( ObjectId TableId, string Name )
    // Posted by Tony Tanzillo 01Sept2006
{
   ObjectId id = ObjectId.Null;
   using( Transaction tr = TableId.Database.TransactionManager.StartTransaction() )
   {
      SymbolTable table = (SymbolTable) tr.GetObject( TableId, OpenMode.ForRead );
      if( table.Has( Name ) )
      {
         id = table[Name];
         if( !id.IsErased )
            return id;
         foreach( ObjectId recId in table )
         {
            if( ! recId.IsErased )
            {
               SymbolTableRecord rec = (SymbolTableRecord) tr.GetObject( recId, OpenMode.ForRead );
               if( string.Compare( rec.Name, Name, true ) == 0 )
                  return recId;
            }
         }
      }
   }
   return id;
}
« Last Edit: January 28, 2010, 12:04:17 pm by T.Willey »
Tim

I don't want to ' end-up ', I want to ' become '. - Me

Please think about donating if this post helped you.

huiz

  • Swamp Rat
  • Posts: 578
  • Certified Prof C3D
Re: .NET BLOCK Routines
« Reply #3 on: August 15, 2010, 09:45:16 am »
This code will place an existing block in ModelSpace, adds attributes and places it on the right layer. It also manages the current annotation scale if the block is annotative.
Thanks to fro2001 for this great piece of code!

Code: [Select]
  ' This procedure will insert a block into the current drawing. It also controls the defined attributes and it will
  ' add the current Annotation Scale if the block is Annotative.
  ' Code contribution:
  ' - fro2001 - via TheSwamp.org
  Public Sub hzInsertBlockMSWithAttributes(ByVal pntInsert As Geometry.Point3d,
                                           ByVal strBlockName As String,
                                           ByVal dScale As Double,
                                           ByVal strLayerName As String,
                                           ByVal arrAttrValues As ArrayList)

    Dim db As Database = HostApplicationServices.WorkingDatabase
    Using tr As Transaction = db.TransactionManager.StartTransaction
      Dim bt As BlockTable = db.BlockTableId.GetObject(OpenMode.ForRead)
      Dim btrMS As BlockTableRecord = bt(BlockTableRecord.ModelSpace).GetObject(OpenMode.ForRead)
      If Not bt.Has(strBlockName) Then
        Exit Sub
      Else
        btrMS.UpgradeOpen()
      End If
      Dim btr As BlockTableRecord = bt(strBlockName).GetObject(OpenMode.ForRead)
      Dim bref As New BlockReference(pntInsert, btr.ObjectId)
      btrMS.AppendEntity(bref)
      tr.AddNewlyCreatedDBObject(bref, True)
      ' set annotation scale if block is annotative
      If btr.Annotative = AnnotativeStates.True Then
        Dim ocm As ObjectContextManager = db.ObjectContextManager
        Dim occ As ObjectContextCollection = ocm.GetContextCollection("ACDB_ANNOTATIONSCALES")
        Internal.ObjectContexts.AddContext(bref, occ.CurrentContext)
      End If
      ' set attributes and values
      ' TODO: Set attribute to layer of block or layer of original definition, now it will be placed on layer 0
      ' TODO: Check if attribute is annotative, it is possible to create non-annotative blocks with annotative attributes
      Dim ccAttCounter As Integer = 0
      For Each objId As ObjectId In btr
        Dim ent As Entity = objId.GetObject(OpenMode.ForRead)
        If TypeOf ent Is AttributeDefinition Then
          ent.UpgradeOpen()
          Dim attDef As AttributeDefinition = CType(ent, AttributeDefinition)
          Dim attRef As New AttributeReference
          attRef.SetAttributeFromBlock(attDef, bref.BlockTransform)
          ' check if there is a value to add
          If arrAttrValues.Count - 1 >= ccAttCounter Then
            attRef.TextString = arrAttrValues(ccAttCounter).ToString.Trim
          Else
            attRef.TextString = ""
          End If
          bref.AttributeCollection.AppendAttribute(attRef)
          tr.AddNewlyCreatedDBObject(attRef, True)
          ccAttCounter += 1
        End If
      Next
      ' set layer
      ' TODO: Check if layername exist
      bref.Layer = strLayerName
      ' commit
      tr.Commit()
    End Using
  End Sub

The conclusion is justified that the initialization of the development of critical subsystem optimizes the probability of success to the development of the technical behavior over a given period.

gile

  • Water Moccasin
  • Posts: 1728
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #4 on: November 23, 2010, 05:43:48 pm »
Hi,
Trying to help someone on http://www.acadnetwork.com/, who wanted to delete duplicated block references.
Are considered as duplicated blocks, those which have the same name, layer, insertion point, rotation and scales.

I first wrote a C# routine using a 'Duplicate' extension method and I thaught it was a good exercice to improve my very new F# learning.
Here're both implementations.

Code: [Select]
using System;
using System.Collections.Generic;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Geometry;
using Autodesk.AutoCAD.Runtime;

namespace DuplicateTest
{
    public static class BlockRefExtension
    {
        public static bool Duplicate(this BlockReference blk1, BlockReference blk2)
        {
            Tolerance tol = new Tolerance(1e-6, 1e-6);
            return
                blk1.OwnerId == blk2.OwnerId &&
                blk1.Name == blk2.Name &&
                blk1.Layer == blk2.Layer &&
                Math.Round(blk1.Rotation, 5) == Math.Round(blk2.Rotation, 5) &&
                blk1.Position.IsEqualTo(blk2.Position, tol) &&
                blk1.ScaleFactors.IsEqualTo(blk2.ScaleFactors, tol);
        }
    }

    public class Commands
    {
        [CommandMethod("Test")]
        public void DelDupBlkRef()
        {
            try
            {
                int del = DeleteDuplicatedBlockRef();
                Application.ShowAlertDialog(del.ToString() + " duplicated block(s) have been erased");
            }
            catch (System.Exception e)
            {
                Application.ShowAlertDialog("\nError: " + e.Message);
            }
        }

        private int DeleteDuplicatedBlockRef()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            int result = 0;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId id in bt)
                {
                    BlockTableRecord btr =
                        (BlockTableRecord)tr.GetObject(id, OpenMode.ForWrite);
                    ObjectIdCollection idCol = btr.GetBlockReferenceIds(true, false);
                    for (int i = 0; i < idCol.Count; i++)
                    {
                        BlockReference blkRef =
                            (BlockReference)tr.GetObject(idCol[i], OpenMode.ForRead);
                        for (int j = i + 1; j < idCol.Count; j++)
                        {
                            BlockReference br =
                                (BlockReference)tr.GetObject(idCol[j], OpenMode.ForRead);
                            if (br.Duplicate(blkRef))
                            {
                                idCol.RemoveAt(j);
                                br.UpgradeOpen();
                                br.Erase();
                                j--;
                                result++;
                            }
                        }
                    }
                }
                tr.Commit();
            }
            return result;
        }
    }
}

Code: [Select]
module DupBlks

open System
open System.Collections.Generic
open Autodesk.AutoCAD.ApplicationServices
open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime

let Duplicate (a: BlockReference) (b: BlockReference) =
    let tol = new Tolerance(1e-6, 1e-6)
    a.Name = b.Name &&
    a.OwnerId = b.OwnerId &&
    a.Position = b.Position &&
    a.Layer = b.Layer &&
    Math.Round(a.Rotation, 6) = Math.Round(b.Rotation, 6) &&
    a.ScaleFactors = b.ScaleFactors

let DeleteDuplicatedBlockRef() =
    let db = HostApplicationServices.WorkingDatabase
    use tr = db.TransactionManager.StartTransaction()
    
    let getRefs (bt: BlockTable) =
        let mutable lst = []
        for id in bt do
            let btr = tr.GetObject(id, OpenMode.ForRead) :?> BlockTableRecord
            for oId in btr.GetBlockReferenceIds(true, false) do
                lst <- tr.GetObject(oId, OpenMode.ForWrite) :?> BlockReference :: lst
        lst

    let rec findDups acc blks =
        match blks with
        | [] -> acc
        | h :: t -> let pair = List.partition (fun x -> Duplicate x h) t
                    findDups (acc @ (fst pair)) (snd pair)
    let delBlk lst =
        List.iter (fun (x: BlockReference) -> x.Erase()) lst
        lst.Length
                
    let cnt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) :?> BlockTable
              |> getRefs |> findDups [] |> delBlk
    tr.Commit()
    cnt
        
[<CommandMethod("Test")>]
let Test() =
    try
        let del = DeleteDuplicatedBlockRef()
        Application.ShowAlertDialog(del.ToString() + " duplicated block(s) have been erased")
    with
        |ex -> Application.ShowAlertDialog(ex.Message)

« Last Edit: November 23, 2010, 06:16:00 pm by gile »
Speaking English as a French Frog

Jeff H

  • Needs a day job
  • Posts: 4598
Re: .NET BLOCK Routines
« Reply #5 on: December 10, 2010, 05:26:13 am »
A little simple one that will change the Visibilty parameter of a all blockreferences based from a dynamic block

Maybe you could add another for each loop in this one



Code: [Select]
[CommandMethod("DynamicBlocksThingy")]
        public void DynamicBlocksThingy()
        {
            Document doc = Application.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;

            using (Transaction trx = db.TransactionManager.StartTransaction())
            {
               
                BlockTable bt = db.BlockTableId.GetObject(OpenMode.ForRead) as BlockTable;
                foreach (ObjectId objId in bt)
                {
                    BlockTableRecord btr = (BlockTableRecord)objId.GetObject(OpenMode.ForRead);

                    if (btr.IsDynamicBlock)
                    {
                        ObjectIdCollection brefObjidColl = btr.GetBlockReferenceIds(true, true);
                        ObjectIdCollection anonObjIdsColl = btr.GetAnonymousBlockIds();

                        foreach (ObjectId anonObjId in anonObjIdsColl)
                        {
                            BlockTableRecord btrAnon = (BlockTableRecord)anonObjId.GetObject(OpenMode.ForRead);
                            ObjectIdCollection ObjidColl = btrAnon.GetBlockReferenceIds(true, true);
                            foreach (ObjectId Id in ObjidColl)
                            brefObjidColl.Add(Id);
                        }

                        foreach (ObjectId brefObjId in brefObjidColl)
                        {
                            BlockReference bref = brefObjId.GetObject(OpenMode.ForWrite) as BlockReference;
                            DynamicBlockReferencePropertyCollection dynBrefColl = bref.DynamicBlockReferencePropertyCollection;
                            foreach (DynamicBlockReferenceProperty dynBrefProps in dynBrefColl)
                            {
                                if (dynBrefProps.PropertyName == "Visibility1")
                                {
                                    dynBrefProps.Value = "Vis2";
                                }
                            }
                        }
                    }
                }
                               
               
                trx.Commit();
            }
        }

mohnston

  • Bull Frog
  • Posts: 283
  • Some Other Mark
Get Dynamic Blocks by Name
« Reply #6 on: February 25, 2011, 02:05:39 pm »
This function will get all dynamic blocks by name.
Normally this would be a simple filter but dynamic blocks become anonymous as soon as a dynamic property is changed.
Code: [Select]
        public static ObjectIdCollection GetDynamicBlocksByName(string BlkName)
        {
            ObjectIdCollection res = new ObjectIdCollection();
            Database db = Application.DocumentManager.MdiActiveDocument.Database;
            using (Transaction trans = db.TransactionManager.StartTransaction())
            {
                //get the blockTable and iterate through all block Defs
                BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
                foreach (ObjectId btrId in bt)
                {
                    //get the block Def and see if it is anonymous
                    BlockTableRecord btr = (BlockTableRecord)trans.GetObject(btrId, OpenMode.ForRead);
                    if (btr.IsDynamicBlock && btr.Name.Equals(BlkName))
                    {
                        //get all anonymous blocks from this dynamic block
                        ObjectIdCollection anonymousIds = btr.GetAnonymousBlockIds();
                        ObjectIdCollection dynBlockRefs = new ObjectIdCollection();
                        foreach (ObjectId anonymousBtrId in anonymousIds)
                        {
                            //get the anonymous block
                            BlockTableRecord anonymousBtr = (BlockTableRecord)trans.GetObject(anonymousBtrId, OpenMode.ForRead);
                            //and all references to this block
                            ObjectIdCollection blockRefIds = anonymousBtr.GetBlockReferenceIds(true, true);
                            foreach (ObjectId id in blockRefIds) dynBlockRefs.Add(id);
                        }
                        res = dynBlockRefs;
                        break;
                    }
                }
                trans.Commit();
            }
            return res;
        }
It's amazing what you can do when you don't know what you can't do.

gile

  • Water Moccasin
  • Posts: 1728
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #7 on: November 05, 2011, 08:12:21 am »
Hi,

Here's a little example for attributes extraction.

I try to write it in a "declarative" style using the Linq extension methods and some others defined in the Extension class (thanks to Thorsten 'kaefer' @ TheSwamp for its nice 'evil' ones).

The extraction is collected in a System.Data.DataTable so that it can be easily converted to an AutoCAD table, an Exel sheet and so on.
The DataTable.WriteXls() extension method uses late binding while interacting with Excel application to avoid version reference dependency.

The BlockAttribute class which defines a new Type which main properties are:
- Block:  the block (effective) name ;
- Attributes: a dictionary for the block attributes where Key is the tag and Value the text string.
The BlockAttributeEqualityComparer class implements the IEqualityComparer<BlockAttribute> interface so that BlockAttribute collections can be used with some Linq extension methods as GroupBy().
Code: [Select]
using System;
using System.Collections.Generic;
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace AttributeExtraction
{
    public class BlockAttribute
    {
        private string _name;
        private Dictionary<string, string> _atts;

        // Public read only properties
        public string Name
        {
            get { return _name; }
        }

        public Dictionary<string, string> Attributes
        {
            get { return _atts; }
        }

        public string this[string key]
        {
            get { return _atts[key.ToUpper()]; }
        }

        // Constructors
        public BlockAttribute(BlockReference br)
        {
            SetProperties(br);
        }

        public BlockAttribute(ObjectId id)
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            using (Transaction tr = doc.TransactionManager.StartTransaction())
            {
                SetProperties(tr.GetObject(id, OpenMode.ForRead) as BlockReference);
            }
        }

        // Public method
        new public string ToString()
        {
            if (_atts != null && _atts.Count > 0)
                return string.Format("{0}: {1}",
                    _name,
                    _atts.Select(a => string.Format("{0}={1}", a.Key, a.Value))
                        .Aggregate((a, b) => string.Format("{0}; {1}", a, b)));
            return _name;
        }

        // Private method
        private void SetProperties(BlockReference br)
        {
            if (br == null) return;
            _name = br.GetEffectiveName();
            _atts = new Dictionary<string, string>();
            br.AttributeCollection
                .GetObjects<AttributeReference>()
                .Iterate(att => _atts.Add(att.Tag.ToUpper(), att.TextString));
        }
    }

    public class BlockAttributeEqualityComparer : IEqualityComparer<BlockAttribute>
    {
        public bool Equals(BlockAttribute x, BlockAttribute y)
        {
            return
                x.Name.Equals(y.Name, StringComparison.CurrentCultureIgnoreCase) &&
                x.Attributes.SequenceEqual(y.Attributes);
        }

        public int GetHashCode(BlockAttribute obj)
        {
            return base.GetHashCode();
        }
    }
}

The Extensions class provides extension methods that allows to write code in a more "declarative" style
and methods to build a DataTable from a BlockAttribute collection and convert this DataTable into xls, csv files or AutoCAD Table.
Code: [Select]
using System;
using System.Collections;
using System.Collections.Generic;
using System.Data;
using System.IO;
using System.Linq;
using Autodesk.AutoCAD.DatabaseServices;
using AcDataTable = Autodesk.AutoCAD.DatabaseServices.DataTable;

namespace AttributeExtraction
{
    public static class Extensions
    {
        // Opens a DBObject in ForRead mode (kaefer @ TheSwamp)
        public static T GetObject<T>(this ObjectId id) where T : DBObject
        {
            return id.GetObject<T>(OpenMode.ForRead);
        }

        // Opens a DBObject in the given mode (kaefer @ TheSwamp)
        public static T GetObject<T>(this ObjectId id, OpenMode mode) where T : DBObject
        {
            return id.GetObject(mode) as T;
        }
 
        // Opens a collection of DBObject in ForRead mode (kaefer @ TheSwamp)       
        public static IEnumerable<T> GetObjects<T>(this IEnumerable ids) where T : DBObject
        {
            return ids.GetObjects<T>(OpenMode.ForRead);
        }

        // Opens a collection of DBObject in the given mode (kaefer @ TheSwamp)
        public static IEnumerable<T> GetObjects<T>(this IEnumerable ids, OpenMode mode) where T : DBObject
        {
            return ids
                .Cast<ObjectId>()
                .Select(id => id.GetObject<T>(mode))
                .Where(res => res != null);
        }

        // Applies the given Action to each element of the collection (mimics the F# Seq.iter function).
        public static void Iterate<T>(this IEnumerable<T> collection, Action<T> action)
        {
            foreach (T item in collection) action(item);
        }

        // Applies the given Action to each element of the collection (mimics the F# Seq.iteri function).
        // The integer passed to the Action indicates the index of element.
        public static void Iterate<T>(this IEnumerable<T> collection, Action<T, int> action)
        {
            int i = 0;
            foreach (T item in collection) action(item, i++);
        }

        // Gets the block effective name (anonymous dynamic blocs).
        public static string GetEffectiveName(this BlockReference br)
        {
            if (br.IsDynamicBlock)
                return br.DynamicBlockTableRecord.GetObject<BlockTableRecord>().Name;
            return br.Name;
        }

        // Creates a System.Data.DataTable from a BlockAttribute collection.
        public static System.Data.DataTable ToDataTable(this IEnumerable<BlockAttribute> blockAtts, string name)
        {
            System.Data.DataTable dTable = new System.Data.DataTable(name);
            dTable.Columns.Add("Name", typeof(string));
            dTable.Columns.Add("Quantity", typeof(int));
            blockAtts
                .GroupBy(blk => blk, (blk, blks) => new { Block = blk, Count = blks.Count() }, new BlockAttributeEqualityComparer())
                .Iterate(row =>
                {
                    System.Data.DataRow dRow = dTable.Rows.Add(row.Block.Name, row.Count);
                    row.Block.Attributes.Iterate(att =>
                    {
                        if (!dTable.Columns.Contains(att.Key))
                            dTable.Columns.Add(att.Key);
                        dRow[att.Key] = att.Value;
                    });
                });
            return dTable;
        }

        // Gets the column names collection of the datatable
        public static IEnumerable<string> GetColumnNames(this System.Data.DataTable dataTbl)
        {
            return dataTbl.Columns.Cast<System.Data.DataColumn>().Select(col => col.ColumnName);
        }

        // Writes an Excel file from the datatable (using late binding)
        public static void WriteXls(this System.Data.DataTable dataTbl, string filename, string sheetName, bool visible)
        {
            object mis = Type.Missing;
            object xlApp = LateBinding.GetOrCreateInstance("Excel.Application");
            xlApp.Set("DisplayAlerts", false);
            object workbooks = xlApp.Get("Workbooks");
            object workbook, worksheet;
            if (File.Exists(filename))
                workbook = workbooks.Invoke("Open", filename);
            else
                workbook = workbooks.Invoke("Add", mis);
            if (string.IsNullOrEmpty(sheetName))
                worksheet = workbook.Get("Activesheet");
            else
            {
                object worksheets = workbook.Get("Worksheets");
                try
                {
                    worksheet = worksheets.Get("Item", sheetName);
                    worksheet.Get("Cells").Invoke("Clear");
                }
                catch
                {
                    worksheet = worksheets.Invoke("Add", mis);
                    worksheet.Set("Name", sheetName);
                }
            }
            object range = worksheet.Get("Cells");
            dataTbl.GetColumnNames()
                .Iterate((name, i) => range.Get("Item", 1, i + 1).Set("Value2", name));
            dataTbl.Rows
                .Cast<DataRow>()
                .Iterate((row, i) => row.ItemArray
                    .Iterate((item, j) => range.Get("Item", i + 2, j + 1).Set("Value2", item)));
            xlApp.Set("DisplayAlerts", true);
            if (visible)
            {
                xlApp.Set("Visible", true);
            }
            else
            {
                if (File.Exists(filename))
                    workbook.Invoke("Save");
                else
                {
                    int fileFormat =
                        string.Compare("11.0", (string)xlApp.Get("Version")) < 0 &&
                        filename.EndsWith(".xlsx", StringComparison.CurrentCultureIgnoreCase) ?
                        51 : -4143;
                    workbook.Invoke("Saveas", filename, fileFormat, string.Empty, string.Empty, false, false, 1, 1);
                }
                workbook.Invoke("Close");
                workbook = null;
                xlApp.ReleaseInstance();
                xlApp = null;
            }
        }

        // Writes a csv file from the datatable.
        public static void WriteCsv(this System.Data.DataTable dataTbl, string filename)
        {
            using (StreamWriter writer = new StreamWriter(filename))
            {
                writer.WriteLine(dataTbl.GetColumnNames().Aggregate((s1, s2) => string.Format("{0},{1}", s1, s2)));
                dataTbl.Rows
                    .Cast<DataRow>()
                    .Select(row => row.ItemArray.Aggregate((s1, s2) => string.Format("{0},{1}", s1, s2)))
                    .Iterate(line => writer.WriteLine(line));
            }
        }

        // Creates an AutoCAD Table from the datatable.
        public static Table ToAcadTable(this System.Data.DataTable dataTbl, double rowHeight, double columnWidth)
        {
            //return dataTbl.Rows.Cast<DataRow>().ToAcadTable(dataTbl.TableName, dataTbl.GetColumnNames(), rowHeight, columnWidth);
            Table tbl = new Table();
            tbl.Rows[0].Height = rowHeight;
            tbl.Columns[0].Width = columnWidth;
            tbl.InsertColumns(0, columnWidth, dataTbl.Columns.Count - 1);
            tbl.InsertRows(0, rowHeight, dataTbl.Rows.Count + 1);
            tbl.Cells[0, 0].Value = dataTbl.TableName;
            dataTbl.GetColumnNames()
                .Iterate((name, i) => tbl.Cells[1, i].Value = name);
            dataTbl.Rows
                .Cast<DataRow>()
                .Iterate((row, i) =>
                    row.ItemArray.Iterate((item, j) =>
                        tbl.Cells[i + 2, j].Value = item));
            return tbl;
        }
    }
}

The LateBinding class provides helpers to write late binding instructions in a more friendly style (thanks to Thorsten and Tony T).
Code: [Select]
using BF = System.Reflection.BindingFlags;

namespace AttributeExtraction
{
    public static class LateBinding
    {
        public static object GetInstance(string appName)
        {
            return System.Runtime.InteropServices.Marshal.GetActiveObject(appName);
        }

        public static object CreateInstance(string appName)
        {
            return System.Activator.CreateInstance(System.Type.GetTypeFromProgID(appName));
        }

        public static object GetOrCreateInstance(string appName)
        {
            try { return GetInstance(appName); }
            catch { return CreateInstance(appName); }
        }

        public static object Get(this object obj, string propName, params object[] parameter)
        {
            return obj.GetType().InvokeMember(propName, BF.GetProperty, null, obj, parameter);
        }

        public static void Set(this object obj, string propName, params object[] parameter)
        {
            obj.GetType().InvokeMember(propName, BF.SetProperty, null, obj, parameter);
        }

        public static object Invoke(this object obj, string methName, params object[] parameter)
        {
            return obj.GetType().InvokeMember(methName, BF.InvokeMethod, null, obj, parameter);
        }

        public static void ReleaseInstance(this object obj)
        {
            System.Runtime.InteropServices.Marshal.ReleaseComObject(obj);
        }
    }
}

A testing command
Code: [Select]
using System.Linq;
using Autodesk.AutoCAD.ApplicationServices;
using Autodesk.AutoCAD.DatabaseServices;
using Autodesk.AutoCAD.EditorInput;
using Autodesk.AutoCAD.Runtime;
using AcAp = Autodesk.AutoCAD.ApplicationServices.Application;

namespace AttributeExtraction
{
    public class Commands
    {
        [CommandMethod("Test")]
        public void Test()
        {
            Document doc = AcAp.DocumentManager.MdiActiveDocument;
            Database db = doc.Database;
            Editor ed = doc.Editor;
            TypedValue[] filter = { new TypedValue(0, "INSERT") };
            PromptSelectionResult psr = ed.GetSelection(new SelectionFilter(filter));
            if (psr.Status != PromptStatus.OK) return;
            PromptPointResult ppr = ed.GetPoint("\nInsertion point: ");
            if (ppr.Status != PromptStatus.OK) return;
            using (Transaction tr = db.TransactionManager.StartTransaction())
            {
                System.Data.DataTable dataTable = psr.Value.GetObjectIds()
                    .Select(id => new BlockAttribute(id.GetObject<BlockReference>()))
                    .ToDataTable("Extraction");
                Table tbl = dataTable.ToAcadTable(9.0, 40.0);
                tbl.Position = ppr.Value.TransformBy(ed.CurrentUserCoordinateSystem);
                BlockTableRecord btr = db.CurrentSpaceId.GetObject<BlockTableRecord>(OpenMode.ForWrite);
                btr.AppendEntity(tbl);
                tr.AddNewlyCreatedDBObject(tbl, true);
                try
                {
                    string filename = (string)AcAp.GetSystemVariable("dwgprefix") + "Extraction.xls";
                    dataTable.WriteXls(filename, null, true);
                }
                catch
                {
                    AcAp.ShowAlertDialog("Failed to open Excel");
                }
                tr.Commit();
            }
        }
    }
}
« Last Edit: November 06, 2011, 04:56:27 am by gile »
Speaking English as a French Frog

gile

  • Water Moccasin
  • Posts: 1728
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #8 on: November 06, 2011, 02:53:55 am »
I added some methods in the upper example: System.Data.Datatable.WriteCsv (to write the datatable in a CSV file) and System.Data.DataTable.WriteXls (to write the datatable in an Excel sheet) which uses late binding to avoid Excel version dependency. The LateBinding class provises methods to make late binding instruction more readable (thanks to Thorsten and Tony T).
« Last Edit: November 06, 2011, 03:04:59 am by gile »
Speaking English as a French Frog

kaefer

  • Swamp Rat
  • Posts: 562
Re: .NET BLOCK Routines
« Reply #9 on: November 08, 2011, 09:22:25 am »
Here's a little example for attributes extraction.

Awesome, that's what your little example is: Just awesome.

Here's a rudimentary F# version doing the same; it reads attributes from the block references in a selection set, counts the different attribute sets, stores this information in a DataTable and finally converts that to an AutoCAD table.

The aim is not to improve on gile's original creation (that's hardly possible) but to illustrate the paradigmatic differences in implementation.

- let-bound functions instead of extension methods
- pipelining (|>) and function composition (>>) instead of method chaining
- reverse argument order compared to extension methods to support pipelining
- uses a data type which supports, inter alia, IEquatable and IStructuralEquatable natively, obviating the need for overriden Equals and GetHashCode methods
- tries to be more imperative "in the small" because for foo in bar do ... reads nicer than bar |> Seq.iter (fun foo -> ...)

Code: [Select]
// Original written in C# by gile @ theswamp
// http://www.theswamp.org/index.php?topic=31859.msg452527#msg452527

module AttributeExtraction

open Autodesk.AutoCAD.DatabaseServices
open Autodesk.AutoCAD.EditorInput
open Autodesk.AutoCAD.Geometry
open Autodesk.AutoCAD.Runtime
open System.Data

type acadApp = Autodesk.AutoCAD.ApplicationServices.Application

// type test returning 'T option
let ofType<'T> : obj -> _ =
    box
    >> function :? 'T as t -> Some t | _ -> None

// get DBObject for read and cast to 'T, if type parameter present
let getObject<'T when 'T :> DBObject> (oid: ObjectId) =
    oid.GetObject OpenMode.ForRead
    |> unbox<'T>

// get all objects from an untyped sequence, and try to cast them to 'T
let getObjects<'T when 'T :> DBObject> : System.Collections.IEnumerable -> _ =
    Seq.cast<ObjectId>
    >> Seq.choose (getObject >> ofType<'T>)

// F# record type to encapsulate a block reference and its attributes
type BlockAttribute =
    {   
        Name : string
        Attributes : Map<string,string>
    } with
    // indexer method
    member me.Item (key : string) = me.Attributes.[key.ToUpper()]
   
    override me.ToString() =
        if me.Attributes.IsEmpty then
            me.Name
        else
            let sb = new System.Text.StringBuilder()
            me.Attributes
            |> Map.fold (fun first k v ->
                if first then
                    sb.AppendFormat("{0}: {1}={2}", me.Name, k, v)
                else
                    sb.AppendFormat("; {0}={1}", k, v)
                |> ignore
                false ) true |> ignore
            sb.ToString()
   
    // Ersatz constructors
    static member Create oid = BlockAttribute.Create (getObject oid)
    static member Create(br : BlockReference) =
        {   Name =
                // Gets the block effective name (anonymous dynamic blocs)
                if br.IsDynamicBlock then
                    (getObject<BlockTableRecord> br.DynamicBlockTableRecord).Name
                else
                    br.Name
            Attributes =
                getObjects<AttributeReference> br.AttributeCollection
                |> Seq.map (fun ar -> ar.Tag.ToUpper(), ar.TextString)
                |> Map.ofSeq }
   
// Create a Datatable from a BlockAttribute collection
let toDataTable name blockAtts =
    let dTable = new System.Data.DataTable(name)
    dTable.Columns.Add("Name", typeof<string>)  |> ignore
    dTable.Columns.Add("Quantity", typeof<int>) |> ignore
    blockAtts
    |> Seq.groupBy id
    |> Seq.map (fun (blk, blks) -> blk, Seq.length blks)
    |> Seq.iter (fun (blk, bcnt) ->
        let dRow = dTable.Rows.Add(blk.Name, bcnt)
        for KeyValue(k, v) in blk.Attributes do
            if not(dTable.Columns.Contains k) then
                dTable.Columns.Add k |> ignore
            dRow.[k] <- v )
    dTable

// Create an AutoCAD Table from a Datatable
let toAcadTable rowHeight columnWidth (dTable : System.Data.DataTable) =
    let tbl = new Table()
    tbl.Rows.[0].Height <- rowHeight
    tbl.Columns.[0].Width <- columnWidth
    tbl.InsertColumns(0, columnWidth, dTable.Columns.Count - 1)
    tbl.InsertRows(0, rowHeight, dTable.Rows.Count + 1)
    tbl.Cells.[0, 0].Value <- dTable.TableName
    for x in 0 .. dTable.Columns.Count - 1 do
        let col = dTable.Columns.[x]
        tbl.Cells.[1, x].Value <- col.ColumnName
        for y in 0 .. dTable.Rows.Count - 1 do
            tbl.Cells.[y + 2, x].Value <- dTable.Rows.[y].[col]
    tbl

// Test command
[<CommandMethod("TestF", CommandFlags.UsePickSet)>]
let testCommand() =
    let doc = acadApp.DocumentManager.MdiActiveDocument
    let db = doc.Database
    let ed = doc.Editor
    let filter = [| new TypedValue(0, "INSERT") |]
    let psr = ed.GetSelection(new SelectionFilter(filter))
    if psr.Status = PromptStatus.OK then
        let ppr = ed.GetPoint "\nInsertion point: "
        if ppr.Status = PromptStatus.OK then
            use tr = db.TransactionManager.StartTransaction()
            let tbl =
                psr.Value.GetObjectIds()
                |> Seq.map BlockAttribute.Create
                |> toDataTable "Extraction"
                |> toAcadTable 9.0 40.0
            tbl.Position <- ppr.Value.TransformBy ed.CurrentUserCoordinateSystem
            let btr : BlockTableRecord = getObject db.CurrentSpaceId
            btr.UpgradeOpen()
            btr.AppendEntity tbl |> ignore
            tr.AddNewlyCreatedDBObject(tbl, true)
            tr.Commit()

Edited: getObjects needs a type annotation to be generic
« Last Edit: November 08, 2011, 03:21:03 pm by kaefer »

gile

  • Water Moccasin
  • Posts: 1728
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #10 on: November 08, 2011, 11:13:15 am »
Thanks Thorsten.
I learn(ed) a lot from you codes and really like very much your GetObject<T>() and GetObjects<T>() methods.
The upper F# code is very nice.

Quote
- tries to be more imperative "in the small" because for foo in bar do ... reads nicer than bar |> Seq.iter (fun foo -> ...)
This is funny, while I try to write C# in a more functional/declarative style, you write F# in a more imperative one.
Speaking English as a French Frog

fixo

  • Swamp Rat
  • Posts: 856
  • My location is not in the Hitleropa
Re: .NET BLOCK Routines
« Reply #11 on: January 08, 2012, 01:00:05 pm »
Very basic code snip for appending attribute to block
Not tested on multiline attributes
Code: [Select]

      [CommandMethod("AppendAttributeToBlock","appatt", CommandFlags.Session | CommandFlags.Modal | CommandFlags.UsePickSet | CommandFlags.Redraw)]
        public static void AppendAttributeTest()
           {         
            Document doc = acadApp.DocumentManager.MdiActiveDocument;

            Editor ed = doc.Editor;

            Database db = doc.Database;

            try
            {
                using (doc.LockDocument())
                {
                    using (Transaction tr = db.TransactionManager.StartTransaction())
                    {
                        BlockTable bt = tr.GetObject(db.BlockTableId, OpenMode.ForRead) as BlockTable;

                        BlockTableRecord currSp = tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite) as BlockTableRecord;

                       
                        PromptNestedEntityOptions pno =

                         new PromptNestedEntityOptions("\nSelect source attribute to append new attribute below this one >>");

                        PromptNestedEntityResult nres =

                                                ed.GetNestedEntity(pno);

                        if (nres.Status != PromptStatus.OK)

                            return;

                        ObjectId id = nres.ObjectId;

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

                        Point3d pnt = nres.PickedPoint;

                        ObjectId owId = ent.OwnerId;

                        AttributeReference attref = null;

                        if (id.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(AttributeReference))))
                        {
                            attref = tr.GetObject(id,OpenMode.ForWrite) as AttributeReference;
                        }
                     

                        BlockTableRecord btr = null;

                        BlockReference bref = null;

                        if (owId.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(BlockReference))))
                        {
                            bref = tr.GetObject(owId,OpenMode.ForWrite) as BlockReference;

                            if (bref.IsDynamicBlock)
                            {
                                btr = tr.GetObject(bref.DynamicBlockTableRecord,OpenMode.ForRead) as BlockTableRecord;
                            }
                            else
                            {
                                btr = tr.GetObject(bref.BlockTableRecord,OpenMode.ForRead) as BlockTableRecord;
                            }
                        }
                       
                        Point3d insPt = attref.Position.TransformBy(bref.BlockTransform);

                        btr.UpgradeOpen();

                        ObjectIdCollection bids = new ObjectIdCollection();

                        AttributeDefinition def = null;

                        foreach (ObjectId defid in btr)
                        {
                            if (defid.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(AttributeDefinition))))
                            {
                                def = tr.GetObject(defid, OpenMode.ForRead) as AttributeDefinition;

                                if (def.Tag == attref.Tag)
                                {
                                    def.UpgradeOpen();

                                    bids.Add(defid);

                                    break;
                                }
                            }
                        }

                     

                        IdMapping map = new IdMapping();

                        db.DeepCloneObjects(bids, btr.ObjectId, map,true);

                        ObjectIdCollection coll = new ObjectIdCollection();

                        AttributeDefinition attDef = null;

                        foreach (IdPair pair in map)
                        {
                            if (pair.IsPrimary)
                            {
                                Entity oent = (Entity)tr.GetObject(pair.Value, OpenMode.ForWrite);

                                if (oent != null)
                                {

                                    if (pair.Value.ObjectClass.IsDerivedFrom(RXClass.GetClass(typeof(AttributeDefinition))))
                                    {
                                         attDef = oent as AttributeDefinition;

                                        attDef.UpgradeOpen();

                                        attDef.SetPropertiesFrom(def as Entity);
                                        // add other properties from source attribute definition to suit here:

                                        attDef.Justify = def.Justify;

                                        attDef.Position = btr.Origin.Add(

                                            new Vector3d(attDef.Position.X, attDef.Position.Y - attDef.Height * 1.25, attDef.Position.Z)).TransformBy(Matrix3d.Identity

                                            );

                                        attDef.Tag = "NEW_TAG";

                                        attDef.TextString = "New Prompt";

                                        attDef.TextString = "New Textstring";

                                        coll.Add(oent.ObjectId);


                                    }
                                }
                            }
                        }
                        btr.AssumeOwnershipOf(coll);

                        btr.DowngradeOpen();

                        attDef.Dispose();//optional

                        bref.RecordGraphicsModified(true);

                        tr.TransactionManager.QueueForGraphicsFlush();

                        doc.TransactionManager.FlushGraphics();//optional

                        ed.UpdateScreen();

                        tr.Commit();
                    }

                }
            }
               
            catch (System.Exception ex)
            {
                ed.WriteMessage(ex.Message + "\n" + ex.StackTrace);
            }
            finally
            {
                acadApp.ShowAlertDialog("Call command \"ATTSYNC\" manually");

            }

        }
\\\"Always drink upstream from the herd."\\\ - Will Rogers, was died in 1935 plane crash

--> Donate to TheSwamp <--

Benzirpi

  • Mosquito
  • Posts: 3
Re: .NET BLOCK Routines
« Reply #12 on: February 01, 2012, 08:53:26 am »
Here's a little example about inserting a block (a .dwg file) into the current open drawing. It uses EntitiyJig in order to ask dinamically the user to pick a insertion point and set a rotation angle.

The main function:
Code: [Select]
    '/**
    '* Pedir al usuario donde y con que angulo, para insertar el bloque indicado en el plano abierto en ese momento.
    '*/
    Public Shared Sub InsertarBloque(ByVal archivoDWG As String)
        Dim acadDoc As Document
        acadDoc = Application.DocumentManager.MdiActiveDocument
        Using acadLockDoc As DocumentLock = acadDoc.LockDocument()

            Dim dbTemporal As Database
            dbTemporal = New Database(buildDefaultDrawing:=False, noDocument:=True)
            dbTemporal.ReadDwgFile(fileName:=archivoDWG, _
                                    fileSharing:=IO.FileShare.Read, _
                                    allowCPConversion:=False, _
                                    password:="")
            Dim nombreBloque As String
            nombreBloque = IO.Path.GetFileNameWithoutExtension(archivoDWG)
            SymbolUtilityServices.ValidateSymbolName(name:=nombreBloque, allowVerticalBar:=False)

            Dim acadDB As Database
            acadDB = acadDoc.Database
            Dim idBloque As ObjectId
            idBloque = acadDB.Insert(blockName:=nombreBloque, dataBase:=dbTemporal, preserveSourceDatabase:=False)
            Dim bloqueAInsertar As BlockReference
            bloqueAInsertar = New BlockReference(position:=Point3d.Origin, BlockTableRecord:=idBloque)

            Dim acadEditor As Editor
            acadEditor = acadDoc.Editor
            bloqueAInsertar.TransformBy(acadEditor.CurrentUserCoordinateSystem)

            Dim unaPlantilla As PlantillaBloque_PedirPuntoDeInserccion
            unaPlantilla = New PlantillaBloque_PedirPuntoDeInserccion(bloqueAInsertar)
            Dim resultadoPedirPunto As PromptResult
            resultadoPedirPunto = acadEditor.Drag(unaPlantilla)
            If resultadoPedirPunto.Status.Equals(PromptStatus.OK) Then

                Dim otraPlantilla As PlantillaBloque_PedirAnguloDeRotacion
                otraPlantilla = New PlantillaBloque_PedirAnguloDeRotacion(bloqueAInsertar, _
                                                                          anguloBase:=bloqueAInsertar.Rotation, _
                                                                          sistemaCoordenadas:=acadEditor.CurrentUserCoordinateSystem.CoordinateSystem3d, _
                                                                          puntoBase:=unaPlantilla.getPuntoDeInsercion)
                Dim resultadoPedirAngulo As PromptResult
                resultadoPedirAngulo = acadEditor.Drag(otraPlantilla)
                If resultadoPedirAngulo.Status.Equals(PromptStatus.OK) Then
                    otraPlantilla.ActualizarRotacion()
                Else
                    bloqueAInsertar.Rotation = 0.0
                End If

                Using acadTrans As Transaction = acadDoc.TransactionManager.StartTransaction
                    Dim espacioActual As BlockTableRecord
                    espacioActual = CType(acadTrans.GetObject(acadDoc.Database.CurrentSpaceId, OpenMode.ForWrite), BlockTableRecord)
                    espacioActual.AppendEntity(entity:=bloqueAInsertar)
                    acadTrans.AddNewlyCreatedDBObject(obj:=bloqueAInsertar, add:=True)

                    Dim bloque As BlockTableRecord
                    bloque = CType(acadTrans.GetObject(idBloque, OpenMode.ForWrite), BlockTableRecord)
                    If bloque.HasAttributeDefinitions Then
                        Dim idElemento As ObjectId
                        For Each idElemento In bloque
                            Dim elemento As DBObject
                            elemento = acadTrans.GetObject(idElemento, OpenMode.ForRead)
                            If elemento.GetType.Name.Equals("AttributeDefinition") Then
                                Dim definicionDeAtributo As AttributeDefinition
                                definicionDeAtributo = CType(acadTrans.GetObject(idElemento, OpenMode.ForRead), AttributeDefinition)
                                Dim referenciaAAtributo As AttributeReference
                                referenciaAAtributo = New AttributeReference
                                referenciaAAtributo.SetAttributeFromBlock(definicionDeAtributo, bloqueAInsertar.BlockTransform)
                                bloqueAInsertar.AttributeCollection.AppendAttribute(referenciaAAtributo)
                                acadTrans.AddNewlyCreatedDBObject(obj:=referenciaAAtributo, add:=True)
                            End If
                        Next
                   End If
                   acadTrans.Commit()
                End Using '//acadTrans
            End If

        End Using '//acadLockDoc

        Application.UpdateScreen()

    End Sub


Ask user for insertion point:
Code: [Select]
Friend Class PlantillaBloque_PedirPuntoDeInserccion
Inherits Autodesk.Autocad.EditorInput.EntityJig

    Dim mPuntoDeInsercion, mPuntoActivo As Point3d


    Public Sub New(bloqueAInsertar as BlockReference)
        MyBase.New(bloqueAInsertar)
        mPuntoDeInsercion = bloqueAInsertar.Position
    End Sub

    Protected Overrides Function Sampler(ByVal prompts As Autodesk.AutoCAD.EditorInput.JigPrompts) As Autodesk.AutoCAD.EditorInput.SamplerStatus
        Dim opciones As New JigPromptPointOptions
        opciones.UserInputControls = UserInputControls.Accept3dCoordinates or _
                                     UserInputControls.NoZeroResponseAccepted or _
                                     UserInputControls.NoNegativeResponseAccepted
        opciones.Message = environment.NewLine & "Punto de insercion: "
        Dim resultado As PromptPointResult
        resultado = prompts.AcquirePoint(opciones)
        If mPuntoActivo = resultado.Value Then
            Return SamplerStatus.NoChange
        Else
            mPuntoActivo = resultado.Value
            Return SamplerStatus.OK
        End If
    End Function

    Protected Overrides Function Update() As Boolean
        mPuntoDeInsercion = mPuntoActivo
        Try
            CType(Entity, BlockReference).Position = mPuntoDeInsercion
        Catch ex As Exception
            Return False
        End Try
        Return True
    End Function


    Public Function getEntity As Entity
        Return Entity
    End Function


    Public Function getPuntoDeInsercion As Point3d
        Return mPuntoDeInsercion
    End Function

   
End Class



Ask user for rotation angle:
Code: [Select]
Friend Class PlantillaBloque_PedirAnguloDeRotacion
Inherits Autodesk.Autocad.EditorInput.EntityJig

    Dim mEntidadARotar As BlockReference
    Dim mAnguloBase, mVariacionAngulo As Double
    Dim mSistemaDeCoordenadas As CoordinateSystem3d
    Dim mPuntoBase As Point3d
    Dim mResultado As string


    Public Sub New(bloqueAInsertar as BlockReference, _
                   anguloBase As Double, _
                   sistemaCoordenadas As CoordinateSystem3d, _
                   puntoBase As Point3d)
        MyBase.New(bloqueAInsertar)
        mEntidadARotar = bloqueAInsertar
        mAnguloBase = anguloBase
        mSistemaDeCoordenadas = sistemaCoordenadas
        mPuntoBase = puntoBase
    End Sub

    Protected Overrides Function Sampler(ByVal prompts As Autodesk.AutoCAD.EditorInput.JigPrompts) As Autodesk.AutoCAD.EditorInput.SamplerStatus
        Dim opciones As New JigPromptAngleOptions
        opciones.Message = environment.NewLine & "Angulo a rotar: "
        opciones.DefaultValue = 0
        opciones.BasePoint = mPuntoBase
        opciones.UseBasePoint = True
        opciones.UserInputControls=UserInputControls.Accept3dCoordinates or UserInputControls.NullResponseAccepted
        Dim resultado As PromptDoubleResult
        resultado = prompts.AcquireAngle(opciones)
        If resultado.Status = PromptStatus.OK Then
            mResultado = resultado.StringResult
            If mVariacionAngulo = resultado.Value Then
                Return SamplerStatus.NoChange
            Else
                mVariacionAngulo = resultado.Value
                Return SamplerStatus.OK
            End If
        End If
        Return SamplerStatus.Cancel
    End Function

    Protected Overrides Function Update() As Boolean
        mEntidadARotar.Rotation = mVariacionAngulo + mAnguloBase
        Return True
    End Function

    Public Sub ActualizarRotacion
        mEntidadARotar.Rotation = mVariacionAngulo + mAnguloBase
    End Sub


    Public Function getEntity As Entity
        Return Entity
    End Function

    Public Function getAnguloDeRotacion As Double
        Return mAnguloBase
    End Function

    Public Function getResultado As String
        Return mResultado
    End Function
   
End Class


« Last Edit: January 08, 2014, 12:40:31 pm by Benzirpi »

trembre

  • Mosquito
  • Posts: 14
Re: .NET BLOCK Routines
« Reply #13 on: February 01, 2012, 06:41:06 pm »
This function returns an ObjectIdCollection of all instances of a particular block reference by name in modelspace;

 
Code: [Select]
public ObjectIdCollection GetModelSpaceBlockReferenceOIDCByBlockName(string blockReferenceName)
        {
            ObjectIdCollection returnOIDs = new ObjectIdCollection();

            using (Database db = AcadApp.DocumentManager.MdiActiveDocument.Database)
            {
                using (Transaction trans = db.TransactionManager.StartTransaction())
                {
                    BlockTable bt = (BlockTable)trans.GetObject(db.BlockTableId, OpenMode.ForRead);
                    BlockTableRecord btrModel = (BlockTableRecord)trans.GetObject(bt[BlockTableRecord.ModelSpace], OpenMode.ForRead);
                    foreach (ObjectId thisOid in btrModel)
                    {
                        Entity blk = (Entity)trans.GetObject(thisOid, OpenMode.ForRead);
                        if (blk.GetType().ToString() == "Autodesk.AutoCAD.DatabaseServices.BlockReference")
                        {
                            BlockReference thisBlockRef = (BlockReference)trans.GetObject(thisOid, OpenMode.ForRead);
                            if(string.Compare(thisBlockRef.Name, blockReferenceName, true) == 0)
                            {
                                returnOIDs.Add(thisOid);
                            }
                        }
                    }
                    return returnOIDs;
                }
            }
        }

gile

  • Water Moccasin
  • Posts: 1728
  • Marseille, France
Re: .NET BLOCK Routines
« Reply #14 on: January 27, 2013, 01:29:33 pm »
Dragging and rotating an (attributed) block.

BlockJig is to be used to drag (and possibly rotate) a simple block.
While dragging the block, it's possible to dynamically rotate the block by pressing Ctrl.
A click terminate the process keeping the current position and rotation for the block

BlockAttribJig inherits from BlockJig and handles the block attribute references.
In the constructor, each attribute definition informations (position, alignement and rotation) is stored in a structure.
The overrided Update() method sets the attributes geometry according to the attribute definition informations and the block reference position and rotation.

Code - C#: [Select]
  1.        class BlockJig : EntityJig
  2.        {
  3.            protected BlockReference _br;
  4.            protected Point3d _pos;
  5.            protected double _rot, _ucsRot;
  6.  
  7.            public BlockJig(BlockReference br)
  8.                : base(br)
  9.            {
  10.                _br = br;
  11.                _pos = _br.Position;
  12.                Editor ed = AcAp.DocumentManager.MdiActiveDocument.Editor;
  13.                CoordinateSystem3d ucs = ed.CurrentUserCoordinateSystem.CoordinateSystem3d;
  14.                Matrix3d ocsMat = Matrix3d.WorldToPlane(new Plane(Point3d.Origin, ucs.Zaxis));
  15.                _ucsRot = Vector3d.XAxis.GetAngleTo(ucs.Xaxis.TransformBy(ocsMat), ucs.Zaxis);
  16.                _rot = _br.Rotation - _ucsRot;
  17.            }
  18.  
  19.            protected override SamplerStatus Sampler(JigPrompts prompts)
  20.            {
  21.                System.Windows.Forms.Keys mods = System.Windows.Forms.Control.ModifierKeys;
  22.                if ((mods & System.Windows.Forms.Keys.Control) > 0)
  23.                {
  24.                    JigPromptAngleOptions jpao =
  25.                        new JigPromptAngleOptions("\nSpecify the rotation: ");
  26.                    jpao.UseBasePoint = true;
  27.                    jpao.BasePoint = _br.Position;
  28.                    jpao.Cursor = CursorType.RubberBand;
  29.                    jpao.UserInputControls = (
  30.                        UserInputControls.Accept3dCoordinates |
  31.                        UserInputControls.UseBasePointElevation);
  32.                    PromptDoubleResult pdr = prompts.AcquireAngle(jpao);
  33.  
  34.                    if (_rot == pdr.Value)
  35.                    {
  36.                        return SamplerStatus.NoChange;
  37.                    }
  38.                    else
  39.                    {
  40.                        _rot = pdr.Value;
  41.                        return SamplerStatus.OK;
  42.                    }
  43.                }
  44.                else
  45.                {
  46.                    JigPromptPointOptions jppo =
  47.                        new JigPromptPointOptions("\nSpecify insertion point (or press Ctrl for rotation): ");
  48.                    jppo.UserInputControls =
  49.                      (UserInputControls.Accept3dCoordinates | UserInputControls.NullResponseAccepted);
  50.                    PromptPointResult ppr = prompts.AcquirePoint(jppo);
  51.                    if (_pos.DistanceTo(ppr.Value) < Tolerance.Global.EqualPoint)
  52.                    {
  53.                        return SamplerStatus.NoChange;
  54.                    }
  55.                    else
  56.                    {
  57.                        _pos = ppr.Value;
  58.                    }
  59.                    return SamplerStatus.OK;
  60.                }
  61.            }
  62.  
  63.            protected override bool Update()
  64.            {
  65.                _br.Position = _pos;
  66.                _br.Rotation = _rot +_ucsRot;
  67.                return true;
  68.            }
  69.        }
  70.  
  71.        class BlockAttribJig : BlockJig
  72.        {
  73.            struct TextInfo
  74.            {
  75.                public Point3d Position;
  76.                public Point3d Alignment;
  77.                public double Rotation;
  78.                public bool IsAligned;
  79.            }
  80.  
  81.            private Dictionary<string, TextInfo> _attInfos;
  82.  
  83.            public BlockAttribJig(BlockReference br)
  84.                : base(br)
  85.            {
  86.                _attInfos = new Dictionary<string, TextInfo>();
  87.                BlockTableRecord btr = (BlockTableRecord)br.BlockTableRecord.GetObject(OpenMode.ForRead);
  88.                foreach (ObjectId id in btr)
  89.                {
  90.                    if (id.ObjectClass.Name == "AcDbAttributeDefinition")
  91.                    {
  92.                        AttributeDefinition attDef = (AttributeDefinition)id.GetObject(OpenMode.ForRead);
  93.                        TextInfo ti = new TextInfo()
  94.                        {
  95.                            Position = attDef.Position,
  96.                            Alignment = attDef.AlignmentPoint,
  97.                            IsAligned = attDef.Justify != AttachmentPoint.BaseLeft,
  98.                            Rotation = attDef.Rotation
  99.                        };
  100.                        _attInfos.Add(attDef.Tag.ToUpper(), ti);
  101.                    }
  102.                }
  103.            }
  104.  
  105.            protected override bool Update()
  106.            {
  107.                base.Update();
  108.                foreach (ObjectId id in _br.AttributeCollection)
  109.                {
  110.                    AttributeReference att = (AttributeReference)id.GetObject(OpenMode.ForWrite);
  111.                    att.Rotation = _br.Rotation;
  112.                    string tag = att.Tag.ToUpper();
  113.                    if (_attInfos.ContainsKey(tag))
  114.                    {
  115.                        TextInfo ti = _attInfos[tag];
  116.                        att.Position = ti.Position.TransformBy(_br.BlockTransform);
  117.                        if (ti.IsAligned)
  118.                        {
  119.                            att.AlignmentPoint =
  120.                                ti.Alignment.TransformBy(_br.BlockTransform);
  121.                            att.AdjustAlignment(_br.Database);
  122.                        }
  123.                        if (att.IsMTextAttribute)
  124.                        {
  125.                            att.UpdateMTextAttribute();
  126.                        }
  127.                        att.Rotation = ti.Rotation + _br.Rotation;
  128.                    }
  129.                }
  130.                return true;
  131.            }
  132.        }

A little using example to insert a block

Code - C#: [Select]
  1.        [CommandMethod("TEST")]
  2.        public void Test()
  3.        {
  4.            Document doc = Application.DocumentManager.MdiActiveDocument;
  5.            Database db = doc.Database;
  6.            Editor ed = doc.Editor;
  7.  
  8.            PromptResult pr = ed.GetString("\nBlock name: ");
  9.            if (pr.Status != PromptStatus.OK) return;
  10.            string blockName = pr.StringResult;
  11.  
  12.            using (Transaction tr = db.TransactionManager.StartTransaction())
  13.            {
  14.                BlockTable bt = (BlockTable)tr.GetObject(db.BlockTableId, OpenMode.ForRead);
  15.                if (!bt.Has(blockName))
  16.                {
  17.                    ed.WriteMessage("\nNone block '{0}' in the document block table.", blockName);
  18.                    return;
  19.                }
  20.                BlockTableRecord curSpace =
  21.                    (BlockTableRecord)tr.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
  22.  
  23.                // Add the block reference to Database first
  24.                BlockReference br = new BlockReference(Point3d.Origin, bt[blockName]);
  25.                br.TransformBy(ed.CurrentUserCoordinateSystem);
  26.                curSpace.AppendEntity(br);
  27.                tr.AddNewlyCreatedDBObject(br, true);
  28.  
  29.                // Get the block definition
  30.                BlockTableRecord btr =
  31.                    (BlockTableRecord)tr.GetObject(bt[blockName], OpenMode.ForRead);
  32.                BlockJig jig;
  33.                if (btr.HasAttributeDefinitions)
  34.                {
  35.                    // Add attribute references to the block reference
  36.                    foreach (ObjectId id in btr)
  37.                    {
  38.                        if (id.ObjectClass.Name == "AcDbAttributeDefinition")
  39.                        {
  40.                            AttributeDefinition attDef =
  41.                                (AttributeDefinition)tr.GetObject(id, OpenMode.ForRead);
  42.                            AttributeReference attRef = new AttributeReference();
  43.                            attRef.SetAttributeFromBlock(attDef, br.BlockTransform);
  44.                            ObjectId attId = br.AttributeCollection.AppendAttribute(attRef);
  45.                            tr.AddNewlyCreatedDBObject(attRef, true);
  46.                        }
  47.                    }
  48.                    // Create a BlockAttribJig instance
  49.                    jig = new BlockAttribJig(br);
  50.                }
  51.                else
  52.                {
  53.                    // Create a BlockJig instance
  54.                    jig = new BlockJig(br);
  55.                }
  56.                // Drag the block reference
  57.                pr = ed.Drag(jig);
  58.                if (pr.Status != PromptStatus.OK) br.Erase();
  59.                tr.Commit();
  60.            }
  61.        }
Speaking English as a French Frog