You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

368 lines
15 KiB
C#

using System;
using System.Collections.Generic;
using System.Drawing;
using System.Drawing.Drawing2D;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace DeepNestLib
{
public class SvgParser
{
public static RawDetail LoadSvg(string path)
{
XDocument doc = XDocument.Load(path);
var fi = new FileInfo(path);
RawDetail s = new RawDetail();
s.Name = fi.Name;
List<GraphicsPath> paths = new List<GraphicsPath>();
var ns = doc.Descendants().First().Name.Namespace.NamespaceName;
foreach (var item in doc.Descendants("path"))
{
var dd = (item.Attribute("d").Value);
List<string> cmnds = new List<string>();
StringBuilder sb = new StringBuilder();
for (int i = 0; i < dd.Length; i++)
{
if (char.IsLetter(dd[i]))
{
if (sb.Length > 0)
{
cmnds.Add(sb.ToString());
}
sb = new StringBuilder();
}
sb.Append(dd[i]);
}
if (sb.Length > 0)
{
cmnds.Add(sb.ToString());
}
//GraphicsPath p = new GraphicsPath();
//polygons.Add(new SvgNestPort.Polygon() { orig = item,
// /*Points = p.PathPoints.Select(z => new SvgPoint(z.X, z.Y)).ToArray()*/ });
}
foreach (var item in doc.Descendants("rect"))
{
float xx = 0;
float yy = 0;
if (item.Attribute("x") != null)
{
xx = float.Parse(item.Attribute("x").Value);
}
if (item.Attribute("y") != null)
{
yy = float.Parse(item.Attribute("y").Value);
}
var ww = float.Parse(item.Attribute("width").Value);
var hh = float.Parse(item.Attribute("height").Value);
GraphicsPath p = new GraphicsPath();
p.AddRectangle(new RectangleF(xx, yy, ww, hh));
s.Outers.Add(new LocalContour() { Points = p.PathPoints.ToList() });
}
foreach (var item in doc.Descendants(XName.Get("polygon", ns)))
{
var str = item.Attribute("points").Value.ToString();
var spl = str.Split(new string[] { " " }, StringSplitOptions.RemoveEmptyEntries);
List<PointF> points = new List<PointF>();
foreach (var sitem in spl)
{
var spl2 = sitem.Split(new string[] { "," }, StringSplitOptions.RemoveEmptyEntries).ToArray();
var ar = spl2.Select(z => float.Parse(z, CultureInfo.InvariantCulture)).ToArray();
points.Add(new PointF(ar[0], ar[1]));
}
s.Outers.Add(new LocalContour() { Points = points.ToList() });
}
return s;
}
public static void Export(string path, IEnumerable<NFP> polygons, IEnumerable<NFP> sheets)
{
StringBuilder sb = new StringBuilder();
sb.AppendLine(" <svg version=\"1.1\" id=\"svg2\" xmlns=\"http://www.w3.org/2000/svg\" xmlns:xlink=\"http://www.w3.org/1999/xlink\" xml:space=\"preserve\">");
foreach (var item in polygons.Union(sheets))
{
if (!sheets.Contains(item))
{
if (!item.fitted) continue;
}
var m = new Matrix();
m.Translate((float)item.x, (float)item.y);
m.Rotate(item.rotation);
PointF[] pp = item.Points.Select(z => new PointF((float)z.x, (float)z.y)).ToArray();
m.TransformPoints(pp);
var points = pp.Select(z => new SvgPoint(z.X, z.Y)).ToArray();
string fill = "lightblue";
if (sheets.Contains(item))
{
fill = "none";
}
sb.AppendLine($"<path fill=\"{fill}\" stroke=\"black\" d=\"");
for (int i = 0; i < points.Count(); i++)
{
var p = points[i];
string coord = p.x.ToString().Replace(",", ".") + " " + p.y.ToString().Replace(",", ".");
if (i == 0)
{
sb.Append("M" + coord + " ");
continue;
}
sb.Append("L" + coord + " ");
}
sb.Append("z ");
if (item.children != null)
{
foreach (var citem in item.children)
{
pp = citem.Points.Select(z => new PointF((float)z.x, (float)z.y)).ToArray();
m.TransformPoints(pp);
points = pp.Select(z => new SvgPoint(z.X, z.Y)).Reverse().ToArray();
for (int i = 0; i < points.Count(); i++)
{
var p = points[i];
string coord = p.x.ToString().Replace(",", ".") + " " + p.y.ToString().Replace(",", ".");
if (i == 0)
{
sb.Append("M" + coord + " ");
continue;
}
sb.Append("L" + coord + " ");
}
sb.Append("z ");
}
}
sb.Append("\"/>");
}
sb.AppendLine("</svg>");
File.WriteAllText(path, sb.ToString());
}
public static SvgConfig Conf = new SvgConfig();
// return a polygon from the given SVG element in the form of an array of points
public static NFP polygonify(XElement element)
{
List<SvgPoint> poly = new List<SvgPoint>();
int i;
switch (element.Name.LocalName)
{
case "polygon":
case "polyline":
{
var pp = element.Attribute("points").Value;
var spl = pp.Split(new char[] { ' ' }, StringSplitOptions.RemoveEmptyEntries);
foreach (var item in spl)
{
var spl2 = item.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries).ToArray();
var x = float.Parse(spl2[0], CultureInfo.InvariantCulture);
var y = float.Parse(spl2[1], CultureInfo.InvariantCulture);
poly.Add(new SvgPoint(x, y));
}
}
break;
case "rect":
{
float x = 0;
float y = 0;
if (element.Attribute("x") != null)
{
x = float.Parse(element.Attribute("x").Value, CultureInfo.InvariantCulture);
}
if (element.Attribute("y") != null)
{
y = float.Parse(element.Attribute("y").Value, CultureInfo.InvariantCulture);
}
var w = float.Parse(element.Attribute("width").Value, CultureInfo.InvariantCulture);
var h = float.Parse(element.Attribute("height").Value, CultureInfo.InvariantCulture);
poly.Add(new SvgPoint(x, y));
poly.Add(new SvgPoint(x + w, y));
poly.Add(new SvgPoint(x + w, y + h));
poly.Add(new SvgPoint(x, y + h));
}
break;
case "circle":
throw new NotImplementedException();
break;
case "ellipse":
throw new NotImplementedException();
break;
case "path":
throw new NotImplementedException();
// // we'll assume that splitpath has already been run on this path, and it only has one M/m command
// var seglist = element.pathSegList;
// var firstCommand = seglist.getItem(0);
// var lastCommand = seglist.getItem(seglist.numberOfItems - 1);
// var x = 0, y = 0, x0 = 0, y0 = 0, x1 = 0, y1 = 0, x2 = 0, y2 = 0, prevx = 0, prevy = 0, prevx1 = 0, prevy1 = 0, prevx2 = 0, prevy2 = 0;
// for (var i = 0; i < seglist.numberOfItems; i++)
// {
// var s = seglist.getItem(i);
// var command = s.pathSegTypeAsLetter;
// prevx = x;
// prevy = y;
// prevx1 = x1;
// prevy1 = y1;
// prevx2 = x2;
// prevy2 = y2;
// if (/[MLHVCSQTA] /.test(command))
// {
// if ('x1' in s) x1 = s.x1;
// if ('x2' in s) x2 = s.x2;
// if ('y1' in s) y1 = s.y1;
// if ('y2' in s) y2 = s.y2;
// if ('x' in s) x = s.x;
// if ('y' in s) y = s.y;
// }
// else{
// if ('x1' in s) x1=x+s.x1;
// if ('x2' in s) x2=x+s.x2;
// if ('y1' in s) y1=y+s.y1;
// if ('y2' in s) y2=y+s.y2;
// if ('x' in s) x+=s.x;
// if ('y' in s) y+=s.y;
// }
// switch(command){
// // linear line types
// case 'm':
// case 'M':
// case 'l':
// case 'L':
// case 'h':
// case 'H':
// case 'v':
// case 'V':
// var point = { };
// point.x = x;
// point.y = y;
// poly.push(point);
// break;
// // Quadratic Beziers
// case 't':
// case 'T':
// // implicit control point
// if(i > 0 && /[QqTt]/.test(seglist.getItem(i-1).pathSegTypeAsLetter)){
// x1 = prevx + (prevx-prevx1);
// y1 = prevy + (prevy-prevy1);
// }
// else{
// x1 = prevx;
// y1 = prevy;
// }
// case 'q':
// case 'Q':
// var pointlist = GeometryUtil.QuadraticBezier.linearize({x: prevx, y: prevy}, {x: x, y: y}, {x: x1, y: y1}, this.conf.tolerance);
//pointlist.shift(); // firstpoint would already be in the poly
// for(var j=0; j<pointlist.length; j++){
// var point = { };
// point.x = pointlist[j].x;
// point.y = pointlist[j].y;
// poly.push(point);
//}
// break;
// case 's':
// case 'S':
// if(i > 0 && /[CcSs]/.test(seglist.getItem(i-1).pathSegTypeAsLetter)){
// x1 = prevx + (prevx - prevx2);
// y1 = prevy + (prevy - prevy2);
//}
// else{
// x1 = prevx;
// y1 = prevy;
//}
// case 'c':
// case 'C':
// var pointlist = GeometryUtil.CubicBezier.linearize({ x: prevx, y: prevy}, { x: x, y: y}, { x: x1, y: y1}, { x: x2, y: y2}, this.conf.tolerance);
//pointlist.shift(); // firstpoint would already be in the poly
// for(var j=0; j<pointlist.length; j++){
// var point = { };
// point.x = pointlist[j].x;
// point.y = pointlist[j].y;
// poly.push(point);
//}
// break;
// case 'a':
// case 'A':
// var pointlist = GeometryUtil.Arc.linearize({ x: prevx, y: prevy}, { x: x, y: y}, s.r1, s.r2, s.angle, s.largeArcFlag,s.sweepFlag, this.conf.tolerance);
//pointlist.shift();
// for(var j=0; j<pointlist.length; j++){
// var point = { };
// point.x = pointlist[j].x;
// point.y = pointlist[j].y;
// poly.push(point);
//}
// break;
// case 'z': case 'Z': x=x0; y=y0; break;
//}
// // Record the start of a subpath
// if (command=='M' || command=='m') x0=x, y0=y;
// }
break;
}
// do not include last point if coincident with starting point
while (poly.Count > 0 && GeometryUtil._almostEqual(poly[0].x, poly[poly.Count - 1].x, Conf.toleranceSvg)
&& GeometryUtil._almostEqual(poly[0].y, poly[poly.Count - 1].y, Conf.toleranceSvg))
{
poly.RemoveAt(0);
}
return new NFP() { Points = new List<SvgPoint>(poly.ToArray()) };
}
}
public class SvgConfig
{
public float tolerance = 2f; // max bound for bezier->line segment conversion, in native SVG units
public float toleranceSvg = 0.005f;// fudge factor for browser inaccuracy in SVG unit handling
}
public class LocalContour
{
public float Len
{
get
{
float len = 0;
for (int i = 1; i <= Points.Count; i++)
{
var p1 = Points[i - 1];
var p2 = Points[i % Points.Count];
len += (float)Math.Sqrt(Math.Pow(p1.X - p2.X, 2) + Math.Pow(p1.Y - p2.Y, 2));
}
return len;
}
}
public List<PointF> Points = new List<PointF>();
public bool Enable = true;
}
}