• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

DomCR / ACadSharp / 22018275163

14 Feb 2026 01:30PM UTC coverage: 77.463% (-0.02%) from 77.486%
22018275163

push

github

web-flow
Merge pull request #977 from DomCR/issue-971_insert-explode-fix

Issue 971 insert explode fix

8144 of 11374 branches covered (71.6%)

Branch coverage included in aggregate %.

15 of 36 new or added lines in 6 files covered. (41.67%)

10 existing lines in 2 files now uncovered.

29454 of 37163 relevant lines covered (79.26%)

151428.29 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

55.83
/src/ACadSharp/Entities/Insert.cs
1
using ACadSharp.Attributes;
2
using ACadSharp.Extensions;
3
using ACadSharp.Objects;
4
using ACadSharp.Tables;
5
using CSMath;
6
using System;
7
using System.Collections.Generic;
8
using System.Linq;
9

10
namespace ACadSharp.Entities
11
{
12
        /// <summary>
13
        /// Represents a <see cref="Insert"/> entity.
14
        /// </summary>
15
        /// <remarks>
16
        /// Object name <see cref="DxfFileToken.EntityInsert"/> <br/>
17
        /// Dxf class name <see cref="DxfSubclassMarker.Insert"/>
18
        /// </remarks>
19
        [DxfName(DxfFileToken.EntityInsert)]
20
        [DxfSubClass(DxfSubclassMarker.Insert)]
21
        public class Insert : Entity
22
        {
23
                /// <summary>
24
                /// Attributes from the block reference
25
                /// </summary>
26
                /// <remarks>
27
                /// If an attribute should be added in this collection a definition will be added into the block reference as well
28
                /// </remarks>
29
                public SeqendCollection<AttributeEntity> Attributes { get; private set; }
19,304✔
30

31
                /// <summary>
32
                /// Gets the insert block definition.
33
                /// </summary>
34
                [DxfCodeValue(DxfReferenceType.Name, 2)]
35
                public BlockRecord Block { get; internal set; }
18,768✔
36

37
                /// <summary>
38
                /// Column count
39
                /// </summary>
40
                [DxfCodeValue(DxfReferenceType.Optional, 70)]
41
                public ushort ColumnCount { get; set; } = 1;
8,950✔
42

43
                /// <summary>
44
                /// Column spacing
45
                /// </summary>
46
                [DxfCodeValue(DxfReferenceType.Optional, 44)]
47
                public double ColumnSpacing { get; set; } = 0;
8,523✔
48

49
                /// <summary>
50
                /// True if the insert has attribute entities in it
51
                /// </summary>
52
                [DxfCodeValue(DxfReferenceType.Ignored, 66)]
53
                public bool HasAttributes
54
                { get { return this.Attributes.Any(); } }
1,323✔
55

56
                /// <inheritdoc/>
57
                public override bool HasDynamicSubclass => true;
2✔
58

59
                /// <summary>
60
                /// A 3D WCS coordinate representing the insertion or origin point.
61
                /// </summary>
62
                [DxfCodeValue(10, 20, 30)]
63
                public XYZ InsertPoint { get; set; } = XYZ.Zero;
46,275✔
64

65
                /// <summary>
66
                /// Specifies the rotation angle for the object.
67
                /// </summary>
68
                public bool IsMultiple { get { return this.RowCount > 1 || this.ColumnCount > 1; } }
1,095✔
69

70
                /// <summary>
71
                /// Specifies the three-dimensional normal unit vector for the object.
72
                /// </summary>
73
                [DxfCodeValue(210, 220, 230)]
74
                public XYZ Normal { get; set; } = XYZ.AxisZ;
10,699✔
75

76
                /// <inheritdoc/>
77
                public override string ObjectName => DxfFileToken.EntityInsert;
16,973✔
78

79
                /// <inheritdoc/>
80
                public override ObjectType ObjectType
81
                {
82
                        get
83
                        {
86✔
84
                                if (this.RowCount > 1 || this.ColumnCount > 1)
86✔
85
                                {
16✔
86
                                        return ObjectType.MINSERT;
16✔
87
                                }
88
                                else
89
                                {
70✔
90
                                        return ObjectType.INSERT;
70✔
91
                                }
92
                        }
86✔
93
                }
94

95
                /// <summary>
96
                /// Specifies the rotation angle for the object.
97
                /// </summary>
98
                /// <value>
99
                /// The rotation angle in radians.
100
                /// </value>
101
                [DxfCodeValue(DxfReferenceType.IsAngle, 50)]
102
                public double Rotation { get; set; } = 0.0;
11,371✔
103

104
                /// <summary>
105
                /// Row count
106
                /// </summary>
107
                [DxfCodeValue(DxfReferenceType.Optional, 71)]
108
                public ushort RowCount { get; set; } = 1;
8,974✔
109

110
                /// <summary>
111
                /// Row spacing
112
                /// </summary>
113
                [DxfCodeValue(DxfReferenceType.Optional, 45)]
114
                public double RowSpacing { get; set; } = 0;
8,523✔
115

116
                /// <summary>
117
                /// Gets or set the spatial filter entry for this <see cref="Insert"/> entity.
118
                /// </summary>
119
                public SpatialFilter SpatialFilter
120
                {
121
                        get
122
                        {
×
123
                                if (this.XDictionary != null
×
124
                                        && this.XDictionary.TryGetEntry(Filter.FilterEntryName, out CadDictionary filters))
×
125
                                {
×
126
                                        return filters.GetEntry<SpatialFilter>(SpatialFilter.SpatialFilterEntryName);
×
127
                                }
128

129
                                return null;
×
130
                        }
×
131
                        set
132
                        {
11✔
133
                                if (this.XDictionary == null)
11✔
134
                                {
11✔
135
                                        this.CreateExtendedDictionary();
11✔
136
                                }
11✔
137

138
                                if (!this.XDictionary.TryGetEntry(Filter.FilterEntryName, out CadDictionary filters))
11✔
139
                                {
11✔
140
                                        filters = new CadDictionary(Filter.FilterEntryName);
11✔
141
                                        this.XDictionary.Add(filters);
11✔
142
                                }
11✔
143

144
                                filters.Remove(SpatialFilter.SpatialFilterEntryName);
11✔
145
                                filters.Add(SpatialFilter.SpatialFilterEntryName, value);
11✔
146
                        }
11✔
147
                }
148

149
                /// <inheritdoc/>
150
                public override string SubclassMarker => this.IsMultiple ? DxfSubclassMarker.MInsert : DxfSubclassMarker.Insert;
322!
151

152
                /// <summary>
153
                /// X scale factor.
154
                /// </summary>
155
                [DxfCodeValue(41)]
156
                public double XScale
157
                {
158
                        get
159
                        {
440✔
160
                                return this._xscale;
440✔
161
                        }
440✔
162
                        set
163
                        {
4,936✔
164
                                if (value.Equals(0))
4,936✔
165
                                {
1✔
166
                                        string name = nameof(this.XScale);
1✔
167
                                        throw new ArgumentOutOfRangeException(name, value, $"{name} value must be none zero.");
1✔
168
                                }
169
                                this._xscale = value;
4,935✔
170
                        }
4,935✔
171
                }
172

173
                /// <summary>
174
                /// Y scale factor.
175
                /// </summary>
176
                [DxfCodeValue(42)]
177
                public double YScale
178
                {
179
                        get
180
                        {
379✔
181
                                return this._yscale;
379✔
182
                        }
379✔
183
                        set
184
                        {
4,732✔
185
                                if (value.Equals(0))
4,732✔
186
                                {
1✔
187
                                        string name = nameof(this.YScale);
1✔
188
                                        throw new ArgumentOutOfRangeException(name, value, $"{name} value must be none zero.");
1✔
189
                                }
190
                                this._yscale = value;
4,731✔
191
                        }
4,731✔
192
                }
193

194
                /// <summary>
195
                /// Z scale factor.
196
                /// </summary>
197
                [DxfCodeValue(43)]
198
                public double ZScale
199
                {
200
                        get
201
                        {
373✔
202
                                return this._zscale;
373✔
203
                        }
373✔
204
                        set
205
                        {
4,721✔
206
                                if (value.Equals(0))
4,721✔
207
                                {
1✔
208
                                        string name = nameof(this.ZScale);
1✔
209
                                        throw new ArgumentOutOfRangeException(name, value, $"{name} value must be none zero.");
1✔
210
                                }
211
                                this._zscale = value;
4,720✔
212
                        }
4,720✔
213
                }
214

215
                private double _xscale = 1;
8,171✔
216

217
                private double _yscale = 1;
8,171✔
218

219
                private double _zscale = 1;
8,171✔
220

221
                /// <summary>
222
                /// Constructor to reference an insert to a block record
223
                /// </summary>
224
                /// <param name="block">Block Record to reference</param>
225
                /// <exception cref="ArgumentNullException"></exception>
226
                public Insert(BlockRecord block) : this()
95✔
227
                {
95✔
228
                        if (block is null) throw new ArgumentNullException(nameof(block));
95!
229

230
                        if (block.Document != null)
95✔
231
                        {
46✔
232
                                this.Block = (BlockRecord)block.Clone();
46✔
233
                        }
46✔
234
                        else
235
                        {
49✔
236
                                this.Block = block;
49✔
237
                        }
49✔
238

239
                        foreach (var item in block.AttributeDefinitions)
335✔
240
                        {
25✔
241
                                this.Attributes.Add(new AttributeEntity(item));
25✔
242
                        }
25✔
243
                }
95✔
244

245
                internal Insert() : base()
8,171✔
246
                {
8,171✔
247
                        this.Attributes = new SeqendCollection<AttributeEntity>(this);
8,171✔
248
                }
8,171✔
249

250
                /// <inheritdoc/>
251
                public override void ApplyTransform(Transform transform)
252
                {
×
253
                        XYZ newPosition = transform.ApplyTransform(this.InsertPoint);
×
254
                        XYZ newNormal = this.transformNormal(transform, this.Normal);
×
255

256
                        Matrix3 transOW = Matrix3.ArbitraryAxis(this.Normal);
×
257
                        transOW *= Matrix3.RotationZ(this.Rotation);
×
258

259
                        Matrix3 transWO = Matrix3.ArbitraryAxis(newNormal);
×
260
                        transWO = transWO.Transpose();
×
261

262
                        var transformation = new Matrix3(transform.Matrix);
×
263
                        XYZ v = transOW * XYZ.AxisX;
×
264
                        v = transformation * v;
×
265
                        v = transWO * v;
×
266
                        double newRotation = new XY(v.X, v.Y).GetAngle();
×
267

268
                        transWO = Matrix3.RotationZ(newRotation).Transpose() * transWO;
×
269

270
                        XYZ s = transOW * new XYZ(this.XScale, this.YScale, this.ZScale);
×
271
                        s = transformation * s;
×
272
                        s = transWO * s;
×
273
                        XYZ newScale = new XYZ(
×
274
                                MathHelper.IsZero(s.X) ? MathHelper.Epsilon : s.X,
×
275
                                MathHelper.IsZero(s.Y) ? MathHelper.Epsilon : s.Y,
×
276
                                MathHelper.IsZero(s.Z) ? MathHelper.Epsilon : s.Z);
×
277

278
                        this.Normal = newNormal;
×
279
                        this.InsertPoint = newPosition;
×
280
                        this.XScale = newScale.X;
×
281
                        this.YScale = newScale.Y;
×
282
                        this.ZScale = newScale.Z;
×
283
                        this.Rotation = newRotation;
×
284

285
                        foreach (AttributeEntity att in this.Attributes)
×
286
                        {
×
287
                                att.ApplyTransform(transform);
×
288
                        }
×
289
                }
×
290

291
                /// <inheritdoc/>
292
                public override CadObject Clone()
293
                {
182✔
294
                        Insert clone = (Insert)base.Clone();
182✔
295

296
                        clone.Block = (BlockRecord)this.Block?.Clone();
182✔
297

298
                        clone.Attributes = new SeqendCollection<AttributeEntity>(clone);
182✔
299
                        foreach (var att in this.Attributes)
548✔
300
                        {
1✔
301
                                clone.Attributes.Add((AttributeEntity)att.Clone());
1✔
302
                        }
1✔
303

304
                        return clone;
182✔
305
                }
182✔
306

307
                /// <summary>
308
                /// Returns an enumerable collection of entities representing the exploded contents of the block, with all entities
309
                /// transformed into the current coordinate system.
310
                /// </summary>
311
                /// <remarks>The returned entities are clones or converted equivalents of the original block entities, with
312
                /// geometric transformations applied. For example, arcs and circles are converted to their corresponding geometric
313
                /// representations. The original block and its entities remain unchanged.</remarks>
314
                /// <returns>An enumerable collection of <see cref="Entity"/> objects that make up the exploded block. Each entity is
315
                /// transformed according to the block's transform. The collection may be empty if the block contains no entities.</returns>
316
                public IEnumerable<Entity> Explode()
317
                {
×
318
                        Transform transform = this.GetTransform();
×
319
                        foreach (var e in this.Block.Entities)
×
320
                        {
×
321
                                Entity c;
322
                                switch (e)
×
323
                                {
324
                                        case Arc arc:
325
                                                arc.GetEndVertices(out XYZ start, out XYZ end);
×
326

327
                                                Arc a;
NEW
328
                                                if (arc.IsCounterClockWise)
×
NEW
329
                                                {
×
NEW
330
                                                        a = new Arc(
×
NEW
331
                                                                transform.ApplyTransform(arc.Center),
×
NEW
332
                                                                transform.ApplyTransform(end),
×
NEW
333
                                                                transform.ApplyTransform(start),
×
NEW
334
                                                                arc.Normal);
×
NEW
335
                                                }
×
336
                                                else
NEW
337
                                                {
×
NEW
338
                                                        a = new Arc(
×
NEW
339
                                                                transform.ApplyTransform(arc.Center),
×
NEW
340
                                                                transform.ApplyTransform(start),
×
NEW
341
                                                                transform.ApplyTransform(end),
×
NEW
342
                                                                arc.Normal);
×
NEW
343
                                                }
×
344

345
                                                a.MatchProperties(e);
×
346

347
                                                yield return a;
×
348
                                                continue;
×
349
                                        case Circle circle:
350
                                                c = new Ellipse()
×
351
                                                {
×
352
                                                        MajorAxisEndPoint = XYZ.AxisX * circle.Radius,
×
353
                                                        RadiusRatio = 1,
×
354
                                                        Center = circle.Center,
×
355
                                                        Normal = circle.Normal,
×
356
                                                };
×
357
                                                c.MatchProperties(e);
×
358
                                                break;
×
359
                                        default:
360
                                                c = e.CloneTyped();
×
361
                                                break;
×
362
                                }
363

364
                                c.ApplyTransform(transform);
×
365

366
                                yield return c;
×
367
                        }
×
368
                }
×
369

370
                /// <inheritdoc/>
371
                public override BoundingBox GetBoundingBox()
372
                {
2✔
373
                        BoundingBox box = this.Block.GetBoundingBox();
2✔
374

375
                        var scale = new XYZ(this.XScale, this.YScale, this.ZScale);
2✔
376
                        var min = box.Min * scale + this.InsertPoint;
2✔
377
                        var max = box.Max * scale + this.InsertPoint;
2✔
378

379
                        return new BoundingBox(min, max);
2✔
380
                }
2✔
381

382
                /// <summary>
383
                /// Get the transform that will be applied to the entities in the <see cref="BlockRecord"/> when this entity is processed.
384
                /// </summary>
385
                /// <returns></returns>
386
                public Transform GetTransform()
387
                {
1✔
388
                        var world = Matrix4.GetArbitraryAxis(this.Normal);
1✔
389
                        var translation = Transform.CreateTranslation(this.InsertPoint);
1✔
390
                        var rotation = Transform.CreateRotation(XYZ.AxisZ, this.Rotation);
1✔
391
                        var scale = Transform.CreateScaling(new XYZ(this.XScale, this.YScale, this.ZScale));
1✔
392

393
                        return new Transform(world * translation.Matrix * rotation.Matrix * scale.Matrix);
1✔
394
                }
1✔
395

396
                /// <summary>
397
                /// Updates all attribute definitions contained in the block reference as <see cref="AttributeDefinition"/> entities in the insert.
398
                /// </summary>
399
                /// <remarks>
400
                /// This will update the attributes based on their <see cref="AttributeBase.Tag"/>.
401
                /// </remarks>
402
                public void UpdateAttributes()
403
                {
×
404
                        var atts = this.Attributes.ToArray();
×
405

406
                        foreach (AttributeEntity att in atts)
×
407
                        {
×
408
                                //Tags are not unique, is it needed? check how the different applications link the atts
409
                                if (!this.Block.AttributeDefinitions.Select(d => d.Tag).Contains(att.Tag))
×
410
                                {
×
411
                                        this.Attributes.Remove(att);
×
412
                                }
×
413
                        }
×
414

415
                        foreach (AttributeDefinition attdef in this.Block.AttributeDefinitions)
×
416
                        {
×
417
                                if (!this.Attributes.Select(d => d.Tag).Contains(attdef.Tag))
×
418
                                {
×
419
                                        AttributeEntity att = new AttributeEntity(attdef);
×
420

421
                                        this.Attributes.Add(att);
×
422
                                }
×
423
                        }
×
424
                }
×
425

426
                internal override void AssignDocument(CadDocument doc)
427
                {
8,350✔
428
                        base.AssignDocument(doc);
8,350✔
429

430
                        doc.RegisterCollection(this.Attributes);
8,350✔
431

432
                        //Should only be triggered for internal use
433
                        if (this.Block == null)
8,350✔
434
                                return;
7,880✔
435

436
                        if (doc.BlockRecords.TryGetValue(this.Block.Name, out BlockRecord blk))
470✔
437
                        {
171✔
438
                                this.Block = blk;
171✔
439
                        }
171✔
440
                        else
441
                        {
299✔
442
                                doc.BlockRecords.Add(this.Block);
299✔
443
                        }
299✔
444
                }
8,350✔
445

446
                internal override void UnassignDocument()
447
                {
205✔
448
                        this.Block = (BlockRecord)this.Block.Clone();
205✔
449
                        this.Document.UnregisterCollection(this.Attributes);
205✔
450

451
                        base.UnassignDocument();
205✔
452
                }
205✔
453
        }
454
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc