diff --git a/ACadSharp.Tests/Common/Factory.cs b/ACadSharp.Tests/Common/Factory.cs index bb5a508d1..7f57fc148 100644 --- a/ACadSharp.Tests/Common/Factory.cs +++ b/ACadSharp.Tests/Common/Factory.cs @@ -58,7 +58,7 @@ private static CadObject createObject(Type type, Type original, bool randomize) return null; } - if (type == typeof(XRecrod) + if (type == typeof(XRecord) || type == typeof(PlotSettings) || type == typeof(Material) || type == typeof(MLStyle) diff --git a/ACadSharp.Tests/IO/DWG/DwgWriterTests.cs b/ACadSharp.Tests/IO/DWG/DwgWriterTests.cs index c8f56175c..3d1b66693 100644 --- a/ACadSharp.Tests/IO/DWG/DwgWriterTests.cs +++ b/ACadSharp.Tests/IO/DWG/DwgWriterTests.cs @@ -11,17 +11,27 @@ namespace ACadSharp.Tests.IO.DWG { public class DwgWriterTests : IOTestsBase { + public static TheoryData Entities { get; } + public DwgWriterTests(ITestOutputHelper output) : base(output) { } + static DwgWriterTests() + { + Entities = new TheoryData + { + EntityFactory.Create(), + EntityFactory.Create(), + }; + } + [Theory] [MemberData(nameof(Versions))] public void WriteEmptyTest(ACadVersion version) { + string path = Path.Combine(_samplesOutFolder, $"out_empty_sample_{version}.dwg"); CadDocument doc = new CadDocument(); doc.Header.Version = version; - string path = Path.Combine(_samplesOutFolder, $"out_empty_sample_{version}.dwg"); - using (var wr = new DwgWriter(path, doc)) { if (isSupportedVersion(version)) @@ -43,6 +53,13 @@ public void WriteEmptyTest(ACadVersion version) //this.checkDwgDocumentInAutocad(Path.GetFullPath(path)); } + [Theory] + [MemberData(nameof(Entities))] + public void WriteSingleEntityFile(Entity entity) + { + + } + [Theory] [MemberData(nameof(Versions))] public void WriteTest(ACadVersion version) @@ -50,12 +67,13 @@ public void WriteTest(ACadVersion version) CadDocument doc = new CadDocument(); doc.Header.Version = version; - addEntities(doc); + this.addEntities(doc); string path = Path.Combine(_samplesOutFolder, $"out_sample_{version}.dwg"); using (var wr = new DwgWriter(path, doc)) { + wr.OnNotification += this.onNotification; if (isSupportedVersion(version)) { wr.Write(); diff --git a/ACadSharp.Tests/IO/IOTests.cs b/ACadSharp.Tests/IO/IOTests.cs index 91bf63434..4cf8947a1 100644 --- a/ACadSharp.Tests/IO/IOTests.cs +++ b/ACadSharp.Tests/IO/IOTests.cs @@ -24,6 +24,19 @@ public void EmptyDwgToDxf() this.writeDxfFile(pathOut, doc, true); } + [Theory] + [MemberData(nameof(DwgFilePaths))] + public void DwgToDwg(string test) + { + CadDocument doc = DwgReader.Read(test); + + string file = Path.GetFileNameWithoutExtension(test); + string pathOut = Path.Combine(_samplesOutFolder, $"{file}_out.dwg"); + + //accoreconsole always fails because cannot recover the file + this.writeDwgFile(pathOut, doc, false); + } + [Theory] [MemberData(nameof(DwgFilePaths))] public void DwgToDxf(string test) @@ -48,7 +61,28 @@ public void DxfToDxf(string test) [Theory] [MemberData(nameof(DwgFilePaths))] - public void DwgEntitiesToNewFile(string test) + public void DwgEntitiesToDwgFile(string test) + { + CadDocument doc = DwgReader.Read(test); + + CadDocument transfer = new CadDocument(); + transfer.Header.Version = doc.Header.Version; + + List entities = new List(doc.Entities); + foreach (var item in entities) + { + Entity e = doc.Entities.Remove(item); + transfer.Entities.Add(e); + } + + string file = Path.GetFileNameWithoutExtension(test); + string pathOut = Path.Combine(_samplesOutFolder, $"{file}_moved_out.dwg"); + this.writeDwgFile(pathOut, transfer, false); + } + + [Theory] + [MemberData(nameof(DwgFilePaths))] + public void DwgEntitiesToDxfFile(string test) { CadDocument doc = DwgReader.Read(test); @@ -67,6 +101,33 @@ public void DwgEntitiesToNewFile(string test) this.writeDxfFile(pathOut, transfer, true); } + private void writeCadFile(string file, CadWriterBase writer, bool check) + { + using (writer) + { + writer.OnNotification += this.onNotification; + writer.Write(); + } + + if (check) + this.checkDxfDocumentInAutocad(Path.GetFullPath(file)); + } + + private void writeDwgFile(string file, CadDocument doc, bool check) + { + if (doc.Header.Version < ACadVersion.AC1014 || doc.Header.Version > ACadVersion.AC1018) + return; + + using (DwgWriter writer = new DwgWriter(file, doc)) + { + writer.OnNotification += this.onNotification; + writer.Write(); + } + + if (check) + this.checkDwgDocumentInAutocad(Path.GetFullPath(file)); + } + private void writeDxfFile(string file, CadDocument doc, bool check) { using (DxfWriter writer = new DxfWriter(file, doc, false)) @@ -78,5 +139,17 @@ private void writeDxfFile(string file, CadDocument doc, bool check) if (check) this.checkDxfDocumentInAutocad(Path.GetFullPath(file)); } + + private void writeDwgFile(string file, CadDocument doc) + { + if (doc.Header.Version > ACadVersion.AC1018) + return; + + using (DwgWriter writer = new DwgWriter(file, doc)) + { + writer.OnNotification += this.onNotification; + writer.Write(); + } + } } } diff --git a/ACadSharp.Tests/IO/LocalSampleTests.cs b/ACadSharp.Tests/IO/LocalSampleTests.cs index af362195d..13199b60a 100644 --- a/ACadSharp.Tests/IO/LocalSampleTests.cs +++ b/ACadSharp.Tests/IO/LocalSampleTests.cs @@ -36,7 +36,7 @@ public void ReadUserDwg(string test) CadDocument doc = DwgReader.Read(test, this._dwgConfiguration, this.onNotification); - //return; + return; string outPath = Path.Combine(Path.GetDirectoryName(test), $"{Path.GetFileNameWithoutExtension(test)}.out.dxf"); using (DxfWriter writer = new DxfWriter(outPath, doc, false)) diff --git a/ACadSharp/CadObjectCollection.cs b/ACadSharp/CadObjectCollection.cs index 388ef9abd..3b7796cc7 100644 --- a/ACadSharp/CadObjectCollection.cs +++ b/ACadSharp/CadObjectCollection.cs @@ -23,6 +23,14 @@ public class CadObjectCollection : IObservableCollection /// public int Count { get { return this._entries.Count; } } + public T this[int index] + { + get + { + return this._entries.ElementAtOrDefault(index); + } + } + protected readonly HashSet _entries = new HashSet(); public CadObjectCollection(CadObject owner) diff --git a/ACadSharp/Entities/Dimension.cs b/ACadSharp/Entities/Dimension.cs index c43ebfb40..a86214502 100644 --- a/ACadSharp/Entities/Dimension.cs +++ b/ACadSharp/Entities/Dimension.cs @@ -167,13 +167,13 @@ public DimensionStyle Style private readonly DimensionType _flags; + private DimensionStyle _style = DimensionStyle.Default; + protected Dimension(DimensionType type) { this._flags = type; } - private DimensionStyle _style = DimensionStyle.Default; - public override CadObject Clone() { Dimension clone = (Dimension)base.Clone(); diff --git a/ACadSharp/Entities/Insert.cs b/ACadSharp/Entities/Insert.cs index 1b91d0121..1f00b4fd6 100644 --- a/ACadSharp/Entities/Insert.cs +++ b/ACadSharp/Entities/Insert.cs @@ -19,7 +19,20 @@ namespace ACadSharp.Entities public class Insert : Entity { /// - public override ObjectType ObjectType => ObjectType.INSERT; + public override ObjectType ObjectType + { + get + { + if (this.RowCount > 1 || this.ColumnCount > 1) + { + return ObjectType.MINSERT; + } + else + { + return ObjectType.INSERT; + } + } + } /// public override string ObjectName => DxfFileToken.EntityInsert; diff --git a/ACadSharp/Entities/Leader.cs b/ACadSharp/Entities/Leader.cs index 786cf3c68..bc93c9a25 100644 --- a/ACadSharp/Entities/Leader.cs +++ b/ACadSharp/Entities/Leader.cs @@ -1,6 +1,7 @@ using ACadSharp.Attributes; using ACadSharp.Tables; using CSMath; +using System; using System.Collections.Generic; namespace ACadSharp.Entities @@ -29,7 +30,26 @@ public class Leader : Entity /// Dimension Style /// [DxfCodeValue(3)] - public DimensionStyle Style { get; set; } = DimensionStyle.Default; + public DimensionStyle Style + { + get { return this._style; } + set + { + if (value == null) + { + throw new ArgumentNullException(nameof(value)); + } + + if (this.Document != null) + { + this._style = this.updateTable(value, this.Document.DimensionStyles); + } + else + { + this._style = value; + } + } + } /// /// Arrowhead flag @@ -112,6 +132,36 @@ public class Leader : Entity [DxfCodeValue(213, 223, 233)] public XYZ AnnotationOffset { get; set; } = XYZ.Zero; + private DimensionStyle _style = DimensionStyle.Default; + + internal override void AssignDocument(CadDocument doc) + { + base.AssignDocument(doc); + + this._style = this.updateTable(this.Style, doc.DimensionStyles); + + doc.DimensionStyles.OnRemove += this.tableOnRemove; + } + + internal override void UnassignDocument() + { + this.Document.DimensionStyles.OnRemove -= this.tableOnRemove; + + base.UnassignDocument(); + + this.Style = (DimensionStyle)this.Style.Clone(); + } + + protected override void tableOnRemove(object sender, CollectionChangedEventArgs e) + { + base.tableOnRemove(sender, e); + + if (e.Item.Equals(this.Style)) + { + this.Style = this.Document.DimensionStyles[DimensionStyle.DefaultName]; + } + } + public override CadObject Clone() { Leader clone = (Leader)base.Clone(); diff --git a/ACadSharp/Entities/PolyFaceMesh.cs b/ACadSharp/Entities/PolyFaceMesh.cs index 1412f7184..1be598c65 100644 --- a/ACadSharp/Entities/PolyFaceMesh.cs +++ b/ACadSharp/Entities/PolyFaceMesh.cs @@ -24,12 +24,12 @@ public class PolyfaceMesh : Polyline /// public override string SubclassMarker => DxfSubclassMarker.PolyfaceMesh; - public SeqendCollection Faces { get; } + public CadObjectCollection Faces { get; } public PolyfaceMesh() { this.Vertices.OnAdd += this.verticesOnAdd; - this.Faces = new SeqendCollection(this); + this.Faces = new CadObjectCollection(this); } public override IEnumerable Explode() @@ -45,5 +45,17 @@ private void verticesOnAdd(object sender, CollectionChangedEventArgs e) throw new ArgumentException($"Wrong vertex type {e.Item.SubclassMarker} for {this.SubclassMarker}"); } } + + internal override void AssignDocument(CadDocument doc) + { + base.AssignDocument(doc); + doc.RegisterCollection(this.Faces); + } + + internal override void UnassignDocument() + { + this.Document.UnregisterCollection(this.Faces); + base.UnassignDocument(); + } } } diff --git a/ACadSharp/Entities/PolyfaceMesh.cs b/ACadSharp/Entities/PolyfaceMesh.cs index 1412f7184..1be598c65 100644 --- a/ACadSharp/Entities/PolyfaceMesh.cs +++ b/ACadSharp/Entities/PolyfaceMesh.cs @@ -24,12 +24,12 @@ public class PolyfaceMesh : Polyline /// public override string SubclassMarker => DxfSubclassMarker.PolyfaceMesh; - public SeqendCollection Faces { get; } + public CadObjectCollection Faces { get; } public PolyfaceMesh() { this.Vertices.OnAdd += this.verticesOnAdd; - this.Faces = new SeqendCollection(this); + this.Faces = new CadObjectCollection(this); } public override IEnumerable Explode() @@ -45,5 +45,17 @@ private void verticesOnAdd(object sender, CollectionChangedEventArgs e) throw new ArgumentException($"Wrong vertex type {e.Item.SubclassMarker} for {this.SubclassMarker}"); } } + + internal override void AssignDocument(CadDocument doc) + { + base.AssignDocument(doc); + doc.RegisterCollection(this.Faces); + } + + internal override void UnassignDocument() + { + this.Document.UnregisterCollection(this.Faces); + base.UnassignDocument(); + } } } diff --git a/ACadSharp/Entities/Shape.cs b/ACadSharp/Entities/Shape.cs index 5b1218be6..0801c1622 100644 --- a/ACadSharp/Entities/Shape.cs +++ b/ACadSharp/Entities/Shape.cs @@ -39,7 +39,7 @@ public class Shape : Entity /// Size /// [DxfCodeValue(40)] - public double Size { get; set; } = 0.0; + public double Size { get; set; } = 1.0; /// /// Shape name diff --git a/ACadSharp/Entities/Spline.cs b/ACadSharp/Entities/Spline.cs index 9c550477f..002b3a11b 100644 --- a/ACadSharp/Entities/Spline.cs +++ b/ACadSharp/Entities/Spline.cs @@ -1,6 +1,5 @@ using ACadSharp.Attributes; using CSMath; -using System; using System.Collections.Generic; namespace ACadSharp.Entities @@ -44,7 +43,7 @@ public class Spline : Entity /// Degree of the spline curve /// [DxfCodeValue(71)] - public double Degree { get; set; } + public int Degree { get; set; } /// /// Number of knots diff --git a/ACadSharp/IO/DWG/DwgStreamReaders/DwgObjectReader.cs b/ACadSharp/IO/DWG/DwgStreamReaders/DwgObjectReader.cs index e4a1cd37c..1c49bfcb9 100644 --- a/ACadSharp/IO/DWG/DwgStreamReaders/DwgObjectReader.cs +++ b/ACadSharp/IO/DWG/DwgStreamReaders/DwgObjectReader.cs @@ -1193,8 +1193,10 @@ private void readCommonAttData(AttributeBase att) { //R2010+: if (this.R2010Plus) + { //Version RC ? att.Version = this._objectReader.ReadByte(); + } //R2018+: if (this.R2018Plus) @@ -1204,27 +1206,12 @@ private void readCommonAttData(AttributeBase att) switch (att.AttributeType) { - case AttributeType.SingleLine: - //Common: - //Tag TV 2 - att.Tag = this._textReader.ReadVariableText(); - //Field length BS 73 unused - short length = this._objectReader.ReadBitShort(); - //Flags RC 70 NOT bit-pair - coded. - att.Flags = (AttributeFlags)this._objectReader.ReadByte(); - //R2007 +: - if (this.R2007Plus) - //Lock position flag B 280 - att.IsReallyLocked = this._objectReader.ReadBit(); - - break; case AttributeType.MultiLine: case AttributeType.ConstantMultiLine: //Attribute type is multi line //MTEXT fields … Here all fields of an embedded MTEXT object //are written, starting from the Entmode //(entity mode). The owner handle can be 0. - short dataSize = this._objectReader.ReadBitShort(); if (dataSize > 0) { @@ -1235,14 +1222,22 @@ private void readCommonAttData(AttributeBase att) //Unknown BS 72? Value 0. this._objectReader.ReadBitShort(); } - att.Tag = this._mergedReaders.ReadVariableText(); - length = this._mergedReaders.ReadBitShort(); - att.Flags = (AttributeFlags)this._mergedReaders.ReadByte(); - att.IsReallyLocked = this._mergedReaders.ReadBit(); - break; - default: break; } + + //Common: + //Tag TV 2 + att.Tag = this._textReader.ReadVariableText(); + //Field length BS 73 unused + short length = this._objectReader.ReadBitShort(); + //Flags RC 70 NOT bit-pair - coded. + att.Flags = (AttributeFlags)this._objectReader.ReadByte(); + //R2007 +: + if (this.R2007Plus) + { + //Lock position flag B 280 + att.IsReallyLocked = this._objectReader.ReadBit(); + } } #endregion Text entities @@ -1261,9 +1256,7 @@ private CadTemplate readDocumentTable(Table table, CadTableTemplate tem //Layers: Numentries BL 70 Counts layer "0", too int numentries = this._objectReader.ReadBitLong(); for (int i = 0; i < numentries; ++i) - //Handle refs H NULL(soft pointer) - //xdicobjhandle(hard owner) - //the apps(soft owner) + //numentries handles in the file (soft owner) template.EntryHandles.Add(this.handleReference()); return template; @@ -1400,7 +1393,7 @@ private void readInsertCommonData(CadInsertTemplate template) template.OwnedObjectsCount = 0; //R2004+: - if (this.R2004Plus & template.HasAtts) + if (this.R2004Plus && template.HasAtts) //Owned Object Count BL Number of objects owned by this object. template.OwnedObjectsCount = this._objectReader.ReadBitLong(); } @@ -1569,12 +1562,15 @@ private CadTemplate readPolyline3D() this.readCommonEntityData(template); //Flags RC 70 NOT DIRECTLY THE 75. Bit-coded (76543210): - byte num1 = this._objectReader.ReadByte(); + byte flags = this._objectReader.ReadByte(); + //75 0 : Splined(75 value is 5) //1 : Splined(75 value is 6) - bool splined = ((uint)num1 & 0b1) > 0; + bool splined = ((uint)flags & 0b1) > 0; + //Should assign pline.SmoothSurface ?? + //(If either is set, set 70 bit 2(4) to indicate splined.) - bool splined1 = ((uint)num1 & 0b10) > 0; + bool splined1 = ((uint)flags & 0b10) > 0; if (splined | splined1) { @@ -2361,7 +2357,7 @@ private CadTemplate readSpline() //The scenario flag becomes 1 if the knot parameter is Custom or has no fit data, otherwise 2. spline.KnotParameterization = (KnotParameterization)this._mergedReaders.ReadBitLong(); - scenario = ((spline.KnotParameterization == KnotParameterization.Custom || (spline.Flags1 & SplineFlags1.UseKnotParameter) == 0) ? 1 : 2); + scenario = (spline.KnotParameterization == KnotParameterization.Custom || (spline.Flags1 & SplineFlags1.UseKnotParameter) == 0) ? 1 : 2; } else if (scenario == 2) { @@ -2383,18 +2379,6 @@ private CadTemplate readSpline() bool flag = false; switch (scenario) { - case 2: - //Fit Tol BD 44 - spline.FitTolerance = this._objectReader.ReadBitDouble(); - //Beg tan vec 3BD 12 Beginning tangent direction vector (normalized). - spline.StartTangent = this._objectReader.Read3BitDouble(); - //End tan vec 3BD 13 Ending tangent direction vector (normalized). - spline.EndTangent = this._objectReader.Read3BitDouble(); - //num fit pts BL 74 Number of fit points. - //Stored as a LONG, although it is defined in DXF as a short. - //You can see this if you create a spline with >=256 fit points - numfitpts = this._objectReader.ReadBitLong(); - break; case 1: //Rational B flag bit 2 if (this._objectReader.ReadBit()) @@ -2420,7 +2404,20 @@ private CadTemplate readSpline() //Weight B Seems to be an echo of the 4 bit on the flag for "weights present". flag = this._objectReader.ReadBit(); break; + case 2: + //Fit Tol BD 44 + spline.FitTolerance = this._objectReader.ReadBitDouble(); + //Beg tan vec 3BD 12 Beginning tangent direction vector (normalized). + spline.StartTangent = this._objectReader.Read3BitDouble(); + //End tan vec 3BD 13 Ending tangent direction vector (normalized). + spline.EndTangent = this._objectReader.Read3BitDouble(); + //num fit pts BL 74 Number of fit points. + //Stored as a LONG, although it is defined in DXF as a short. + //You can see this if you create a spline with >=256 fit points + numfitpts = this._objectReader.ReadBitLong(); + break; } + for (int i = 0; i < numknots; i++) { //Knot BD knot value @@ -2721,9 +2718,10 @@ private CadTemplate readLeader() leader.Vertices.Add(this._objectReader.Read3BitDouble()); } - //Origin 3BD --- The leader plane origin (by default it’s the first point). - this._objectReader.Read3BitDouble(); //Is necessary to store this value? - //Extrusion 3BD 210 + //Origin 3BD --- The leader plane origin (by default it’s the first point) + //Is necessary to store this value? + this._objectReader.Read3BitDouble(); + //Extrusion 3BD 210 leader.Normal = this._objectReader.Read3BitDouble(); //x direction 3BD 211 leader.HorizontalDirection = this._objectReader.Read3BitDouble(); @@ -3081,8 +3079,9 @@ private CadTemplate readLayer() //Color CMC 62 layer.Color = this._mergedReaders.ReadCmColor(); - //Handle refs H Layer control (soft pointer) + //TODO: This is not the Layer control handle template.LayerControlHandle = this.handleReference(); + //Handle refs H Layer control (soft pointer) //[Reactors(soft pointer)] //xdicobjhandle(hard owner) //External reference block handle(hard pointer) @@ -3163,6 +3162,8 @@ private CadTemplate readLTypeControlObject() this.readDocumentTable(template.CadObject, template); //the linetypes, ending with BYLAYER and BYBLOCK. + //all are soft owner references except BYLAYER and + //BYBLOCK, which are hard owner references. template.EntryHandles.Add(this.handleReference()); template.EntryHandles.Add(this.handleReference()); @@ -4032,7 +4033,7 @@ private CadTemplate readGroup() //Numhandles BL # objhandles in this group int numhandles = this._objectReader.ReadBitLong(); for (int index = 0; index < numhandles; ++index) - //Handle refs H parenthandle (soft pointer) + //the entries in the group(hard pointer) template.Handles.Add(this.handleReference()); return template; @@ -4102,12 +4103,16 @@ 1024 512 //R2018+: if (this.R2018Plus) + { //Line type handle H Line type handle (hard pointer) elementTemplate.LinetypeHandle = this.handleReference(); + } //Before R2018: else + { //Ltindex BS Linetype index (yes, index) elementTemplate.LinetypeIndex = this._objectReader.ReadBitShort(); + } template.ElementTemplates.Add(elementTemplate); mlineStyle.Elements.Add(element); @@ -4135,17 +4140,17 @@ private CadTemplate readLWPolyline() if ((flags & 0x200) != 0) lwPolyline.Flags |= LwPolylineFlags.Closed; - if ((flags & 4u) != 0) + if ((flags & 0x4u) != 0) { lwPolyline.ConstantWidth = this._objectReader.ReadBitDouble(); } - if ((flags & 8u) != 0) + if ((flags & 0x8u) != 0) { lwPolyline.Elevation = this._objectReader.ReadBitDouble(); } - if ((flags & 2u) != 0) + if ((flags & 0x2u) != 0) { lwPolyline.Thickness = this._objectReader.ReadBitDouble(); } @@ -4158,18 +4163,19 @@ private CadTemplate readLWPolyline() int nvertices = this._objectReader.ReadBitLong(); int nbulges = 0; - if (((uint)flags & 0x10u) != 0) + if (((uint)flags & 0x10) != 0) { nbulges = this._objectReader.ReadBitLong(); } + int nids = 0; - if (((uint)flags & 0x400u) != 0) + if (((uint)flags & 0x400) != 0) { nids = this._objectReader.ReadBitLong(); } int ndiffwidth = 0; - if (((uint)flags & 0x20u) != 0) + if (((uint)flags & 0x20) != 0) { ndiffwidth = this._objectReader.ReadBitLong(); } @@ -4429,7 +4435,6 @@ private CadTemplate readHatch() //numboundaryobjhandles BL 97 Number of boundary object handles for this path int numboundaryobjhandles = this._objectReader.ReadBitLong(); - for (int h = 0; h < numboundaryobjhandles; h++) { //boundaryhandle H 330 boundary handle(soft pointer) @@ -4600,7 +4605,7 @@ private CadTemplate readWipeout() private CadTemplate readXRecord() { - XRecrod xRecord = new XRecrod(); + XRecord xRecord = new XRecord(); CadXRecordTemplate template = new CadXRecordTemplate(xRecord); this.readCommonNonEntityData(template); @@ -4629,10 +4634,10 @@ private CadTemplate readXRecord() case GroupCodeValueType.None: break; case GroupCodeValueType.String: - xRecord.Entries.Add(new XRecrod.Entry(code, this._objectReader.ReadTextUnicode())); + xRecord.Entries.Add(new XRecord.Entry(code, this._objectReader.ReadTextUnicode())); break; case GroupCodeValueType.Point3D: - xRecord.Entries.Add(new XRecrod.Entry(code, + xRecord.Entries.Add(new XRecord.Entry(code, new XYZ( this._objectReader.ReadDouble(), this._objectReader.ReadDouble(), @@ -4640,25 +4645,25 @@ private CadTemplate readXRecord() ))); break; case GroupCodeValueType.Double: - xRecord.Entries.Add(new XRecrod.Entry(code, this._objectReader.ReadDouble())); + xRecord.Entries.Add(new XRecord.Entry(code, this._objectReader.ReadDouble())); break; case GroupCodeValueType.Int16: - xRecord.Entries.Add(new XRecrod.Entry(code, this._objectReader.ReadShort())); + xRecord.Entries.Add(new XRecord.Entry(code, this._objectReader.ReadShort())); break; case GroupCodeValueType.Int32: - xRecord.Entries.Add(new XRecrod.Entry(code, this._objectReader.ReadRawLong())); + xRecord.Entries.Add(new XRecord.Entry(code, this._objectReader.ReadRawLong())); break; case GroupCodeValueType.Int64: - xRecord.Entries.Add(new XRecrod.Entry(code, this._objectReader.ReadRawLong())); + xRecord.Entries.Add(new XRecord.Entry(code, this._objectReader.ReadRawLong())); break; case GroupCodeValueType.Handle: - xRecord.Entries.Add(new XRecrod.Entry(code, this._objectReader.ReadTextUnicode())); + xRecord.Entries.Add(new XRecord.Entry(code, this._objectReader.ReadTextUnicode())); break; case GroupCodeValueType.Bool: - xRecord.Entries.Add(new XRecrod.Entry(code, this._objectReader.ReadByte() > 0)); + xRecord.Entries.Add(new XRecord.Entry(code, this._objectReader.ReadByte() > 0)); break; case GroupCodeValueType.Chunk: - xRecord.Entries.Add(new XRecrod.Entry(code, this._objectReader.ReadBytes(this._objectReader.ReadByte()))); + xRecord.Entries.Add(new XRecord.Entry(code, this._objectReader.ReadBytes(this._objectReader.ReadByte()))); break; default: break; @@ -4667,8 +4672,10 @@ private CadTemplate readXRecord() //R2000+: if (this.R2000Plus) + { //Cloning flag BS 280 xRecord.ClonningFlags = (DictionaryCloningFlags)this._objectReader.ReadBitShort(); + } long size = this._objectInitialPos + (long)(this._size * 8U) - 7L; while (this._handlesReader.PositionInBits() < size) @@ -4814,9 +4821,9 @@ private void readPlotSettings(PlotSettings plot) plot.PaperSize = this._textReader.ReadVariableText(); //Plot origin 2BD 46,47 plotsettings origin offset in millimeters - var plotOrigin = this._objectReader.Read2BitDouble(); - plot.PlotOriginX = plotOrigin.X; - plot.PlotOriginY = plotOrigin.Y; + plot.PlotOriginX = this._objectReader.ReadBitDouble(); + plot.PlotOriginY = this._objectReader.ReadBitDouble(); + //Paper units BS 72 plotsettings plot paper units plot.PaperUnits = (PlotPaperUnits)this._objectReader.ReadBitShort(); //Plot rotation BS 73 plotsettings plot rotation @@ -4825,13 +4832,11 @@ private void readPlotSettings(PlotSettings plot) plot.PlotType = (PlotType)this._objectReader.ReadBitShort(); //Window min 2BD 48,49 plotsettings plot window area lower left - var windowLowerLeft = this._objectReader.Read2BitDouble(); - plot.WindowLowerLeftX = windowLowerLeft.X; - plot.WindowLowerLeftY = windowLowerLeft.Y; + plot.WindowLowerLeftX = this._objectReader.ReadBitDouble(); + plot.WindowLowerLeftY = this._objectReader.ReadBitDouble(); //Window max 2BD 140,141 plotsettings plot window area upper right - var windowUpperLeft = this._objectReader.Read2BitDouble(); - plot.WindowUpperLeftX = windowUpperLeft.X; - plot.WindowUpperLeftY = windowUpperLeft.Y; + plot.WindowUpperLeftX = this._objectReader.ReadBitDouble(); + plot.WindowUpperLeftY = this._objectReader.ReadBitDouble(); //R13 - R2000 Only: if (this._version >= ACadVersion.AC1012 && this._version <= ACadVersion.AC1015) diff --git a/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Common.cs b/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Common.cs index c06278900..5bf31cbdd 100644 --- a/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Common.cs +++ b/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Common.cs @@ -8,8 +8,6 @@ namespace ACadSharp.IO.DWG { internal partial class DwgObjectWriter : DwgSectionIO { - public override string SectionName => DwgSectionDefinition.AcDbObjects; - private void registerObject(CadObject cadObject) { this._writer.WriteSpearShift(); @@ -119,7 +117,9 @@ private void writeCommonData(CadObject cadObject) case ObjectType.UNLISTED: case ObjectType.INVALID: case ObjectType.UNUSED: - throw new NotImplementedException(); + this.notify($"CadObject type: {cadObject.ObjectType} fullname: {cadObject.GetType().FullName}", NotificationType.NotImplemented); + return; + throw new NotImplementedException($"CadObject type: {cadObject.ObjectType} fullname: {cadObject.GetType().FullName}"); default: this._writer.WriteObjectType(cadObject.ObjectType); break; @@ -322,7 +322,7 @@ private void writeExtendedData(ExtendedDataDictionary data) private void writeReactorsAndDictionaryHandle(CadObject cadObject) { - //TODO: Write reactors and dictionary + //TODO: Write reactors //Numreactors S number of reactors in this object this._writer.WriteBitLong(0); @@ -331,12 +331,17 @@ private void writeReactorsAndDictionaryHandle(CadObject cadObject) // //[Reactors (soft pointer)] // template.CadObject.Reactors.Add(this.handleReference(), null); + bool noDictionary = cadObject.XDictionary == null; + //R2004+: if (this.R2004Plus) { - _writer.WriteBit(true); - //_writer.WriteBit(cadObject.XDictionary == null); - //this._writer.HandleReference(DwgReferenceType.HardOwnership, cadObject.XDictionary); + + this._writer.WriteBit(noDictionary); + if (!noDictionary) + { + this._writer.HandleReference(DwgReferenceType.HardOwnership, cadObject.XDictionary); + } } else { @@ -350,6 +355,12 @@ private void writeReactorsAndDictionaryHandle(CadObject cadObject) //Has DS binary data B If 1 then this object has associated binary data stored in the data store this._writer.WriteBit(false); } + + if (!noDictionary) + { + _dictionaries.Add(cadObject.XDictionary.Handle, cadObject.XDictionary); + _objects.Enqueue(cadObject.XDictionary); + } } private byte getEntMode(Entity entity) diff --git a/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Entities.cs b/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Entities.cs index 6c4b7a551..7eb6776e1 100644 --- a/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Entities.cs +++ b/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Entities.cs @@ -1,6 +1,9 @@ using ACadSharp.Entities; using CSMath; using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; namespace ACadSharp.IO.DWG { @@ -8,6 +11,22 @@ internal partial class DwgObjectWriter : DwgSectionIO { private void writeEntity(Entity entity) { + List children = new List(); + Seqend seqend = null; + + //Ignored Entities + switch (entity) + { + case AttributeEntity: + case MText: + case Shape: + case Solid3D: + //Unlisted + case Wipeout: + this.notify($"Entity type not implemented {entity.GetType().FullName}", NotificationType.NotImplemented); + return; + } + this.writeCommonEntityData(entity); switch (entity) @@ -23,62 +42,208 @@ private void writeEntity(Entity entity) switch (dimension) { case DimensionLinear linear: - writeDimensionLinear(linear); + this.writeDimensionLinear(linear); break; case DimensionAligned aligned: - writeDimensionAligned(aligned); + this.writeDimensionAligned(aligned); break; case DimensionRadius radius: - writeDimensionRadius(radius); + this.writeDimensionRadius(radius); break; case DimensionAngular2Line angular2Line: - writeDimensionAngular2Line(angular2Line); + this.writeDimensionAngular2Line(angular2Line); break; case DimensionAngular3Pt angular3pt: - writeDimensionAngular3Pt(angular3pt); + this.writeDimensionAngular3Pt(angular3pt); break; case DimensionDiameter diamenter: - writeDimensionDiameter(diamenter); + this.writeDimensionDiameter(diamenter); break; case DimensionOrdinate ordinate: - writeDimensionOrdinate(ordinate); + this.writeDimensionOrdinate(ordinate); break; - //default: - // throw new NotImplementedException(); + default: + throw new NotImplementedException($"Dimension not implemented : {entity.GetType().FullName}"); } break; case Ellipse ellipse: this.writeEllipse(ellipse); break; + case Insert insert: + this.writeInsert(insert); + + children.AddRange(insert.Attributes); + + seqend = insert.Attributes.Seqend; + break; + case Face3D face3D: + this.writeFace3D(face3D); + break; + case Hatch hatch: + this.writeHatch(hatch); + break; + case Leader l: + this.writeLeader(l); + break; case Line l: this.writeLine(l); break; + case LwPolyline lwpolyline: + this.writeLwPolyline(lwpolyline); + break; + case MLine mLine: + this.writeMLine(mLine); + break; case Point p: this.writePoint(p); break; + case Polyline pline: + switch (pline) + { + case PolyfaceMesh faceMesh: + this.writePolyfaceMesh(faceMesh); + children.AddRange(faceMesh.Faces); + break; + case Polyline2D pline2d: + this.writePolyline2D(pline2d); + break; + case Polyline3D pline3d: + this.writePolyline3D(pline3d); + break; + default: + throw new NotImplementedException($"Polyline not implemented : {entity.GetType().FullName}"); + } + children.AddRange(pline.Vertices); + seqend = pline.Vertices.Seqend; + break; + case Ray ray: + this.writeRay(ray); + break; + case Shape shape: + this.writeShape(shape); + break; + case Solid solid: + this.writeSolid(solid); + break; + case Solid3D solid3d: + this.writeSolid3D(solid3d); + break; + case Spline spline: + this.writeSpline(spline); + break; case TextEntity text: - this.writeTextEntity(text); + switch (text) + { + case AttributeEntity att: + this.writeAttribute(att); + break; + case AttributeDefinition attdef: + this.writeAttDefinition(attdef); + break; + case TextEntity textEntity: + this.writeTextEntity(textEntity); + break; + default: + throw new NotImplementedException($"Entity not implemented : {entity.GetType().FullName}"); + } + break; + case Vertex vertex: + switch (vertex) + { + case Vertex2D vertex2D: + this.writeVertex2D(vertex2D); + break; + case VertexFaceRecord faceRecord: + this.writeFaceRecord(faceRecord); + break; + case Vertex3D: + case VertexFaceMesh: + this.writeVertex(vertex); + break; + default: + throw new NotImplementedException($"Vertex not implemented : {entity.GetType().FullName}"); + } + break; + case Viewport viewport: + this.writeViewport(viewport); + break; + case XLine xline: + this.writeXLine(xline); break; default: - this.notify($"Entity not implemented : {entity.GetType().FullName}", NotificationType.NotImplemented); - return; + throw new NotImplementedException($"Entity not implemented : {entity.GetType().FullName}"); } this.registerObject(entity); + + this.writeChildEntities(children, seqend); } private void writeArc(Arc arc) { - //this.writeCircle(arc); - this._writer.Write3BitDouble(arc.Center); - this._writer.WriteBitDouble(arc.Radius); - this._writer.WriteBitThickness(arc.Thickness); - this._writer.WriteBitExtrusion(arc.Normal); + this.writeCircle(arc); this._writer.WriteBitDouble(arc.StartAngle); this._writer.WriteBitDouble(arc.EndAngle); } + private void writeAttribute(AttributeEntity att) + { + this.writeCommonAttData(att); + } + + private void writeAttDefinition(AttributeDefinition attdef) + { + this.writeCommonAttData(attdef); + + //R2010+: + if (this.R2010Plus) + //Version RC ? Repeated?? + this._writer.WriteByte(attdef.Version); + + //Common: + //Prompt TV 3 + this._writer.WriteVariableText(attdef.Prompt); + } + + private void writeCommonAttData(AttributeBase att) + { + this.writeTextEntity(att); + + //R2010+: + if (this.R2010Plus) + { + //Version RC ? + this._writer.WriteByte(att.Version); + } + + //R2018+: + if (this.R2018Plus) + { + this._writer.WriteByte((byte)att.AttributeType); + + if (att.AttributeType == AttributeType.MultiLine || att.AttributeType == AttributeType.ConstantMultiLine) + { + throw new NotImplementedException("Multiple line Attribute not implemented"); + } + } + + //Common: + //Tag TV 2 + this._writer.WriteVariableText(att.Tag); + //Field length BS 73 unused + this._writer.WriteBitShort(0); + //Flags RC 70 NOT bit-pair - coded. + this._writer.WriteByte((byte)att.Flags); + + //R2007 +: + if (this.R2007Plus) + { + //Lock position flag B 280 + this._writer.WriteBit(att.IsReallyLocked); + } + } + private void writeCircle(Circle circle) { this._writer.Write3BitDouble(circle.Center); @@ -162,7 +327,7 @@ private void writeCommonDimensionData(Dimension dimension) private void writeDimensionLinear(DimensionLinear dimension) { - writeDimensionAligned(dimension); + this.writeDimensionAligned(dimension); //Dim rot BD 50 Linear dimension rotation; see DXF documentation. this._writer.WriteBitDouble(dimension.Rotation); @@ -233,25 +398,706 @@ private void writeDimensionDiameter(DimensionDiameter dimension) this._writer.WriteBitDouble(dimension.LeaderLength); } - private void writeDimensionOrdinate(DimensionOrdinate dimension) + private void writeDimensionOrdinate(DimensionOrdinate dimension) + { + //Common: + //10 - pt 3BD 10 See DXF documentation. + this._writer.Write3BitDouble(dimension.DefinitionPoint); + //13 - pt 3BD 13 See DXF documentation. + this._writer.Write3BitDouble(dimension.FeatureLocation); + //14 - pt 3BD 14 See DXF documentation. + this._writer.Write3BitDouble(dimension.LeaderEndpoint); + } + + private void writeEllipse(Ellipse ellipse) + { + this._writer.Write3BitDouble(ellipse.Center); + this._writer.Write3BitDouble(ellipse.EndPoint); + this._writer.Write3BitDouble(ellipse.Normal); + this._writer.WriteBitDouble(ellipse.RadiusRatio); + this._writer.WriteBitDouble(ellipse.StartParameter); + this._writer.WriteBitDouble(ellipse.EndParameter); + } + + private void writeInsert(Insert insert) + { + //Ins pt 3BD 10 + this._writer.Write3BitDouble(insert.InsertPoint); + + //R13-R14 Only: + if (this.R13_14Only) + { + //X Scale BD 41 + this._writer.WriteBitDouble(insert.XScale); + //Y Scale BD 42 + this._writer.WriteBitDouble(insert.YScale); + //Z Scale BD 43 + this._writer.WriteBitDouble(insert.ZScale); + } + + //R2000 + Only: + if (this.R2000Plus) + { + //Data flags BB + //Scale Data Varies with Data flags: + if (insert.XScale == 1.0 && insert.YScale == 1.0 && insert.ZScale == 1.0) + { + //11 - scale is (1.0, 1.0, 1.0), no data stored. + this._writer.Write2Bits(3); + } + else if (insert.XScale == insert.YScale && insert.XScale == insert.ZScale) + { + //10 – 41 value stored as a RD, and 42 & 43 values are not stored, assumed equal to 41 value. + this._writer.Write2Bits(2); + this._writer.WriteRawDouble(insert.XScale); + } + else if (insert.XScale == 1.0) + { + //01 – 41 value is 1.0, 2 DD’s are present, each using 1.0 as the default value, representing the 42 and 43 values. + this._writer.Write2Bits(1); + this._writer.WriteBitDoubleWithDefault(insert.YScale, 1.0); + this._writer.WriteBitDoubleWithDefault(insert.ZScale, 1.0); + } + else + { + //00 – 41 value stored as a RD, followed by a 42 value stored as DD (use 41 for default value), and a 43 value stored as a DD(use 41 value for default value). + this._writer.Write2Bits(0); + this._writer.WriteRawDouble(insert.XScale); + this._writer.WriteBitDoubleWithDefault(insert.YScale, insert.XScale); + this._writer.WriteBitDoubleWithDefault(insert.ZScale, insert.XScale); + } + } + + //Common: + //Rotation BD 50 + this._writer.WriteBitDouble(insert.Rotation); + //Extrusion 3BD 210 + this._writer.Write3BitDouble(insert.Normal); + //Has ATTRIBs B 66 Single bit; 1 if ATTRIBs follow. + this._writer.WriteBit(insert.HasAttributes); + + //R2004+: + if (this.R2004Plus && insert.HasAttributes) + { + //Owned Object Count BL Number of objects owned by this object. + this._writer.WriteBitLong(insert.Attributes.Count); + } + + if (insert.ObjectType == ObjectType.MINSERT) + { + //Common: + //Numcols BS 70 + this._writer.WriteBitShort((short)insert.ColumnCount); + //Numrows BS 71 + this._writer.WriteBitShort((short)insert.RowCount); + //Col spacing BD 44 + this._writer.WriteBitDouble(insert.ColumnSpacing); + //Row spacing BD 45 + this._writer.WriteBitDouble(insert.RowSpacing); + } + + //Common: + //Common Entity Handle Data + //H 2 BLOCK HEADER(hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, insert.Block); + + if (!insert.HasAttributes) + { + return; + } + + //R13 - R2000: + if (this._version >= ACadVersion.AC1012 && this._version <= ACadVersion.AC1015) + { + this._writer.HandleReference(DwgReferenceType.SoftPointer, insert.Attributes.First()); + this._writer.HandleReference(DwgReferenceType.SoftPointer, insert.Attributes.Last()); + } + //R2004+: + else if (this.R2004Plus) + { + foreach (AttributeEntity att in insert.Attributes) + { + //H[ATTRIB(hard owner)] Repeats “Owned Object Count” times. + this._writer.HandleReference(DwgReferenceType.HardOwnership, att); + } + } + + //Common: + //H[SEQEND(hard owner)] if 66 bit set + this._writer.HandleReference(DwgReferenceType.HardOwnership, insert.Attributes.Seqend); + } + + private void writeFace3D(Face3D face) + { + //R13 - R14 Only: + if (this.R13_14Only) + { + //1st corner 3BD 10 + this._writer.Write3BitDouble(face.FirstCorner); + //2nd corner 3BD 11 + this._writer.Write3BitDouble(face.SecondCorner); + //3rd corner 3BD 12 + this._writer.Write3BitDouble(face.ThirdCorner); + //4th corner 3BD 13 + this._writer.Write3BitDouble(face.FourthCorner); + //Invis flags BS 70 Invisible edge flags + this._writer.WriteBitShort((short)face.Flags); + } + + //R2000 +: + if (this.R2000Plus) + { + bool noFlags = face.Flags == InvisibleEdgeFlags.None; + //Has no flag ind. B + this._writer.WriteBit(noFlags); + + bool zIsZero = face.FirstCorner.Z == 0.0; + //Z is zero bit B + this._writer.WriteBit(zIsZero); + + //1st corner x RD 10 + this._writer.WriteRawDouble(face.FirstCorner.X); + //1st corner y RD 20 + this._writer.WriteRawDouble(face.FirstCorner.Y); + + if (!zIsZero) + { + //1st corner z RD 30 Present only if “Z is zero bit” is 0. + this._writer.WriteRawDouble(face.FirstCorner.Z); + } + + //2nd corner 3DD 11 Use 10 value as default point + this._writer.Write3BitDoubleWithDefault(face.SecondCorner, face.FirstCorner); + //3rd corner 3DD 12 Use 11 value as default point + this._writer.Write3BitDoubleWithDefault(face.ThirdCorner, face.SecondCorner); + //4th corner 3DD 13 Use 12 value as default point + this._writer.Write3BitDoubleWithDefault(face.FourthCorner, face.ThirdCorner); + + //Invis flags BS 70 Present it “Has no flag ind.” is 0. + if (!noFlags) + { + this._writer.WriteBitShort((short)face.Flags); + } + } + } + + private void writeMLine(MLine mline) + { + //Scale BD 40 + this._writer.WriteBitDouble(mline.ScaleFactor); + //Just EC top (0), bottom(2), or center(1) + this._writer.WriteByte((byte)mline.Justification); + //Base point 3BD 10 + this._writer.Write3BitDouble(mline.StartPoint); + //Extrusion 3BD 210 etc. + this._writer.Write3BitDouble(mline.Normal); + + //Openclosed BS open (1), closed(3) + this._writer.WriteBitShort((short)(mline.Flags.HasFlag(MLineFlags.Closed) ? 3 : 1)); + + int nlines = 0; + if (mline.Vertices.Count > 0) + { + nlines = mline.Vertices.First().Segments.Count; + } + //Linesinstyle RC 73 + this._writer.WriteByte((byte)nlines); + + //Numverts BS 72 + this._writer.WriteBitShort((short)mline.Vertices.Count); + + foreach (var v in mline.Vertices) + { + //vertex 3BD + this._writer.Write3BitDouble(v.Position); + //vertex direction 3BD + this._writer.Write3BitDouble(v.Direction); + //miter direction 3BD + this._writer.Write3BitDouble(v.Miter); + + // All the Vertices must have the same number of segments + for (int i = 0; i < nlines; i++) + { + var element = v.Segments[i]; + + //numsegparms BS + this._writer.WriteBitShort((short)element.Parameters.Count); + foreach (double p in element.Parameters) + { + //segparm BD segment parameter + this._writer.WriteBitDouble(p); + } + + //numareafillparms BS + this._writer.WriteBitShort((short)element.AreaFillParameters.Count); + foreach (double afp in element.AreaFillParameters) + { + //areafillparm BD area fill parameter + this._writer.WriteBitDouble(afp); + } + } + } + + //H mline style oject handle (hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, mline.MLStyle); + } + + private void writeLwPolyline(LwPolyline lwPolyline) + { + bool nbulges = false; + bool ndiffwidth = false; + foreach (LwPolyline.Vertex item in lwPolyline.Vertices) + { + if (!nbulges && item.Bulge != 0.0) + { + nbulges = true; + } + if (!ndiffwidth && (item.StartWidth != 0.0 || item.EndWidth != 0.0)) + { + ndiffwidth = true; + } + } + + short flags = 0; + + if (lwPolyline.Flags.HasFlag(LwPolylineFlags.Plinegen)) + { + flags = (short)(flags | 0x100); + } + + if (lwPolyline.Flags.HasFlag(LwPolylineFlags.Closed)) + { + flags = (short)(flags | 0x200); + } + + if (lwPolyline.ConstantWidth != 0.0) + { + flags = (short)(flags | 0x4); + } + + if (lwPolyline.Elevation != 0.0) + { + flags = (short)(flags | 0x8); + } + + if (lwPolyline.Thickness != 0.0) + { + flags = (short)(flags | 2); + } + + if (lwPolyline.Normal != XYZ.AxisZ) + { + flags = (short)(flags | 1); + } + + if (nbulges) + { + flags = (short)(flags | 0x10); + } + //Skip ids, not necessary + if (ndiffwidth) + { + flags = (short)(flags | 0x20); + } + + //B : bytes containing the LWPOLYLINE entity data. + //This excludes the common entity data. + //More specifically: it starts at the LWPOLYLINE flags (BS), and ends with the width array (BD). + this._writer.WriteBitShort(flags); + + if (lwPolyline.ConstantWidth != 0.0) + { + this._writer.WriteBitDouble(lwPolyline.ConstantWidth); + } + if (lwPolyline.Elevation != 0.0) + { + this._writer.WriteBitDouble(lwPolyline.Elevation); + } + if (lwPolyline.Thickness != 0.0) + { + this._writer.WriteBitDouble(lwPolyline.Thickness); + } + if (lwPolyline.Normal != XYZ.AxisZ) + { + this._writer.Write3BitDouble(lwPolyline.Normal); + } + + this._writer.WriteBitLong(lwPolyline.Vertices.Count); + + if (nbulges) + { + this._writer.WriteBitLong(lwPolyline.Vertices.Count); + } + + if (ndiffwidth) + { + this._writer.WriteBitLong(lwPolyline.Vertices.Count); + } + + if (this.R13_14Only) + { + for (int i = 0; i < lwPolyline.Vertices.Count; i++) + { + this._writer.Write2RawDouble(lwPolyline.Vertices[i].Location); + } + } + + if (this.R2000Plus && lwPolyline.Vertices.Count > 0) + { + LwPolyline.Vertex last = lwPolyline.Vertices[0]; + this._writer.Write2RawDouble(last.Location); + for (int j = 1; j < lwPolyline.Vertices.Count; j++) + { + LwPolyline.Vertex curr = lwPolyline.Vertices[j]; + this._writer.Write2BitDoubleWithDefault(curr.Location, last.Location); + last = curr; + } + } + + if (nbulges) + { + for (int k = 0; k < lwPolyline.Vertices.Count; k++) + { + this._writer.WriteBitDouble(lwPolyline.Vertices[k].Bulge); + } + } + + if (ndiffwidth) + { + for (int l = 0; l < lwPolyline.Vertices.Count; l++) + { + this._writer.WriteBitDouble(lwPolyline.Vertices[l].StartWidth); + this._writer.WriteBitDouble(lwPolyline.Vertices[l].EndWidth); + } + } + } + + private void writeHatch(Hatch hatch) + { + //R2004+: + if (this.R2004Plus) + { + HatchGradientPattern gradient = hatch.GradientColor; //TODO: set default ?? HatchGradientPattern.Default; + + //Is Gradient Fill BL 450 Non-zero indicates a gradient fill is used. + this._writer.WriteBitLong(gradient.Enabled ? 1 : 0); + + //Reserved BL 451 + this._writer.WriteBitLong(gradient.Reserved); + //Gradient Angle BD 460 + this._writer.WriteBitDouble(gradient.Angle); + //Gradient Shift BD 461 + this._writer.WriteBitDouble(gradient.Shift); + //Single Color Grad.BL 452 + this._writer.WriteBitLong(gradient.IsSingleColorGradient ? 1 : 0); + //Gradient Tint BD 462 + this._writer.WriteBitDouble(gradient.ColorTint); + + //# of Gradient Colors BL 453 + this._writer.WriteBitLong(gradient.Colors.Count); + foreach (GradientColor color in gradient.Colors) + { + //Gradient Value double BD 463 + this._writer.WriteBitDouble(color.Value); + //RGB Color + this._writer.WriteCmColor(color.Color); + } + + //Gradient Name TV 470 + this._writer.WriteVariableText(gradient.Name); + } + + //Common: + //Z coord BD 30 X, Y always 0.0 + this._writer.WriteBitDouble(hatch.Elevation); + //Extrusion 3BD 210 + this._writer.Write3BitDouble(hatch.Normal); + //Name TV 2 name of hatch + this._writer.WriteVariableText(hatch.Pattern.Name); + //Solidfill B 70 1 if solidfill, else 0 + this._writer.WriteBit(hatch.IsSolid); + //Associative B 71 1 if associative, else 0 + this._writer.WriteBit(hatch.IsAssociative); + + //Numpaths BL 91 Number of paths enclosing the hatch + this._writer.WriteBitLong(hatch.Paths.Count); + bool hasDerivedBoundary = false; + foreach (Hatch.BoundaryPath boundaryPath in hatch.Paths) + { + //Pathflag BL 92 Path flag + this._writer.WriteBitLong((int)boundaryPath.Flags); + + if (boundaryPath.Flags.HasFlag(BoundaryPathFlags.Derived)) + { + hasDerivedBoundary = true; + } + + if (boundaryPath.Flags.HasFlag(BoundaryPathFlags.Polyline)) + { + //TODO: Polyline may need to be treated different than the regular edges + Hatch.BoundaryPath.Polyline pline = boundaryPath.Edges.First() as Hatch.BoundaryPath.Polyline; + + //bulgespresent B 72 bulges are present if 1 + this._writer.WriteBit(pline.HasBulge); + //closed B 73 1 if closed + this._writer.WriteBit(pline.IsClosed); + + //numpathsegs BL 91 number of path segments + this._writer.WriteBitLong(pline.Vertices.Count); + foreach (var vertex in pline.Vertices) + { + this._writer.Write2RawDouble(vertex); + if (pline.HasBulge) + { + this._writer.WriteBitDouble(pline.Bulge); + } + } + } + else + { + //Numpathsegs BL 93 number of segments in this path + this._writer.WriteBitLong(boundaryPath.Edges.Count); + foreach (var edge in boundaryPath.Edges) + { + //pathtypestatus RC 72 type of path + this._writer.WriteByte((byte)edge.Type); + + switch (edge) + { + case Hatch.BoundaryPath.Line line: + //pt0 2RD 10 first endpoint + this._writer.Write2RawDouble(line.Start); + //pt1 2RD 11 second endpoint + this._writer.Write2RawDouble(line.End); + break; + case Hatch.BoundaryPath.Arc arc: + //pt0 2RD 10 center + this._writer.Write2RawDouble(arc.Center); + //radius BD 40 radius + this._writer.WriteBitDouble(arc.Radius); + //startangle BD 50 start angle + this._writer.WriteBitDouble(arc.StartAngle); + //endangle BD 51 endangle + this._writer.WriteBitDouble(arc.EndAngle); + //isccw B 73 1 if counter clockwise, otherwise 0 + this._writer.WriteBit(arc.CounterClockWise); + break; + case Hatch.BoundaryPath.Ellipse ellispe: + //pt0 2RD 10 center + this._writer.Write2RawDouble(ellispe.Center); + //endpoint 2RD 11 endpoint of major axis + this._writer.Write2RawDouble(ellispe.MajorAxisEndPoint); + //minormajoratio BD 40 ratio of minor to major axis + this._writer.WriteBitDouble(ellispe.MinorToMajorRatio); + //startangle BD 50 start angle + this._writer.WriteBitDouble(ellispe.StartAngle); + //endangle BD 51 endangle + this._writer.WriteBitDouble(ellispe.EndAngle); + //isccw B 73 1 if counter clockwise; otherwise 0 + this._writer.WriteBit(ellispe.CounterClockWise); + break; + case Hatch.BoundaryPath.Spline splineEdge: + //degree BL 94 degree of the spline + this._writer.WriteBitLong(splineEdge.Degree); + //isrational B 73 1 if rational(has weights), else 0 + this._writer.WriteBit(splineEdge.Rational); + //isperiodic B 74 1 if periodic, else 0 + this._writer.WriteBit(splineEdge.Periodic); + + //numknots BL 95 number of knots + this._writer.WriteBitLong(splineEdge.Knots.Count); + //numctlpts BL 96 number of control points + this._writer.WriteBitLong(splineEdge.ControlPoints.Count); + foreach (double k in splineEdge.Knots) + { + //knot BD 40 knot value + this._writer.WriteBitDouble(k); + } + + for (int p = 0; p < splineEdge.ControlPoints.Count; ++p) + { + //pt0 2RD 10 control point + this._writer.Write2RawDouble((XY)splineEdge.ControlPoints[p]); + + if (splineEdge.Rational) + //weight BD 40 weight + this._writer.WriteBitDouble(splineEdge.ControlPoints[p].Z); + } + + //R24: + if (this.R2010Plus) + { + //Numfitpoints BL 97 number of fit points + this._writer.WriteBitLong(splineEdge.FitPoints.Count); + if (splineEdge.FitPoints.Any()) + { + foreach (XY fp in splineEdge.FitPoints) + { + //Fitpoint 2RD 11 + this._writer.Write2RawDouble(fp); + } + + //Start tangent 2RD 12 + this._writer.Write2RawDouble(splineEdge.StartTangent); + //End tangent 2RD 13 + this._writer.Write2RawDouble(splineEdge.EndTangent); + } + } + break; + default: + throw new ArgumentException($"Unrecognized Boundary type: {boundaryPath.GetType().FullName}"); + } + } + } + + //numboundaryobjhandles BL 97 Number of boundary object handles for this path + this._writer.WriteBitLong(boundaryPath.Entities.Count); + foreach (Entity e in boundaryPath.Entities) + { + //boundaryhandle H 330 boundary handle(soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, e); + } + } + + //style BS 75 style of hatch 0==odd parity, 1==outermost, 2==whole area + this._writer.WriteBitShort((short)hatch.Style); + //patterntype BS 76 pattern type 0==user-defined, 1==predefined, 2==custom + this._writer.WriteBitShort((short)hatch.PatternType); + + if (!hatch.IsSolid) + { + HatchPattern pattern = hatch.Pattern; + this._writer.WriteBitDouble(hatch.PatternAngle); + this._writer.WriteBitDouble(hatch.PatternScale); + this._writer.WriteBit(hatch.IsDouble); + + _writer.WriteBitShort((short)pattern.Lines.Count); + foreach (var line in pattern.Lines) + { + //angle BD 53 line angle + _writer.WriteBitDouble(line.Angle); + //pt0 2BD 43 / 44 pattern through this point(X, Y) + _writer.Write2BitDouble(line.BasePoint); + //offset 2BD 45 / 56 pattern line offset + _writer.Write2BitDouble(line.Offset); + + // numdashes BS 79 number of dash length items + _writer.WriteBitShort((short)line.DashLengths.Count); + foreach (double dl in line.DashLengths) + { + //dashlength BD 49 dash length + _writer.WriteBitDouble(dl); + } + } + } + + if (hasDerivedBoundary) + { + //pixelsize BD 47 pixel size + this._writer.WriteBitDouble(hatch.PixelSize); + } + + //numseedpoints BL 98 number of seed points + this._writer.WriteBitLong(hatch.SeedPoints.Count); + foreach (XY sp in hatch.SeedPoints) + { + //pt0 2RD 10 seed point + this._writer.Write2RawDouble(sp); + } + } + + private void writeLeader(Leader leader) { + //Unknown bit B --- Always seems to be 0. + this._writer.WriteBit(false); + + //Annot type BS --- Annotation type (NOT bit-coded): + this._writer.WriteBitShort((short)leader.CreationType); + //path type BS --- + this._writer.WriteBitShort((short)leader.PathType); + + //numpts BL --- number of points + this._writer.WriteBitLong(leader.Vertices.Count); + foreach (XYZ v in leader.Vertices) + { + //point 3BD 10 As many as counter above specifies. + this._writer.Write3BitDouble(v); + } + + //Origin 3BD --- The leader plane origin (by default it’s the first point). + this._writer.Write3BitDouble(leader.Vertices.FirstOrDefault()); + //Extrusion 3BD 210 + this._writer.Write3BitDouble(leader.Normal); + //x direction 3BD 211 + this._writer.Write3BitDouble(leader.HorizontalDirection); + //offsettoblockinspt 3BD 212 Used when the BLOCK option is used. Seems to be an unused feature. + this._writer.Write3BitDouble(leader.BlockOffset); + + //R14+: + if (this._version >= ACadVersion.AC1014) + { + //Endptproj 3BD --- A non-planar leader gives a point that projects the endpoint back to the annotation. + this._writer.Write3BitDouble(leader.AnnotationOffset); + } + + //R13-R14 Only: + if (this.R13_14Only) + { + //DIMGAP BD --- The value of DIMGAP in the associated DIMSTYLE at the time of creation, multiplied by the dimscale in that dimstyle. + this._writer.WriteBitDouble(leader.Style.DimensionLineGap); + } + + //Common: - //10 - pt 3BD 10 See DXF documentation. - this._writer.Write3BitDouble(dimension.DefinitionPoint); - //13 - pt 3BD 13 See DXF documentation. - this._writer.Write3BitDouble(dimension.FeatureLocation); - //14 - pt 3BD 14 See DXF documentation. - this._writer.Write3BitDouble(dimension.LeaderEndpoint); - } + if (this._version <= ACadVersion.AC1021) + { + //Box height BD 40 MTEXT extents height. (A text box is slightly taller, probably by some DIMvar amount.) + this._writer.WriteBitDouble(leader.TextHeight); + //Box width BD 41 MTEXT extents width. (A text box is slightly wider, probably by some DIMvar amount.) + this._writer.WriteBitDouble(leader.TextWidth); + } - private void writeEllipse(Ellipse ellipse) - { - this._writer.Write3BitDouble(ellipse.Center); - this._writer.Write3BitDouble(ellipse.EndPoint); - this._writer.Write3BitDouble(ellipse.Normal); - this._writer.WriteBitDouble(ellipse.RadiusRatio); - this._writer.WriteBitDouble(ellipse.StartParameter); - this._writer.WriteBitDouble(ellipse.EndParameter); + //Hooklineonxdir B hook line is on x direction if 1 + this._writer.WriteBit(leader.HookLineDirection); + //Arrowheadon B arrowhead on indicator + this._writer.WriteBit(leader.ArrowHeadEnabled); + + //R13-R14 Only: + if (this.R13_14Only) + { + //Arrowheadtype BS arrowhead type + this._writer.WriteBitShort(0); + //Dimasz BD DIMASZ at the time of creation, multiplied by DIMSCALE + this._writer.WriteBitDouble(leader.Style.ArrowSize * leader.Style.ScaleFactor); + //Unknown B + this._writer.WriteBit(false); + //Unknown B + this._writer.WriteBit(false); + //Unknown BS + this._writer.WriteBitShort(0); + //Byblockcolor BS + this._writer.WriteBitShort(0); + //Unknown B + this._writer.WriteBit(false); + //Unknown B + this._writer.WriteBit(false); + } + + //R2000+: + if (this.R2000Plus) + { + //Unknown BS + this._writer.WriteBitShort(0); + //Unknown B + this._writer.WriteBit(false); + //Unknown B + this._writer.WriteBit(false); + } + + //H 340 Associated annotation + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + //H 2 DIMSTYLE (hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, leader.Style); } private void writeLine(Line line) @@ -310,6 +1156,319 @@ private void writePoint(Point point) this._writer.WriteBitDouble(point.Rotation); } + private void writePolyfaceMesh(PolyfaceMesh fm) + { + //Numverts BS 71 Number of vertices in the mesh. + this._writer.WriteBitShort((short)fm.Vertices.Count); + //Numfaces BS 72 Number of faces + this._writer.WriteBitShort((short)fm.Faces.Count); + + //R2004 +: + if (this.R2004Plus) + { + //Owned Object Count BL Number of objects owned by this object. + this._writer.WriteBitLong(fm.Vertices.Count + fm.Faces.Count); + foreach (var v in fm.Vertices) + { + //H[VERTEX(soft pointer)] Repeats “Owned Object Count” times. + this._writer.HandleReference(DwgReferenceType.SoftPointer, v); + } + foreach (var f in fm.Faces) + { + this._writer.HandleReference(DwgReferenceType.SoftPointer, f); + } + } + + //R13 - R2000: + if (this.R13_15Only) + { + List child = new List(fm.Vertices); + child.AddRange(fm.Faces); + + CadObject first = child.FirstOrDefault(); + CadObject last = child.LastOrDefault(); + + //H first VERTEX(soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, first); + //H last VERTEX(soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, last); + } + + //Common: + //H SEQEND(hard owner) + this._writer.HandleReference(DwgReferenceType.SoftPointer, fm.Vertices.Seqend); + } + + private void writePolyline2D(Polyline2D pline) + { + //Flags BS 70 + this._writer.WriteBitShort((short)pline.Flags); + //Curve type BS 75 Curve and smooth surface type. + this._writer.WriteBitShort((short)pline.SmoothSurface); + //Start width BD 40 Default start width + this._writer.WriteBitDouble(pline.StartWidth); + //End width BD 41 Default end width + this._writer.WriteBitDouble(pline.EndWidth); + //Thickness BT 39 + this._writer.WriteBitThickness(pline.Thickness); + //Elevation BD 10 The 10-pt is (0,0,elev) + this._writer.WriteBitDouble(pline.Elevation); + //Extrusion BE 210 + this._writer.WriteBitExtrusion(pline.Normal); + + int count = pline.Vertices.Count; + //R2004+: + if (this.R2004Plus) + { + //Owned Object Count BL Number of objects owned by this object. + this._writer.WriteBitLong(count); + for (int i = 0; i < count; i++) + { + this._writer.HandleReference(DwgReferenceType.HardOwnership, pline.Vertices[i]); + } + } + + //R13-R2000: + if (this._version >= ACadVersion.AC1012 && this._version <= ACadVersion.AC1015) + { + //H first VERTEX (soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, pline.Vertices.FirstOrDefault()); + //H last VERTEX (soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, pline.Vertices.LastOrDefault()); + } + + //Common: + //H SEQEND(hard owner) + this._writer.HandleReference(DwgReferenceType.HardOwnership, pline.Vertices.Seqend); + } + + private void writePolyline3D(Polyline3D pline) + { + //Flags RC 70 NOT DIRECTLY THE 75. Bit-coded (76543210): + //75 0 : Splined(75 value is 5) + //1 : Splined(75 value is 6) + //Should assign pline.SmoothSurface ?? + this._writer.WriteByte(0); + + //Flags RC 70 NOT DIRECTLY THE 70. Bit-coded (76543210): + //0 : Closed(70 bit 0(1)) + //(Set 70 bit 3(8) because this is a 3D POLYLINE.) + this._writer.WriteByte((byte)(pline.Flags.HasFlag(PolylineFlags.ClosedPolylineOrClosedPolygonMeshInM) ? 1 : 0)); + + //R2004+: + if (this.R2004Plus) + { + //Owned Object Count BL Number of objects owned by this object. + this._writer.WriteBitLong(pline.Vertices.Count); + + foreach (var vertex in pline.Vertices) + { + this._writer.HandleReference(DwgReferenceType.HardOwnership, vertex); + } + } + + //R13-R2000: + if (this._version >= ACadVersion.AC1012 && this._version <= ACadVersion.AC1015) + { + //H first VERTEX (soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, pline.Vertices.FirstOrDefault()); + //H last VERTEX (soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, pline.Vertices.LastOrDefault()); + } + + //Common: + //H SEQEND(hard owner) + this._writer.HandleReference(DwgReferenceType.HardOwnership, pline.Vertices.Seqend); + } + + private void writeSeqend(Seqend seqend) + { + //for empty list seqend is null + if (seqend == null) + return; + + //Seqend does not have links for AC1015 or before (causes errors) + Entity prevHolder = this._prev; + Entity nextHolder = this._next; + this._prev = null; + this._next = null; + + this.writeCommonEntityData(seqend); + this.registerObject(seqend); + + this._prev = prevHolder; + this._next = nextHolder; + } + + private void writeShape(Shape shape) + { + //Ins pt 3BD 10 + this._writer.Write3BitDouble(shape.InsertionPoint); + //Scale BD 40 Scale factor, default value 1. + this._writer.WriteBitDouble(shape.Size); + //Rotation BD 50 Rotation in radians, default value 0. + this._writer.WriteBitDouble(shape.Rotation); + //Width factor BD 41 Width factor, default value 1. + this._writer.WriteBitDouble(shape.RelativeXScale); + //Oblique BD 51 Oblique angle in radians, default value 0. + this._writer.WriteBitDouble(shape.ObliqueAngle); + //Thickness BD 39 + this._writer.WriteBitDouble(shape.Thickness); + + //Shapeno BS 2 + //This is the shape index. + //In DXF the shape name is stored. + //When reading from DXF, the shape is found by iterating over all the text styles + //(SHAPEFILE, see paragraph 20.4.56) and when the text style contains a shape file, + //iterating over all the shapes until the one with the matching name is found. + this._writer.WriteBitShort(0); //TODO: missing implementation for shapeIndex + + //Extrusion 3BD 210 + this._writer.Write3BitDouble(shape.Normal); + + //H SHAPEFILE (hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + } + + private void writeSolid(Solid solid) + { + this.writeCommonEntityData(solid); + + //Thickness BT 39 + this._writer.WriteBitThickness(solid.Thickness); + + //Elevation BD ---Z for 10 - 13. + this._writer.WriteBitDouble((double)solid.FirstCorner.Z); + + //1st corner 2RD 10 + this._writer.WriteRawDouble(solid.FirstCorner.X); + this._writer.WriteRawDouble(solid.FirstCorner.Y); + //2nd corner 2RD 11 + this._writer.WriteRawDouble(solid.SecondCorner.X); + this._writer.WriteRawDouble(solid.SecondCorner.Y); + //3rd corner 2RD 12 + this._writer.WriteRawDouble(solid.ThirdCorner.X); + this._writer.WriteRawDouble(solid.ThirdCorner.Y); + //4th corner 2RD 13 + this._writer.WriteRawDouble(solid.FirstCorner.X); + this._writer.WriteRawDouble(solid.FirstCorner.Y); + + //Extrusion BE 210 + this._writer.WriteBitExtrusion(solid.Normal); + } + + private void writeSolid3D(Solid3D solid) + { + } + + private void writeSpline(Spline spline) + { + int scenario; + //R2013+: + if (this.R2013Plus) + { + //The scenario flag becomes 1 if the knot parameter is Custom or has no fit data, otherwise 2. + if (spline.KnotParameterization == KnotParameterization.Custom || spline.FitPoints.Count == 0) + { + scenario = 1; + } + else + { + scenario = 2; + } + + this._writer.WriteBitLong(scenario); + this._writer.WriteBitLong((int)spline.Flags1); + this._writer.WriteBitLong((int)spline.KnotParameterization); + } + else + { + scenario = (spline.FitPoints.Count <= 0) ? 1 : 2; + if (scenario == 2 && spline.KnotParameterization != 0) + { + scenario = 1; + } + + //Scenario BL a flag which is 2 for fitpts only, 1 for ctrlpts/knots. + this._writer.WriteBitLong(scenario); + } + + //Common: + //Degree BL degree of this spline + this._writer.WriteBitLong(spline.Degree); + + bool flag = spline.Weights.Count > 0; + switch (scenario) + { + case 1: + { + //Rational B flag bit 2 + this._writer.WriteBit(spline.Flags.HasFlag(SplineFlags.Rational)); + //Closed B flag bit 0 + this._writer.WriteBit(spline.Flags.HasFlag(SplineFlags.Closed)); + //Periodic B flag bit 1 + this._writer.WriteBit(spline.Flags.HasFlag(SplineFlags.Periodic)); + + //Knot tol BD 42 + this._writer.WriteBitDouble(spline.KnotTolerance); + //Ctrl tol BD 43 + this._writer.WriteBitDouble(spline.ControlPointTolerance); + + //Numknots BL 72 This is stored as a LONG + this._writer.WriteBitLong(spline.Knots.Count); + //Numctrlpts BL 73 Number of 10's (and 41's, if weighted) that follow. + this._writer.WriteBitLong(spline.ControlPoints.Count); + + //Weight B Seems to be an echo of the 4 bit on the flag for "weights present". + this._writer.WriteBit(flag); + + foreach (double k in spline.Knots) + { + //Knot BD knot value + this._writer.WriteBitDouble(k); + } + + for (int i = 0; i < spline.ControlPoints.Count; i++) + { + //Control pt 3BD 10 + this._writer.Write3BitDouble(spline.ControlPoints[i]); + if (flag) + { + //Weight D 41 if present as indicated by 4 bit on flag + this._writer.WriteBitDouble(spline.Weights[i]); + } + } + break; + } + case 2: + { + //Fit Tol BD 44 + this._writer.WriteBitDouble(spline.FitTolerance); + //Beg tan vec 3BD 12 Beginning tangent direction vector (normalized). + this._writer.Write3BitDouble(spline.StartTangent); + //End tan vec 3BD 13 Ending tangent direction vector (normalized). + this._writer.Write3BitDouble(spline.EndTangent); + //num fit pts BL 74 Number of fit points. + this._writer.WriteBitLong(spline.FitPoints.Count); + + foreach (XYZ fp in spline.FitPoints) + { + //Fit pt 3BD + this._writer.Write3BitDouble(fp); + } + break; + } + } + } + + private void writeRay(Ray ray) + { + //Point 3BD 10 + this._writer.Write3BitDouble(ray.StartPoint); + //Vector 3BD 11 + this._writer.Write3BitDouble(ray.Direction); + } + private void writeTextEntity(TextEntity text) { //R13-14 Only: @@ -581,5 +1740,258 @@ private void writeMText(MText mtext) } #endif } + + private void writeFaceRecord(VertexFaceRecord face) + { + this.writeCommonEntityData(face); + + //Vert index BS 71 1 - based vertex index(see DXF doc) + this._writer.WriteBitShort(face.Index1); + //Vert index BS 72 1 - based vertex index(see DXF doc) + this._writer.WriteBitShort(face.Index2); + //Vert index BS 73 1 - based vertex index(see DXF doc) + this._writer.WriteBitShort(face.Index3); + //Vert index BS 74 1 - based vertex index(see DXF doc) + this._writer.WriteBitShort(face.Index4); + } + + private void writeVertex2D(Vertex2D vertex) + { + //Flags EC 70 NOT bit-pair-coded. + this._writer.WriteByte((byte)(vertex.Flags)); + + //Point 3BD 10 NOTE THAT THE Z SEEMS TO ALWAYS BE 0.0! The Z must be taken from the 2D POLYLINE elevation. + this._writer.WriteBitDouble(vertex.Location.X); + this._writer.WriteBitDouble(vertex.Location.Y); + this._writer.WriteBitDouble(0.0); + + //Start width BD 40 If it's negative, use the abs val for start AND end widths (and note that no end width will be present). + //This is a compression trick for cases where the start and end widths are identical and non-0. + if (vertex.StartWidth != 0.0 && vertex.EndWidth == vertex.StartWidth) + { + this._writer.WriteBitDouble(0.0 - (double)vertex.StartWidth); + } + else + { + this._writer.WriteBitDouble(vertex.StartWidth); + //End width BD 41 Not present if the start width is < 0.0; see above. + this._writer.WriteBitDouble(vertex.EndWidth); + } + + //Bulge BD 42 + this._writer.WriteBitDouble(vertex.Bulge); + + //R2010+: + if (this.R2010Plus) + { + //Vertex ID BL 91 + this._writer.WriteBitLong(vertex.Id); + } + + //Common: + //Tangent dir BD 50 + this._writer.WriteBitDouble(vertex.CurveTangent); + } + + private void writeVertex(Vertex vertex) + { + this.writeCommonEntityData(vertex); + + //Flags EC 70 NOT bit-pair-coded. + this._writer.WriteByte((byte)vertex.Flags); + //Point 3BD 10 + this._writer.Write3BitDouble(vertex.Location); + } + + private void writeViewport(Viewport viewport) + { + //Center 3BD 10 + this._writer.Write3BitDouble(viewport.Center); + //Width BD 40 + this._writer.WriteBitDouble(viewport.Width); + //Height BD 41 + this._writer.WriteBitDouble(viewport.Height); + + //R2000 +: + if (this.R2000Plus) + { + //View Target 3BD 17 + this._writer.Write3BitDouble(viewport.ViewTarget); + //View Direction 3BD 16 + this._writer.Write3BitDouble(viewport.ViewDirection); + //View Twist Angle BD 51 + this._writer.WriteBitDouble(viewport.TwistAngle); + //View Height BD 45 + this._writer.WriteBitDouble(viewport.ViewHeight); + //Lens Length BD 42 + this._writer.WriteBitDouble(viewport.LensLength); + //Front Clip Z BD 43 + this._writer.WriteBitDouble(viewport.FrontClipPlane); + //Back Clip Z BD 44 + this._writer.WriteBitDouble(viewport.BackClipPlane); + //Snap Angle BD 50 + this._writer.WriteBitDouble(viewport.SnapAngle); + //View Center 2RD 12 + this._writer.Write2RawDouble(viewport.ViewCenter); + //Snap Base 2RD 13 + this._writer.Write2RawDouble(viewport.SnapBase); + //Snap Spacing 2RD 14 + this._writer.Write2RawDouble(viewport.SnapSpacing); + //Grid Spacing 2RD 15 + this._writer.Write2RawDouble(viewport.GridSpacing); + //Circle Zoom BS 72 + this._writer.WriteBitShort(viewport.CircleZoomPercent); + } + + //R2007 +: + if (this.R2007Plus) + { + //Grid Major BS 61 + this._writer.WriteBitShort(viewport.MajorGridLineFrequency); + } + + //R2000 +: + if (this.R2000Plus) + { + //Frozen Layer Count BL + this._writer.WriteBitLong(viewport.FrozenLayers.Count); + //Status Flags BL 90 + this._writer.WriteBitLong((int)viewport.Status); + //Style Sheet TV 1 + this._writer.WriteVariableText(string.Empty); //This is never used + //Render Mode RC 281 + this._writer.WriteByte((byte)viewport.RenderMode); + //UCS at origin B 74 + this._writer.WriteBit(viewport.DisplayUcsIcon); + //UCS per Viewport B 71 + this._writer.WriteBit(viewport.UcsPerViewport); + //UCS Origin 3BD 110 + this._writer.Write3BitDouble(viewport.UcsOrigin); + //UCS X Axis 3BD 111 + this._writer.Write3BitDouble(viewport.UcsXAxis); + //UCS Y Axis 3BD 112 + this._writer.Write3BitDouble(viewport.UcsYAxis); + //UCS Elevation BD 146 + this._writer.WriteBitDouble(viewport.Elevation); + //UCS Ortho View Type BS 79 + this._writer.WriteBitShort((short)viewport.UcsOrthographicType); + } + + //R2004 +: + if (this.R2004Plus) + { + //ShadePlot Mode BS 170 + this._writer.WriteBitShort((short)viewport.ShadePlotMode); + } + + //R2007 +: + if (this.R2007Plus) + { + //Use def. lights B 292 + this._writer.WriteBit(viewport.UseDefaultLighting); + //Def.lighting type RC 282 + this._writer.WriteByte((byte)viewport.DefaultLightingType); + //Brightness BD 141 + this._writer.WriteBitDouble(viewport.Brightness); + //Contrast BD 142 + this._writer.WriteBitDouble(viewport.Constrast); + //Ambient light color CMC 63 + this._writer.WriteCmColor(viewport.AmbientLightColor); + } + + //R13 - R14 Only: + if (this.R13_14Only) + { + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + } + + //R2000 +: + if (this.R2000Plus) + { + foreach (var layer in viewport.FrozenLayers) + { + if (this.R2004Plus) + { + //H 341 Frozen Layer Handles(use count from above) + //(hard pointer until R2000, soft pointer from R2004 onwards) + this._writer.HandleReference(DwgReferenceType.SoftPointer, layer); + } + else + { + this._writer.HandleReference(DwgReferenceType.HardPointer, layer); + } + } + + //H 340 Clip boundary handle(soft pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, viewport.Boundary); + } + + //R2000: + if (this._version == ACadVersion.AC1015) + { + //H VIEWPORT ENT HEADER((hard pointer)) + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + } + + //R2000 +: + if (this.R2000Plus) + { + //TODO: Implement viewport UCS + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + } + + //R2007 +: + if (this.R2007Plus) + { + //H 332 Background(soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, null); + //H 348 Visual Style(hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + //H 333 Shadeplot ID(soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, null); + //H 361 Sun(hard owner) + this._writer.HandleReference(DwgReferenceType.HardOwnership, null); + } + } + + private void writeXLine(XLine xline) + { + //3 RD: a point on the construction line + this._writer.Write3BitDouble(xline.FirstPoint); + //3 RD : another point + this._writer.Write3BitDouble(xline.Direction); + } + + private void writeChildEntities(IEnumerable entities, Seqend seqend) + { + if (!entities.Any()) + return; + + Entity prevHolder = this._prev; + Entity nextHolder = this._next; + this._prev = null; + this._next = null; + + Entity curr = entities.First(); + for (int i = 1; i < entities.Count(); i++) + { + this._next = entities.ElementAt(i); + this.writeEntity(curr); + this._prev = curr; + curr = this._next; + } + + this._next = null; + this.writeEntity(curr); + + this._prev = prevHolder; + this._next = nextHolder; + + if (seqend != null) + { + this.writeSeqend(seqend); + } + } } } diff --git a/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Objects.cs b/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Objects.cs new file mode 100644 index 000000000..07b71eae5 --- /dev/null +++ b/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.Objects.cs @@ -0,0 +1,474 @@ +using ACadSharp.Objects; +using CSUtilities.Converters; +using CSUtilities.IO; +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; + +namespace ACadSharp.IO.DWG +{ + internal partial class DwgObjectWriter : DwgSectionIO + { + private void writeObjects() + { + while (this._objects.Any()) + { + CadObject obj = this._objects.Dequeue(); + + this.writeObject(obj); + } + } + + private void writeObject(CadObject obj) + { + switch (obj) + { + case SortEntitiesTable: + case XRecord: + this.notify($"Object type not implemented {obj.GetType().FullName}", NotificationType.NotImplemented); + return; + } + + this.writeCommonNonEntityData(obj); + + switch (obj) + { + case AcdbPlaceHolder acdbPlaceHolder: + this.writeAcdbPlaceHolder(acdbPlaceHolder); + break; + case CadDictionaryWithDefault dictionarydef: + this.writeCadDictionaryWithDefault(dictionarydef); + break; + case CadDictionary dictionary: + this.writeDictionary(dictionary); + break; + case DictionaryVariable dictionaryVariable: + this.writeDictionaryVariable(dictionaryVariable); + break; + case Group group: + this.writeGroup(group); + break; + case Layout layout: + this.writeLayout(layout); + break; + case MLStyle style: + this.writeMLStyle(style); + break; + case PlotSettings plotsettings: + this.writePlotSettings(plotsettings); + break; + case Scale scale: + this.writeScale(scale); + break; + case XRecord record: + this.writeXRecord(record); + break; + default: + throw new NotImplementedException($"Object not implemented : {obj.GetType().FullName}"); + } + + this.registerObject(obj); + } + + private void writeAcdbPlaceHolder(AcdbPlaceHolder acdbPlaceHolder) + { + } + + private void writeCadDictionaryWithDefault(CadDictionaryWithDefault dictionary) + { + this.writeDictionary(dictionary); + + //H 7 Default entry (hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, dictionary.DefaultEntry); + } + + private void writeDictionary(CadDictionary dictionary) + { + //Common: + //Numitems L number of dictonary items + this._writer.WriteBitLong(dictionary.Count()); + + //R14 Only: + if (this._version == ACadVersion.AC1014) + { + //Unknown R14 RC Unknown R14 byte, has always been 0 + this._writer.WriteByte(0); + } + + //R2000 +: + if (this.R2000Plus) + { + //Cloning flag BS 281 + this._writer.WriteBitShort((short)dictionary.ClonningFlags); + this._writer.WriteByte((byte)(dictionary.HardOwnerFlag ? 1u : 0u)); + } + + //Common: + foreach (var name in dictionary.EntryNames) + { + this._writer.WriteVariableText(name); + } + + foreach (var handle in dictionary.EntryHandles) + { + this._writer.HandleReference(DwgReferenceType.SoftOwnership, handle); + } + + this.addEntriesToWriter(dictionary); + } + + private void addEntriesToWriter(CadDictionary dictionary) + { + foreach (CadObject e in dictionary) + { + this._objects.Enqueue(e); + } + } + + private void writeDictionaryVariable(DictionaryVariable dictionaryVariable) + { + //Intval RC an integer value + this._writer.WriteByte(0); + + //BS a string + this._writer.WriteVariableText(dictionaryVariable.Value); + } + + private void writeGroup(Group group) + { + //Str TV name of group + this._writer.WriteVariableText(group.Description); + + //Unnamed BS 1 if group has no name + this._writer.WriteBitShort((short)(group.IsUnnamed ? 1 : 0)); + //Selectable BS 1 if group selectable + this._writer.WriteBitShort((short)(group.Selectable ? 1 : 0)); + + //Numhandles BL # objhandles in this group + this._writer.WriteBitLong(group.Entities.Count); + foreach (ulong h in group.Entities.Keys) + { + //the entries in the group(hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, h); + } + } + + private void writeLayout(Layout layout) + { + this.writePlotSettings(layout); + + //Common: + //Layout name TV 1 layout name + this._writer.WriteVariableText(layout.Name); + //Tab order BL 71 layout tab order + this._writer.WriteBitLong(layout.TabOrder); + //Flag BS 70 layout flags + this._writer.WriteBitShort((short)layout.LayoutFlags); + //Ucs origin 3BD 13 layout ucs origin + this._writer.Write3BitDouble(layout.Origin); + //Limmin 2RD 10 layout minimum limits + this._writer.Write2RawDouble(layout.MinLimits); + //Limmax 2RD 11 layout maximum limits + this._writer.Write2RawDouble(layout.MinLimits); + //Inspoint 3BD 12 layout insertion base point + this._writer.Write3BitDouble(layout.InsertionBasePoint); + this._writer.Write3BitDouble(layout.XAxis); + this._writer.Write3BitDouble(layout.YAxis); + this._writer.WriteBitDouble(layout.Elevation); + this._writer.WriteBitShort((short)layout.UcsOrthographicType); + this._writer.Write3BitDouble(layout.MinExtents); + this._writer.Write3BitDouble(layout.MaxExtents); + + //R2004 +: + if (this.R2004Plus) + { + //Viewport count RL # of viewports in this layout + this._writer.WriteBitLong(layout.Viewports.Count()); + } + + //Common: + //330 associated paperspace block record handle(soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, layout.AssociatedBlock); + //331 last active viewport handle(soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, layout.Viewport); + + //If not present and 76 code is non-zero, then base UCS is taken to be WORLD + if (layout.UcsOrthographicType == OrthographicType.None) + { + //346 base ucs handle(hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + //345 named ucs handle(hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, layout.UCS); + } + else + { + //346 base ucs handle(hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, layout.BaseUCS); + //345 named ucs handle(hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + } + + //R2004+: + if (this.R2004Plus) + { + foreach (Entities.Viewport viewport in layout.Viewports) + { + //Viewport handle(repeats Viewport count times) (soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, viewport); + } + } + } + + private void writeMLStyle(MLStyle mlineStyle) + { + //Common: + //Name TV Name of this style + this._writer.WriteVariableText(mlineStyle.Name); + //Desc TV Description of this style + this._writer.WriteVariableText(mlineStyle.Description); + + short flags = 0; + if (mlineStyle.Flags.HasFlag(MLineStyleFlags.DisplayJoints)) + { + flags = (short)(flags | 1U); + } + if (mlineStyle.Flags.HasFlag(MLineStyleFlags.FillOn)) + { + flags = (short)(flags | 2U); + } + if (mlineStyle.Flags.HasFlag(MLineStyleFlags.StartSquareCap)) + { + flags = (short)(flags | 16U); + } + if (mlineStyle.Flags.HasFlag(MLineStyleFlags.StartRoundCap)) + { + flags = (short)(flags | 0x20); + } + if (mlineStyle.Flags.HasFlag(MLineStyleFlags.StartInnerArcsCap)) + { + flags = (short)(flags | 0x40); + } + if (mlineStyle.Flags.HasFlag(MLineStyleFlags.EndSquareCap)) + { + flags = (short)(flags | 0x100); + } + if (mlineStyle.Flags.HasFlag(MLineStyleFlags.EndRoundCap)) + { + flags = (short)(flags | 0x200); + } + if (mlineStyle.Flags.HasFlag(MLineStyleFlags.EndInnerArcsCap)) + { + flags = (short)(flags | 0x400); + } + + //Flags BS A short which reconstitutes the mlinestyle flags as defined in DXF. + this._writer.WriteBitShort(flags); + + //fillcolor CMC Fill color for this style + this._writer.WriteCmColor(mlineStyle.FillColor); + //startang BD Start angle + this._writer.WriteBitDouble(mlineStyle.StartAngle); + //endang BD End angle + this._writer.WriteBitDouble(mlineStyle.EndAngle); + + //linesinstyle RC Number of lines in this style + this._writer.WriteByte((byte)mlineStyle.Elements.Count); + foreach (MLStyle.Element element in mlineStyle.Elements) + { + //Offset BD Offset of this segment + this._writer.WriteBitDouble(element.Offset); + //Color CMC Color of this segment + this._writer.WriteCmColor(element.Color); + //R2018+: + if (this.R2018Plus) + { + //Line type handle H Line type handle (hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, element.LineType); + } + //Before R2018: + else + { + //TODO: Fix the Linetype index for dwgReader and DwgWriter + //Ltindex BS Linetype index (yes, index) + this._writer.WriteBitShort(0); + } + } + } + + private void writePlotSettings(PlotSettings plot) + { + //Common: + //Page setup name TV 1 plotsettings page setup name + this._writer.WriteVariableText(plot.PageName); + //Printer / Config TV 2 plotsettings printer or configuration file + this._writer.WriteVariableText(plot.SystemPrinterName); + //Plot layout flags BS 70 plotsettings plot layout flag + this._writer.WriteBitShort((short)plot.Flags); + + //Left Margin BD 40 plotsettings left margin in millimeters + this._writer.WriteBitDouble(plot.UnprintableMargin.Left); + //Bottom Margin BD 41 plotsettings bottom margin in millimeters + this._writer.WriteBitDouble(plot.UnprintableMargin.Bottom); + //Right Margin BD 42 plotsettings right margin in millimeters + this._writer.WriteBitDouble(plot.UnprintableMargin.Right); + //Top Margin BD 43 plotsettings top margin in millimeters + this._writer.WriteBitDouble(plot.UnprintableMargin.Top); + + //Paper Width BD 44 plotsettings paper width in millimeters + this._writer.WriteBitDouble(plot.PaperWidth); + //Paper Height BD 45 plotsettings paper height in millimeters + this._writer.WriteBitDouble(plot.PaperHeight); + + //Paper Size TV 4 plotsettings paper size + this._writer.WriteVariableText(plot.PaperSize); + + //Plot origin 2BD 46,47 plotsettings origin offset in millimeters + this._writer.WriteBitDouble(plot.PlotOriginX); + this._writer.WriteBitDouble(plot.PlotOriginY); + + //Paper units BS 72 plotsettings plot paper units + this._writer.WriteBitShort((short)plot.PaperUnits); + //Plot rotation BS 73 plotsettings plot rotation + this._writer.WriteBitShort((short)plot.PaperRotation); + //Plot type BS 74 plotsettings plot type + this._writer.WriteBitShort((short)plot.PlotType); + + //Window min 2BD 48,49 plotsettings plot window area lower left + this._writer.WriteBitDouble(plot.WindowLowerLeftX); + this._writer.WriteBitDouble(plot.WindowLowerLeftY); + //Window max 2BD 140,141 plotsettings plot window area upper right + this._writer.WriteBitDouble(plot.WindowUpperLeftX); + this._writer.WriteBitDouble(plot.WindowUpperLeftY); + + //R13 - R2000 Only: + if (this._version >= ACadVersion.AC1012 && this._version <= ACadVersion.AC1015) + { + //Plot view name T 6 plotsettings plot view name + this._writer.WriteVariableText(plot.PlotViewName); + } + + //Common: + //Real world units BD 142 plotsettings numerator of custom print scale + this._writer.WriteBitDouble(plot.NumeratorScale); + //Drawing units BD 143 plotsettings denominator of custom print scale + this._writer.WriteBitDouble(plot.DenominatorScale); + //Current style sheet TV 7 plotsettings current style sheet + this._writer.WriteVariableText(plot.StyleSheet); + //Scale type BS 75 plotsettings standard scale type + this._writer.WriteBitShort((short)plot.ScaledFit); + //Scale factor BD 147 plotsettings scale factor + this._writer.WriteBitDouble(plot.StandardScale); + //Paper image origin 2BD 148,149 plotsettings paper image origin + this._writer.Write2BitDouble(plot.PaperImageOrigin); + + //R2004+: + if (this.R2004Plus) + { + //Shade plot mode BS 76 + this._writer.WriteBitShort((short)plot.ShadePlotMode); + //Shade plot res.Level BS 77 + this._writer.WriteBitShort((short)plot.ShadePlotResolutionMode); + //Shade plot custom DPI BS 78 + this._writer.WriteBitShort(plot.ShadePlotDPI); + + //6 plot view handle(hard pointer) + this._writer.HandleReference(DwgReferenceType.HardPointer, null); + } + + //R2007 +: + if (this.R2007Plus) + { + //Visual Style handle(soft pointer) + this._writer.HandleReference(DwgReferenceType.SoftPointer, null); + } + } + + private void writeScale(Scale scale) + { + //BS 70 Unknown(ODA writes 0). + this._writer.WriteBitShort(scale.Unknown); + //TV 300 Name + this._writer.WriteVariableText(scale.Name); + //BD 140 Paper units(numerator) + this._writer.WriteBitDouble(scale.PaperUnits); + //BD 141 Drawing units(denominator, divided by 10). + this._writer.WriteBitDouble(scale.DrawingUnits); + //B 290 Has unit scale + this._writer.WriteBit(scale.IsUnitScale); + } + + private void writeXRecord(XRecord xrecord) + { + MemoryStream stream = new MemoryStream(); + StreamIO ms = new StreamIO(stream); + ms.EndianConverter = new LittleEndianConverter(); + + foreach (XRecord.Entry entry in xrecord.Entries) + { + if (entry.Value == null) + { + continue; + } + + ms.Write((short)entry.Code); + GroupCodeValueType groupValueType = GroupCodeValue.TransformValue(entry.Code); + + switch (groupValueType) + { + case GroupCodeValueType.None: + break; + case GroupCodeValueType.String: + break; + case GroupCodeValueType.Point3D: + break; + case GroupCodeValueType.Double: + break; + case GroupCodeValueType.Int16: + break; + case GroupCodeValueType.Int32: + break; + case GroupCodeValueType.Int64: + break; + case GroupCodeValueType.Handle: + break; + case GroupCodeValueType.ObjectId: + break; + case GroupCodeValueType.Bool: + break; + case GroupCodeValueType.Chunk: + break; + case GroupCodeValueType.Comment: + break; + case GroupCodeValueType.ExtendedDataString: + break; + case GroupCodeValueType.ExtendedDataChunk: + break; + case GroupCodeValueType.ExtendedDataHandle: + break; + case GroupCodeValueType.ExtendedDataDouble: + break; + case GroupCodeValueType.ExtendedDataInt16: + break; + case GroupCodeValueType.ExtendedDataInt32: + break; + default: + break; + } + } + + //Common: + //Numdatabytes BL number of databytes + this._writer.WriteBitLong((int)ms.Length); + this._writer.WriteBytes(stream.GetBuffer()); + + //R2000+: + if (this.R2000Plus) + { + //Cloning flag BS 280 + this._writer.WriteBitShort((short)xrecord.ClonningFlags); + } + + } + } +} diff --git a/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.cs b/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.cs index d94789183..88c73face 100644 --- a/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.cs +++ b/ACadSharp/IO/DWG/DwgStreamWriters/DwgObjectWriter.cs @@ -1,5 +1,6 @@ using ACadSharp.Blocks; using ACadSharp.Entities; +using ACadSharp.Objects; using ACadSharp.Tables; using ACadSharp.Tables.Collections; using CSUtilities.Text; @@ -12,11 +13,17 @@ namespace ACadSharp.IO.DWG { internal partial class DwgObjectWriter : DwgSectionIO { + public override string SectionName => DwgSectionDefinition.AcDbObjects; + /// /// Key : handle | Value : Offset /// public Dictionary Map { get; } = new Dictionary(); + private Dictionary _dictionaries = new(); + + private Queue _objects = new(); + private MemoryStream _msmain; private IDwgStreamWriter _writer; @@ -47,18 +54,48 @@ public void Write() this._writer.WriteRawLong(0xDCA); } - this.writeTable(this._document.AppIds); + this.writeBlockControl(); this.writeTable(this._document.Layers); - this.writeTable(this._document.LineTypes); this.writeTable(this._document.TextStyles); - this.writeTable(this._document.UCSs); + this.writeLTypeControlObject(); this.writeTable(this._document.Views); + this.writeTable(this._document.UCSs); this.writeTable(this._document.VPorts); - this.writeBlockControl(); + this.writeTable(this._document.AppIds); //For some reason the dimension must be writen the last this.writeTable(this._document.DimensionStyles); - this.writeBlocks(); + this.writeBlockEntities(); + this.writeObjects(); + } + + private void writeLTypeControlObject() + { + this.writeCommonNonEntityData(this._document.LineTypes); + + //Common: + //Numentries BL 70 + this._writer.WriteBitLong(this._document.LineTypes.Count - 2); + + foreach (LineType item in this._document.LineTypes) + { + if (item.Name.Equals(LineType.ByBlockName, StringComparison.OrdinalIgnoreCase) + || item.Name.Equals(LineType.ByLayerName, StringComparison.OrdinalIgnoreCase)) + { + continue; + } + + //numentries handles in the file (soft owner) + this._writer.HandleReference(DwgReferenceType.SoftOwnership, item); + } + + //the linetypes, ending with BYLAYER and BYBLOCK. + this._writer.HandleReference(DwgReferenceType.HardOwnership, this._document.LineTypes.ByBlock); + this._writer.HandleReference(DwgReferenceType.HardOwnership, this._document.LineTypes.ByLayer); + + this.registerObject(this._document.LineTypes); + + this.writeEntries(this._document.LineTypes); } private void writeBlockControl() @@ -66,7 +103,7 @@ private void writeBlockControl() this.writeCommonNonEntityData(this._document.BlockRecords); //Common: - //Numentries BL 70 + //Numentries BL 70 Doesn't count *MODEL_SPACE and *PAPER_SPACE. this._writer.WriteBitLong(this._document.BlockRecords.Count - 2); foreach (var item in this._document.BlockRecords) @@ -74,9 +111,11 @@ private void writeBlockControl() if (item.Name.Equals(BlockRecord.ModelSpaceName, StringComparison.OrdinalIgnoreCase) || item.Name.Equals(BlockRecord.PaperSpaceName, StringComparison.OrdinalIgnoreCase)) { - //Handle refs H NULL(soft pointer) - this._writer.HandleReference(DwgReferenceType.SoftOwnership, item); + continue; } + + //numentries handles of blockheaders in the file (soft owner) + this._writer.HandleReference(DwgReferenceType.SoftOwnership, item); } //*MODEL_SPACE and *PAPER_SPACE(hard owner). @@ -88,7 +127,7 @@ private void writeBlockControl() this.writeEntries(this._document.BlockRecords); } - private void writeTable(Table table, bool register = true, bool writeEntries = true) + private void writeTable(Table table) where T : TableEntry { this.writeCommonNonEntityData(table); @@ -99,15 +138,13 @@ private void writeTable(Table table, bool register = true, bool writeEntri foreach (var item in table) { - //Handle refs H NULL(soft pointer) + //numentries handles in the file (soft owner) this._writer.HandleReference(DwgReferenceType.SoftOwnership, item); } - if (register) - this.registerObject(table); + this.registerObject(table); - if (writeEntries) - this.writeEntries(table); + this.writeEntries(table); } private void writeEntries(Table table) @@ -151,7 +188,7 @@ private void writeEntries(Table table) } } - private void writeBlocks() + private void writeBlockEntities() { foreach (BlockRecord blkRecord in this._document.BlockRecords) { @@ -405,8 +442,9 @@ private void writeLayer(Layer layer) //Color CMC 62 this._writer.WriteCmColor(layer.Color); + this._writer.HandleReference(DwgReferenceType.SoftPointer, null); + //Handle refs H Layer control (soft pointer) - this._writer.HandleReference(DwgReferenceType.SoftPointer, this._document.Layers); //[Reactors(soft pointer)] //xdicobjhandle(hard owner) //External reference block handle(hard pointer) diff --git a/ACadSharp/IO/DWG/DwgStreamWriters/DwgStreamWriterBase.cs b/ACadSharp/IO/DWG/DwgStreamWriters/DwgStreamWriterBase.cs index 137457e0f..7a1871803 100644 --- a/ACadSharp/IO/DWG/DwgStreamWriters/DwgStreamWriterBase.cs +++ b/ACadSharp/IO/DWG/DwgStreamWriters/DwgStreamWriterBase.cs @@ -366,6 +366,12 @@ public virtual void WriteCmColor(Color value) this.WriteBitShort(value.Index); } + public void Write2BitDouble(XY value) + { + this.WriteBitDouble(value.X); + this.WriteBitDouble(value.Y); + } + public void Write3BitDouble(XYZ value) { this.WriteBitDouble(value.X); @@ -517,6 +523,19 @@ public virtual void WriteBitExtrusion(XYZ normal) this.Write3BitDouble(normal); } + public void Write2BitDoubleWithDefault(XY def, XY value) + { + this.WriteBitDoubleWithDefault(def.X, value.X); + this.WriteBitDoubleWithDefault(def.Y, value.Y); + } + + public void Write3BitDoubleWithDefault(XYZ def, XYZ value) + { + this.WriteBitDoubleWithDefault(def.X, value.X); + this.WriteBitDoubleWithDefault(def.Y, value.Y); + this.WriteBitDoubleWithDefault(def.Z, value.Z); + } + public void WriteBitDoubleWithDefault(double def, double value) { if (def == value) diff --git a/ACadSharp/IO/DWG/DwgStreamWriters/DwgmMergedStreamWriter.cs b/ACadSharp/IO/DWG/DwgStreamWriters/DwgmMergedStreamWriter.cs index b3f650574..4e5672c7b 100644 --- a/ACadSharp/IO/DWG/DwgStreamWriters/DwgmMergedStreamWriter.cs +++ b/ACadSharp/IO/DWG/DwgStreamWriters/DwgmMergedStreamWriter.cs @@ -68,6 +68,11 @@ public void Write2RawDouble(XY value) this.Main.Write2RawDouble(value); } + public void Write2BitDouble(XY value) + { + this.Main.Write2BitDouble(value); + } + public void Write3BitDouble(XYZ value) { this.Main.Write3BitDouble(value); @@ -87,6 +92,15 @@ public void WriteBitDouble(double value) { this.Main.WriteBitDouble(value); } + public void Write2BitDoubleWithDefault(XY def, XY value) + { + this.Main.Write2BitDoubleWithDefault(def, value); + } + + public void Write3BitDoubleWithDefault(XYZ def, XYZ value) + { + this.Main.Write3BitDoubleWithDefault(def, value); + } public void WriteBitDoubleWithDefault(double def, double value) { diff --git a/ACadSharp/IO/DWG/DwgStreamWriters/IDwgStreamWriter.cs b/ACadSharp/IO/DWG/DwgStreamWriters/IDwgStreamWriter.cs index 9930510fb..4b59b815a 100644 --- a/ACadSharp/IO/DWG/DwgStreamWriters/IDwgStreamWriter.cs +++ b/ACadSharp/IO/DWG/DwgStreamWriters/IDwgStreamWriter.cs @@ -50,6 +50,8 @@ internal interface IDwgStreamWriter void WriteCmColor(Color value); + void Write2BitDouble(XY value); + void Write3BitDouble(XYZ value); void Write2RawDouble(XY value); @@ -78,6 +80,10 @@ internal interface IDwgStreamWriter void WriteBitDoubleWithDefault(double def, double value); + void Write2BitDoubleWithDefault(XY def, XY value); + + void Write3BitDoubleWithDefault(XYZ def, XYZ value); + void ResetStream(); void SavePositonForSize(); diff --git a/ACadSharp/IO/DWG/DwgWriter.cs b/ACadSharp/IO/DWG/DwgWriter.cs index 9df957567..5fd2655cb 100644 --- a/ACadSharp/IO/DWG/DwgWriter.cs +++ b/ACadSharp/IO/DWG/DwgWriter.cs @@ -286,7 +286,9 @@ private void writeObjects() { MemoryStream stream = new MemoryStream(); DwgObjectWriter writer = new DwgObjectWriter(stream, this._document); + writer.OnNotification += this.triggerNotification; writer.Write(); + this._handlesMap = writer.Map; this._fileHeaderWriter.AddSection(DwgSectionDefinition.AcDbObjects, stream, true); diff --git a/ACadSharp/IO/DXF/DxfStreamReader/DxfObjectsSectionReader.cs b/ACadSharp/IO/DXF/DxfStreamReader/DxfObjectsSectionReader.cs index dfc053eb0..8816fa317 100644 --- a/ACadSharp/IO/DXF/DxfStreamReader/DxfObjectsSectionReader.cs +++ b/ACadSharp/IO/DXF/DxfStreamReader/DxfObjectsSectionReader.cs @@ -66,7 +66,7 @@ private CadTemplate readObject() case DxfFileToken.ObjectVisualStyle: return this.readObjectCodes(new CadTemplate(new VisualStyle()), this.readVisualStyle); case DxfFileToken.ObjectXRecord: - return this.readObjectCodes(new CadXRecordTemplate(), readXRecord); + return this.readObjectCodes(new CadXRecordTemplate(), readXRecord); default: this._builder.Notify($"Object not implemented: {this._reader.ValueAsString}", NotificationType.NotImplemented); do @@ -170,13 +170,13 @@ private bool readXRecord(CadTemplate template, DxfMap map) } } - private void readXRecordEntries(XRecrod recrod) + private void readXRecordEntries(XRecord recrod) { this._reader.ReadNext(); while (this._reader.DxfCode != DxfCode.Start) { - recrod.Entries.Add(new XRecrod.Entry(this._reader.Code, this._reader.Value)); + recrod.Entries.Add(new XRecord.Entry(this._reader.Code, this._reader.Value)); this._reader.ReadNext(); } diff --git a/ACadSharp/IO/DXF/DxfStreamWriter/DxfObjectsSectionWriter.cs b/ACadSharp/IO/DXF/DxfStreamWriter/DxfObjectsSectionWriter.cs index 141fb36dd..7f38971e3 100644 --- a/ACadSharp/IO/DXF/DxfStreamWriter/DxfObjectsSectionWriter.cs +++ b/ACadSharp/IO/DXF/DxfStreamWriter/DxfObjectsSectionWriter.cs @@ -65,7 +65,7 @@ protected void writeObject(T co) case SortEntitiesTable sortensTable: //this.writeSortentsTable(sortensTable); break; - case XRecrod record: + case XRecord record: this.writeXRecord(record); break; default: @@ -237,7 +237,7 @@ private void writeSortentsTable(SortEntitiesTable e) this._writer.Write(330, e.BlockOwner.Handle); } - protected void writeXRecord(XRecrod e) + protected void writeXRecord(XRecord e) { this._writer.Write(DxfCode.Subclass, DxfSubclassMarker.XRecord); diff --git a/ACadSharp/IO/Templates/CadLayoutTemplate.cs b/ACadSharp/IO/Templates/CadLayoutTemplate.cs index 579caac8d..75ed65bf9 100644 --- a/ACadSharp/IO/Templates/CadLayoutTemplate.cs +++ b/ACadSharp/IO/Templates/CadLayoutTemplate.cs @@ -39,7 +39,7 @@ public override void Build(CadDocumentBuilder builder) if (builder.TryGetCadObject(this.BaseUcsHandle, out UCS ucs)) { - this.CadObject.UCS = ucs; + this.CadObject.BaseUCS = ucs; } if (builder.TryGetCadObject(this.NamesUcsHandle, out UCS nameducs)) diff --git a/ACadSharp/IO/Templates/CadXRecordTemplate.cs b/ACadSharp/IO/Templates/CadXRecordTemplate.cs index 181298313..d913b2b87 100644 --- a/ACadSharp/IO/Templates/CadXRecordTemplate.cs +++ b/ACadSharp/IO/Templates/CadXRecordTemplate.cs @@ -2,10 +2,10 @@ namespace ACadSharp.IO.Templates { - internal class CadXRecordTemplate : CadTemplate + internal class CadXRecordTemplate : CadTemplate { - public CadXRecordTemplate() : base(new XRecrod()) { } + public CadXRecordTemplate() : base(new XRecord()) { } - public CadXRecordTemplate(XRecrod cadObject) : base(cadObject) { } + public CadXRecordTemplate(XRecord cadObject) : base(cadObject) { } } } diff --git a/ACadSharp/Objects/Layout.cs b/ACadSharp/Objects/Layout.cs index 25e0e72c8..672755193 100644 --- a/ACadSharp/Objects/Layout.cs +++ b/ACadSharp/Objects/Layout.cs @@ -167,13 +167,23 @@ internal set } /// - /// Layout's UCS + /// UCS Table Record if UCS is a named UCS /// + /// + /// If not present, then UCS is unnamed + /// [DxfCodeValue(DxfReferenceType.Handle, 345)] public UCS UCS { get; set; } - //346 ID/handle of AcDbUCSTableRecord of base UCS if UCS is orthographic(76 code is non-zero). - //If not present and 76 code is non-zero, then base UCS is taken to be WORLD + /// + /// UCSTableRecord of base UCS if UCS is orthographic ( is non-zero) + /// + /// + /// If not present and is non-zero, then base UCS is taken to be WORLD + /// + [DxfCodeValue(DxfReferenceType.Handle, 346)] + public UCS BaseUCS { get; set; } + //333 Shade plot ID diff --git a/ACadSharp/Objects/XRecrod.cs b/ACadSharp/Objects/XRecrod.cs index d4ec07643..5bf39cd74 100644 --- a/ACadSharp/Objects/XRecrod.cs +++ b/ACadSharp/Objects/XRecrod.cs @@ -4,7 +4,7 @@ namespace ACadSharp.Objects { /// - /// Represents a object + /// Represents a object /// /// /// Object name
@@ -12,7 +12,7 @@ namespace ACadSharp.Objects ///
[DxfName(DxfFileToken.ObjectXRecord)] [DxfSubClass(DxfSubclassMarker.XRecord)] - public class XRecrod : CadObject + public class XRecord : CadObject { /// public override ObjectType ObjectType => ObjectType.XRECORD; diff --git a/ACadSharp/Tables/Collections/LineTypesTable.cs b/ACadSharp/Tables/Collections/LineTypesTable.cs index 0a5ece572..afb50d41e 100644 --- a/ACadSharp/Tables/Collections/LineTypesTable.cs +++ b/ACadSharp/Tables/Collections/LineTypesTable.cs @@ -10,6 +10,21 @@ public class LineTypesTable : Table /// public override string ObjectName => DxfFileToken.TableLinetype; + /// + /// Get the ByLayer entry in the table + /// + public LineType ByLayer { get { return this[LineType.ByLayerName]; } } + + /// + /// Get the ByBlock entry in the table + /// + public LineType ByBlock { get { return this[LineType.ByBlockName]; } } + + /// + /// Get the Continuous entry in the table + /// + public LineType Continuous { get { return this[LineType.ContinuousName]; } } + protected override string[] _defaultEntries { get diff --git a/ACadSharpInternal.Tests/DwgObjectWriterTests.cs b/ACadSharpInternal.Tests/DwgObjectWriterTests.cs index f48af8a03..c2ca28314 100644 --- a/ACadSharpInternal.Tests/DwgObjectWriterTests.cs +++ b/ACadSharpInternal.Tests/DwgObjectWriterTests.cs @@ -52,6 +52,7 @@ public void EntitiesTest(ACadVersion version) document.Entities.Add(EntityFactory.Create()); document.Entities.Add(EntityFactory.Create()); document.Entities.Add(EntityFactory.Create()); + document.Entities.Add(EntityFactory.Create(typeof(Insert))); document.Entities.Add(EntityFactory.Create()); document.Entities.Add(EntityFactory.Create()); document.Entities.Add(EntityFactory.Create());