Skip to content

Commit e59f15a

Browse files
committed
Fixing path type 4 handling
1 parent f87c5c5 commit e59f15a

File tree

2 files changed

+53
-9
lines changed

2 files changed

+53
-9
lines changed

GDStoSVG/GDSItems.cs

Lines changed: 48 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,7 @@ public void OptimizeGeometry()
4646
}
4747
}
4848

49+
// This assumes all underlying geometry has been pre-optimized with OptimizeGeometry() first. Meaning there are no Box, Path, or Boundary objects remaining.
4950
public void FlattenAndOptimize(Transform? trans = null, List<Element>? targetList = null)
5051
{
5152
if (Elements == null || this.IsFlattened) { return; }
@@ -155,7 +156,7 @@ public class Boundary : LayerElement
155156
}
156157

157158
public class Path : LayerElement
158-
{
159+
{ // TODO: Path type 0 (butt) paths of 0 length are not correctly handled if optimizing
159160
public short? Datatype = null;
160161
public short PathType = 0;
161162
public int Width = 0; // negative means not affected by magnification
@@ -165,18 +166,59 @@ public class Path : LayerElement
165166
public override List<PointD>? GetPolygonCoords()
166167
{
167168
if(this.Coords == null) { return null; }
168-
List<List<PointD>> Input = new() { new(this.Coords) };
169169
EndType Ends = EndType.Butt;
170170
if (this.PathType == 1) { Ends = EndType.Round; }
171171
if (this.PathType == 2) { Ends = EndType.Square; }
172-
if (this.PathType == 4)
173-
{
174-
// TODO: A whole bunch of stuff here
175-
}
172+
if (this.PathType == 4) { ConvertType4(); } // Start and end extensions separately defined, apply them manually, then treat as butt
173+
List<List<PointD>> Input = new() { new(this.Coords) };
176174
List<List<PointD>> Output = Clipper.InflatePaths(Input, -Math.Abs(this.Width), JoinType.Square, Ends);
177175
if (Output.Count != 1) { throw new InvalidDataException("Trying to convert paths to polygons resulted in multiple polygons."); }
178176
return Output[0];
179177
}
178+
179+
public void ConvertType4()
180+
{
181+
if (this.Coords != null && this.Coords.Length >= 2)
182+
{
183+
if (this.ExtensionStart != 0)
184+
{
185+
double StartDeltaX = this.Coords[1].x - this.Coords[0].x;
186+
double StartDeltaY = this.Coords[1].y - this.Coords[0].y;
187+
if (StartDeltaY == 0) { this.Coords[0].x -= (StartDeltaX >= 0) ? this.ExtensionStart : -this.ExtensionStart; } // Horizontal
188+
else if (StartDeltaX == 0) { this.Coords[0].y -= (StartDeltaY >= 0) ? this.ExtensionStart : -this.ExtensionStart; } // Vertical
189+
else // Angled
190+
{
191+
double Slope = StartDeltaY / StartDeltaX;
192+
double XIncr = Math.Abs(this.ExtensionStart) / Math.Sqrt((Slope * Slope) + 1); // Always pos
193+
double YIncr = Math.Abs(Slope) * XIncr; // Always pos
194+
bool XOffsetIsPos = (StartDeltaX > 0) ^ (this.ExtensionStart > 0);
195+
bool YOffsetIsPos = (StartDeltaY > 0) ^ (this.ExtensionStart > 0);
196+
this.Coords[0].x += XOffsetIsPos ? XIncr : -XIncr;
197+
this.Coords[0].y += YOffsetIsPos ? YIncr : -YIncr;
198+
}
199+
}
200+
if (this.ExtensionEnd != 0)
201+
{
202+
double EndDeltaX = this.Coords[^1].x - this.Coords[^2].x;
203+
double EndDeltaY = this.Coords[^1].y - this.Coords[^2].y;
204+
if (EndDeltaY == 0) { this.Coords[^1].x += (EndDeltaX >= 0) ? this.ExtensionEnd : -this.ExtensionEnd; } // Horizontal
205+
else if (EndDeltaX == 0) { this.Coords[^1].y += (EndDeltaY >= 0) ? this.ExtensionEnd : -this.ExtensionEnd; } // Vertical
206+
else // Angled
207+
{
208+
double Slope = EndDeltaY / EndDeltaX;
209+
double XIncr = Math.Abs(this.ExtensionEnd) / Math.Sqrt((Slope * Slope) + 1); // Always pos
210+
double YIncr = Math.Abs(Slope) * XIncr; // Always pos
211+
bool XOffsetIsNeg = (EndDeltaX > 0) ^ (this.ExtensionEnd > 0); // Note inverted logic from start for these 4 lines
212+
bool YOffsetIsNeg = (EndDeltaY > 0) ^ (this.ExtensionEnd > 0);
213+
this.Coords[^1].x += XOffsetIsNeg ? -XIncr : XIncr;
214+
this.Coords[^1].y += YOffsetIsNeg ? -YIncr : YIncr;
215+
}
216+
}
217+
}
218+
else { throw new InvalidDataException("Trying to convert type 4 path to polygon with no or too few points."); }
219+
this.PathType = 0;
220+
}
221+
180222
public override Path Clone() => new()
181223
{
182224
Coords = (PointD[]?)this.Coords?.Clone(),

GDStoSVG/SVGWriter.cs

Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -146,6 +146,11 @@ public void WritePath(Path path, Transform trans)
146146
if (path.Coords!.Length < 2) { Console.WriteLine("Skipping path with less than 2 points."); return; }
147147
if (!this.Output.ContainsKey((short)path.Layer!)) { this.Output.Add((short)path.Layer, new List<string>()); }
148148

149+
string EndcapType = "butt";
150+
if (path.PathType == 1) { EndcapType = "round"; }
151+
if (path.PathType == 2) { EndcapType = "square"; } // TODO: This doesn't properly handle 0-length paths. Works if optimizing though.
152+
if (path.PathType == 4) { path.ConvertType4(); } // Converts to butt type by adjusting ends
153+
149154
string Out = @"<polyline points=""";
150155
for(int i = 0; i < path.Coords.Length; i++)
151156
{
@@ -155,9 +160,6 @@ public void WritePath(Path path, Transform trans)
155160
UpdateExtents(Transformed.x, Transformed.y);
156161
}
157162

158-
string EndcapType = "butt"; // Doesn't support type 4.
159-
if (path.PathType == 1) { EndcapType = "round"; }
160-
if (path.PathType == 2) { EndcapType = "square"; }
161163
Layer Layer = GetLayer((short)path.Layer!);
162164
double Width = path.Width < 0 ? -path.Width : path.Width * trans.Magnification;
163165

0 commit comments

Comments
 (0)