Author Topic: Converting dynamic block to static block  (Read 157 times)

0 Members and 1 Guest are viewing this topic.

jon

  • Mosquito
  • Posts: 4
Converting dynamic block to static block
« on: May 12, 2019, 06:38:32 PM »
Hello wizards of the swamp!

I have a problem when I try to convert a Dynamic Block into a Static Block through C#, the problem is that some of the blocks I convert end up with the wrong rotation.

I have a DWG that my company works in, and here I run a custom command called "STATIFY". This command removes some Dynamic Blocks my company prefers to work with and insert new Static Blocks from an external DWG given to us by our customer. Because of specifications given by the customer, the DWG we produce needs to contain the Static Blocks provided by the customer. This leaves the built-in ConvertToStaticBlock method useless as when you use the ConvertToStaticBlock method, your "new" converted block's name will be the anonymous name of the dynamic block. I.E a converted block gets the name *UXX while I want the name to be CUSTOMER_BLOCK_TYPE_YY.

So my STATIFY command works pretty alright, for some blocks it even works perfectly but that isn't good enough. So in short I pass the Dynamic Blocks' block reference values to the Static Blocks' block reference values.
Code: [Select]
...
NewBlockRef.Rotation = OldBlockRef.Rotation
NewBlockRef.Scaling = OldBlockRef.Scaling
...
At this point I get into trouble since when I set the rotational value it doesn't always work right. At first I thought it was because of the flip states, but after closer inspection I think there is a problem with the Dynamic Blocks in the DWG I have to work with. For instance I have noticed that a 90 degree rotation works in different ways on different blocks even though it shouldn't:


I suspect this might be caused by an user error, but I think I will be a pretty unpopular man if I tell people they have to redo all their DWGs and also I think I am close to a solution that will work:
Code: [Select]
//This line fixes the rotation
NewBlockRef.BlockTableRecord = OldBlockRef.BlockTableRecord
//This next line fixes my problem too
NewBlockRef.BlockTableRecord = OldBlockRef.AnonymousBlockTableRecord
//The following line does NOT fix the rotation
NewBlockRef.BlockTableRecord = OldBlockRef.DynamicBlockTableRecord
Unfortunately these two fixes leads to my new Static Blocks getting the names of the anonymous blocks (*UXX and not Customer_Block_YY). When I don't set:
Code: [Select]
NewBlockRef.BlockTableRecord = OldBlockRef.BlockTableRecord
all my new blocks get the names they are supposed to btw. Had I been able to change the name from *UXX to CUSTOMER_BLOCK_TYPE_YY I think my solution would have been complete, but this does not work because I get a DuplicateNameError. I am also unable to change the name to CUSTOMER_BLOCK_TYPE_YY*UXX as this leads to InvalidInputError. I suspect that these errors are caused by my lack of knowledge of AutoCAD and what BlockReference and BlockTableRecord really are, and also something tells me that the statement
Code: [Select]
NewBlockRef.BlockTableRecord = OldBlockRef.BlockTableRecord
is problematic. Despite this, my brain tells me that I have to be onto something here. There has to be some data in the OldBlockRef.BlockTableRecord or OldBlockRef.AnonymousBlockTableRecord that will make the rotation of my NewBlock correct? What data could this be and how would I proceed to extract it? For instance I tried to check if there was a Transformational Matrix I could extract from the OldBlockRef, but this didn't work either.

I am not at my workstation right now so I don't have access to the code, but if anyone wants to give this a go I can post some code when I get back to work. I do have to take some precautions with what code I post as this is a work project though, but if I am able to get my code to work with help from The Swamp I will obviously write up a working example I can post so others might learn from my mistakes.

It's Alive!

  • BricsCAD
  • Needs a day job
  • Posts: 6923
  • AKA Daniel
Re: Converting dynamic block to static block
« Reply #1 on: May 12, 2019, 08:52:58 PM »

Hi, could it be that you’re using the wrong function?

public virtual void ConvertToStaticBlock();
public virtual void ConvertToStaticBlock(string newBlockName);

Converts the dynamic block instance to a legacy (nondynamic) block. 

When successful, the dynamic block properties are frozen at their current values and the reference ceases to be a dynamic block. A new block is defined with the name newBlockName, which must not already exist in the drawing and must satisfy all of the naming restrictions of normal block references.





jon

  • Mosquito
  • Posts: 4
Re: Converting dynamic block to static block
« Reply #2 on: May 13, 2019, 03:24:12 AM »
Thanks for your tips!

I have tried using
Code: [Select]
oldBlkRef.ConvertToStaticBlock(name) but this also yields a DuplicateNameError. I guess if I create a new name consisting of the anonymous block name and the block name I want to use, this method would sort of work. Still I see two limitations with this method:

1) The block that is converted is not my customer's static block. The block converted ends up being my company's dynamic block, without the dynamic properties. Because of that I would have to programmatically change some things about the converted block to make it look like the static blocks, but in the end it will not be the static block I want it to be.

2) I am quite new to AutoCAD development and I am very curious on what I am getting wrong. In my head my method should work and I want to understand what I am currently not understanding.

I do see your suggestion as a possible workaround though. So I tried to do this:
Code: [Select]
//Turns out having a * in the string I want as a name is what made me get the InalidInputError
oldBlockRef.ConvertToStaticBlock(customerBlockName+anonymousBlockName.Subtring(1))
newBlockRef.Rotation = oldBlockRef.Rotation
...
newBlockRef.BlockTableRecord = oldBlockRef.BlockTableRecord
And now I have the right type of blocks with the name: Customer_Block_YYUXX. I would definitely prefer the name to simply being Customer_Block_YY but this is closer to a solution than I have been so far I guess and I think I can work with this so I get a solution that is good enough.

But this is not perfect, and I really want to understand what I am doing wrong just for the sake of learning. What data does the BlockRecordTable contain that make the rotations work perfectly? Might WCS and UCS (which I have no experience with) be involved in this? Why can't I create blocks with identical names? I mean, I have seen DWGs with multiple occurrences of Customer_Block_YY and I manage to create blocks with multiple occurrences of Customer_Block_YY as long as I do not set
Code: [Select]
newBlockRef.BlockTableRecord = oldBlockRef.BlockTableRecord
« Last Edit: May 13, 2019, 04:10:29 AM by jon »

kdub

  • Mesozoic relic
  • SuperMod
  • Swamp Rat
  • Posts: 1261
  • class keyThumper<T>:ILazy<T>
Re: Converting dynamic block to static block
« Reply #3 on: May 13, 2019, 05:29:54 AM »
Jon ,
I'm just running past ..

Define Blocks (.NET)
http://help.autodesk.com/view/ACD/2017/ENU/?guid=GUID-DF67671C-101D-4917-808B-DD2C5BE3C7E9

Insert Blocks (.NET)
http://help.autodesk.com/view/ACD/2017/ENU/?guid=GUID-2656E245-6EAA-41A3-ABE9-742868182821

Try to absorb these.

There are probably better examples, but essentially, in concept
the definition is similar in concept to a class definition.
the insertion instance is similar in concept to a class instance, adds the elements from the definition and applies scale, rotation, location, layer etc.

Once you grok that, it gets easier.

Attributes are the next step ...



Welcome to TheSwamp

Regards,



« Last Edit: May 13, 2019, 05:33:36 AM by kdub »
called Kerry in my other life

Sometimes the question is more important than the answer.

jon

  • Mosquito
  • Posts: 4
Re: Converting dynamic block to static block
« Reply #4 on: May 13, 2019, 12:48:02 PM »
Thank you for your explanation, you put me on the right track and I managed to put together a solution. The solution was quite simple, I just had to explode the dynamic block and store the values of the exploded block instead of the values of the dynamic block.

I will post the solution code/pseudo code within a couple of days in case anyone is interested.

kdub

  • Mesozoic relic
  • SuperMod
  • Swamp Rat
  • Posts: 1261
  • class keyThumper<T>:ILazy<T>
Re: Converting dynamic block to static block
« Reply #5 on: May 13, 2019, 10:34:51 PM »
Jon, do you need to do this process often ?

How many blocks ?

Are you likely to be able to determine the name of the clients block easily from the dynamic settings ?

I assume you will have issues if you have multiple instances of a dynamic block in the drawing.

An option may be to just replace each instance of the dynamic blocks with a client block.

It could get as sophisticated as your time/cost savings will allow ...
from selecting a dBlock and displaying a list of relevant client blocks to replace that instance using the scale, rotation etc from the original
.... rinse and repeat
you'd probably need a printed reference sheet for the operators
to :
A fully automated Database controlled 'look ma no hands' app.
Regards,

called Kerry in my other life

Sometimes the question is more important than the answer.

jon

  • Mosquito
  • Posts: 4
Re: Converting dynamic block to static block
« Reply #6 on: May 14, 2019, 04:26:56 AM »
>Jon, do you need to do this process often ?
Well I don't have to do it, but the engineers I work with have to use my plugin a couple of times every day or so.

>How many blocks ?
On the DWGs we work with, I think we typically have between 20-100 blocks. Those blocks can be any of the 20ish static blocks we got from our client.

>Are you likely to be able to determine the name of the clients block easily from the dynamic settings ?
It might not be the best solution but I have 1 DWG with the static and 1 DWG with the (unmodified) dynamic blocks. I have a created a method for importing these two DWGs so I can create named pairs:
Code: [Select]
List<string> blockPairs
string[] blockPair
blockPair =  new string[2] {"static_block_XX","dynamic_block_XX"}
blockPairs.Add(blockPair)
Of course there is a bit more to it than that, but that's the essence of it

>I assume you will have issues if you have multiple instances of a dynamic block in the drawing.
I did in the beginning but I found some help over at KeanW's blog:
https://www.keanw.com/2012/09/creating-a-selection-filter-that-finds-dynamic-blocks-in-autocad-using-net.html
So the logic behind my plugin when replacing dynamic blocks is basically:
Code: [Select]
//GetDynamicBlockNames() returns names of the format "dynamic_block_XX"
List<string> dynamicBlockNames = GetDynamicBlockNames()
List<string> anonymousNames
foreach(dynamicBlockName in dynamicBlockNames)
{
    //GetAnonymousNames() returns the anonymous names of the format "*UXX"
    anonymousNames = GetAnonymousNames(dynamicBlockName)
    foreach(anonymousName in anonymousNames)
    {
            ReplaceBlock(blockTable[anonymousName])
    }
}

The only "problem" I have now is that the plugin sometimes crashes after repeated use. For instance, if a user converts back and forth between dynamic and static blocks 5+ times I sometimes randomly get an AccessViolationError. I am quite certain this is a problem with AutoCAD's API and their DynamicBlockProperties though, since this problem does not occur if I set the Dynamic Block's rotation property instead of the DynamicBlockProperty linked to rotation. Also I get significant performance improvements when I stay away from DynamicBlockProperty.
Code: [Select]
//The faster and safer way
blkRef.Rotation = Math.PI

//The slower and less safe way
DynamicBlockReferenceProperty rotation= dynamicBlockReferencePropertyCollection[2];
rotation.Value = Math.PI

Finally here's the method I was struggling with until you got me to understand my problem
Code: [Select]
private static void AddBlock(Database db, Transaction acTrans, string nameOfBlockToInsert, BlockReference blockToReplace)
{
            BlockTable acBlockTable = (BlockTable)acTrans.GetObject(db.BlockTableId, OpenMode.ForRead);
            ObjectId acBlkRecId = acBlockTable[nameOfBlockToInsert];
            BlockReference acBlkRef = new BlockReference(blockToReplace.Origin, acBlkRecId);
            DBObjectCollection dbObjectCollection = new DBObjectCollection();
            BlockTableRecord currentSpaceTableRecord = (BlockTableRecord)acTrans.GetObject(db.CurrentSpaceId, OpenMode.ForWrite);
       
            Point3d position = new Point3d();
            Vector3d normal = new Vector3d();
            double rotation = 0;
            double scaleX = 1;
            double scaleY = 1;

            //This turned out to be the trick
            blockToReplace.Explode(dbObjectCollection);
            foreach (DBObject dbObj in dbObjectCollection)
            {
                BlockReference acEnt = (BlockReference) dbObj;

                //Consider this statement:
                rotation = acEnt.Rotation;
                //VS.
                //rotation = blockToReplace.Rotation
               
                scaleX = acEnt.ScaleFactors.X;
                scaleY = acEnt.ScaleFactors.Y;
                position = acEnt.Position;
                normal = acEnt.Normal;
                dbObj.Dispose();
            }

            acBlkRef.ScaleFactors = new Scale3d(scaleX,scaleY,1);
            acBlkRef.Position = position;
            acBlkRef.Rotation = rotation;
            acBlkRef.Normal = normal;

            currentSpaceTableRecord .AppendEntity(acBlkRef);
            acTrans.AddNewlyCreatedDBObject(acBlkRef, true);
            acBlkRef.Dispose();
}

So again, thank you for your help  :smitten: