pcut_uwp/PCUT/PCUT/Extensions/HpglExtensions.cs

173 lines
7.4 KiB
C#

using PCUT.Extensions.Transforms;
using SvgPathProperties;
using SvgPathProperties.Base;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Xml.Linq;
namespace PCUT.Extensions
{
public static class HpglExtensions
{
private const double MinimumCurveSegmentLength = 1; // mm
private const double UnitLength = 0.025; // mm
private const int BufferLength = 15000; // byte
class StartPointComparer : IComparer<Point>
{
public static StartPointComparer Instance = new StartPointComparer();
public int Compare(Point x, Point y)
{
return x.X != y.X ? x.X.CompareTo(y.X) : y.Y.CompareTo(x.Y);
}
}
public static IEnumerable<(string Path, TimeSpan EstimateTime)> ToHPGL(
this XDocument svg,
(double Width, double Height) size,
int speed, // mm/s
int pen = 0)
{
var minCurveSegmentLength = MinimumCurveSegmentLength / UnitLength;
var widthUnit = (int)Math.Ceiling(size.Width / UnitLength);
var heightUnit = (int)Math.Ceiling(size.Height / UnitLength);
SvgTransform transform = new TranslateTransform(0, heightUnit);
var flipTransform = new ScaleTransform(1, -1);
flipTransform.SetNext(transform);
transform = flipTransform;
var scaleTransform = new ScaleTransform(1 / UnitLength, 1 / UnitLength);
scaleTransform.SetNext(transform);
transform = scaleTransform;
var paths = new List<SvgPath>();
svg.GetPaths(paths, transform);
yield return ($"IN;SP{pen};", TimeSpan.FromMilliseconds(200));
var builder = new StringBuilder();
TimeSpan timeSpan = TimeSpan.Zero;
var maxX = 0d;
var length = 0d;
foreach (var data in paths.OrderBy(x => x.GetPointAtLength(0), StartPointComparer.Instance))
{
foreach (var segment in data.Segments)
{
switch (segment)
{
case MoveCommand moveCommand:
{
var lengthX = Math.Abs(maxX - moveCommand.X);
builder.AddHpglMove(moveCommand.X, moveCommand.Y, ref maxX);
length += (lengthX + heightUnit) * 0.75;
break;
}
case LineCommand lineCommand:
{
builder.AddHpglLine(lineCommand.ToX, lineCommand.ToY, ref maxX);
length += lineCommand.Length;
break;
}
case BezierCommand bezierCommand:
{
var iterationCount = (int)Math.Ceiling(bezierCommand.Length / minCurveSegmentLength);
for (int i = 1; i < iterationCount; i++)
{
var point = bezierCommand.GetPointAtLength(i * minCurveSegmentLength);
builder.AddHpglLine(point.X, point.Y, ref maxX);
length += minCurveSegmentLength;
if (builder.Length > BufferLength)
{
timeSpan = CalculateWaitTime(length, speed);
yield return (builder.ToString(), timeSpan);
builder.Clear();
length = 0;
}
}
var end = bezierCommand.IsQuadratic ? bezierCommand.Cp2OrEnd : bezierCommand.End;
builder.AddHpglLine(end.X, end.Y, ref maxX);
length += minCurveSegmentLength;
break;
}
case ArcCommand arcCommand:
{
var iterationCount = (int)Math.Ceiling(arcCommand.Length / minCurveSegmentLength);
for (int i = 1; i < iterationCount; i++)
{
var point = arcCommand.GetPointAtLength(i * minCurveSegmentLength);
builder.AddHpglLine(point.X, point.Y, ref maxX);
length += minCurveSegmentLength;
if (builder.Length > BufferLength)
{
timeSpan = CalculateWaitTime(length, speed);
yield return (builder.ToString(), timeSpan);
builder.Clear();
length = 0;
}
}
builder.AddHpglLine(arcCommand.ToX, arcCommand.ToY, ref maxX);
length += minCurveSegmentLength;
break;
}
}
if (builder.Length > BufferLength)
{
timeSpan = CalculateWaitTime(length, speed);
yield return (builder.ToString(), timeSpan);
builder.Clear();
length = 0;
}
}
timeSpan = CalculateWaitTime(length, speed);
yield return (builder.ToString(), timeSpan);
builder.Clear();
length = 0;
}
yield return ($"PU{maxX + 2000},0;IN;", TimeSpan.Zero);
}
private static void GetPaths(this XDocument document, List<SvgPath> paths, SvgTransform transform = null)
{
foreach (var component in document.Root.Descendants().Where(z => z.Name.LocalName == "path"))
{
if (!component.IsHidden())
{
var path = transform.Apply(new SvgPath(component.Attribute("d").Value, unarc: true));
paths.Add(path);
}
}
}
private static TimeSpan CalculateWaitTime(double length, int speed)
{
return TimeSpan.FromSeconds((int)Math.Ceiling((length * UnitLength) / speed));
}
private static StringBuilder AddHpglMove(this StringBuilder builder, double x, double y, ref double maxX)
{
x = Math.Round(x);
y = Math.Round(y);
maxX = Math.Max(x, maxX);
return builder
.Append("PU")
.Append(x).Append(',')
.Append(y).Append(";");
}
private static StringBuilder AddHpglLine(this StringBuilder builder, double x, double y, ref double maxX)
{
x = Math.Round(x);
y = Math.Round(y);
maxX = Math.Max(x, maxX);
return builder
.Append("PD")
.Append(x).Append(',')
.Append(y).Append(";");
}
}
}