173 lines
7.4 KiB
C#
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(";");
|
|
}
|
|
}
|
|
}
|