haplokuon / netDxf

.net dxf Reader-Writer
MIT License
986 stars 400 forks source link

How to import and merge dxf files without mixing them up? #324

Closed LeiYangGH closed 2 years ago

LeiYangGH commented 2 years ago

This lib is great and with it I can successfully do read write entity operations.

My goal is to merge multiple dxf files's entities into single one new dxf file, of course each input file's entities should be placed into a seperate location to avoid overlapping.

I couldn't find built in functions to do merge. So after some experiments I came up with such solution:

        static Block ImportDxfAsBlock(string dxfFile)
        {
            DxfDocument doc = DxfDocument.Load(dxfFile);
            Block block = new Block(Hash(dxfFile));
            foreach (Layout layout in doc.Layouts)
            {
                block.Entities.AddRange(doc.Layouts.GetReferences(layout)
                    .OfType<EntityObject>().Select(x => x.Clone() as EntityObject));
            }
            return block;
        }
        static void Main(string[] args)
        {
            string[] inputs = new string[] { "../../../1.dxf", "../../../2.dxf" };
            //merge order matters
            //string[] inputs = new string[] { "../../../2.dxf", "../../../1.dxf" }; 
            double y = 0;
            DxfDocument doc = new DxfDocument(netDxf.Header.DxfVersion.AutoCad2018);
            foreach (string input in inputs)
            {
                doc.Entities.Add(new Insert(ImportDxfAsBlock(input), new Vector2(0, y)));
                y += 5000;
            }
            doc.Save("../../../merged.dxf");
        }

This code works for most of my dxf files. But a few files got mixed up result. For example, I have two dxf files:

  1. 1

  2. 2

After merge, I got: merged

The order of input file also affects the result. I have checked the two files both contain a INSERT entity named *U9, and thouoght it could possibly be the reason but as a beginner of dxf I don't know what to do with it.

Questions

  1. I might have missed some important objects to copy(such as layers, models, attributes?), could you point out?
  2. I tried a python lib ezdxf and it can import entities without mix up, maybe because it has built in importer. Is there any similar function in netdxf? Of course, there are many benifits to use netdxf than ezdxf.
  3. I tried to install by nuget(2.2.0.1) but no function named doc.Entities, so I included latest source instead. Did I used the lib in the wrong way?

Environment

  1. Win10, net 4.8, netdxf 3.0.0
  2. Minimal reproducible code pushed to github
haplokuon commented 2 years ago

All named objects, all classes that derive from TableObject, must be unique, there cannot be two with the same name. This is the case of layers, line types, styles,..., and what seems to be the case giving you problems, blocks. Two TableObjects are considered equal if they have the same name, regardless of any other of its internal properties.

When you copy an entity into another document, the named objects in the destination document will have preference over the ones on the original drawing, so any duplicates will have to be solved manually if necessary.

In your post you are talking about a block named "U9", that corresponds to a dynamic block. There are three kind of blocks that AutoCad internally creates to handle three kinds of entities. Dimensions are represented by the blocks that starts with "D", Dynamic Blocks starts with "U", and tables starts with "T". From those three only Dimensions are fully supported and they shouldn't give you problems when merging documents, but dynamic blocks and tables are not, any incompatibilities with them will have to be sorted manually.

You will have to rename the blocks giving you conflicts, you can use the Clone method that takes a new name as an argument, since internal used blocks cannot be renamed; and with this new block recreate the Insert entities that used that block.

From your Questions section:

  1. The root of the problem seems to be the duplicate "*U9" block in the original and destination. Problem that will have to be solved manually as explained before.

  2. I do not know anything about "ezdxf", and no I did implemented any method to merge two different drawings. It is not a trivial procedure, you will need to do it yourself and decide what to do with any duplicate named objects.

  3. All I upload is here at Github, I have nothing to do with the releases at nuget. Since version 2.2 many things have changed, read the changelog.txt. The actual source code is for version 3.0 that I have not release yet so consider it a beta.

LeiYangGH commented 2 years ago

Hi @haplokuon,

Thanks for you kindly comprehensive reply! During the days before you answer, I came up with with an ugly but working solution to avoid conflict names across files: collect block names of the doc, then File.ReadAllText, string.Replace those names suffixing a file hash, and load from the txt's stream instead of actual file.

Now I see your suggustion, it should be a better one, but I cannot see any overload of Clone method, I can only see one Clone mthod of EntityObject. Could you paste a code snippet of Clone with unique name? Thanks!

Note, I already tried giving a unique name in Block constructor, but that doesn't help the problem in my case.

haplokuon commented 2 years ago

Now, the internal blocks that start with "U" or "T" can be safely renamed. They are internally created to represent dynamic blocks, arrays, and tables; although the information of those objects is lost when importing the DXF, the block that represent its graphical appearance is imported.

LeiYangGH commented 2 years ago

thanks, the block name set is very useful!