зеркало из https://github.com/SixLabors/Shapes.git
add path outlining
This commit is contained in:
Родитель
922b5a61d0
Коммит
d8bb364084
|
@ -15,8 +15,8 @@
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha5-00040" />
|
<PackageReference Include="ImageSharp" Version="1.0.0-alpha6-00065" />
|
||||||
<PackageReference Include="ImageSharp" Version="1.0.0-alpha5-00046" />
|
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha6-00026" />
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
|
|
||||||
</Project>
|
</Project>
|
||||||
|
|
|
@ -33,9 +33,28 @@ namespace SixLabors.Shapes.DrawShapesWithImageSharp
|
||||||
DrawSerializedOPenSansLetterShape_a();
|
DrawSerializedOPenSansLetterShape_a();
|
||||||
DrawSerializedOPenSansLetterShape_o();
|
DrawSerializedOPenSansLetterShape_o();
|
||||||
|
|
||||||
DrawFatL();
|
OutlineFatL();
|
||||||
|
OutlineSquare();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private static void OutlineFatL()
|
||||||
|
{
|
||||||
|
var shape = new Path(new LinearLineSegment(new Vector2(8, 8),
|
||||||
|
new Vector2(64, 8),
|
||||||
|
new Vector2(64, 64),
|
||||||
|
new Vector2(120, 64),
|
||||||
|
new Vector2(120, 120),
|
||||||
|
new Vector2(8, 120))).GenerateOutline(3, new[] { 2f, 1f });
|
||||||
|
shape.SaveImage("Outline", "ClippedCorner.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void OutlineSquare()
|
||||||
|
{
|
||||||
|
var p = new Rectangle(new Vector2(10, 10), new Size(30, 30)).GenerateOutline(3, new[] { 2f, 1f });
|
||||||
|
p.SaveImage("Outline", "Square.png");
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private static void DrawFatL()
|
private static void DrawFatL()
|
||||||
{
|
{
|
||||||
var shape = new Polygon(new LinearLineSegment(new Vector2(8, 8),
|
var shape = new Polygon(new LinearLineSegment(new Vector2(8, 8),
|
||||||
|
@ -80,7 +99,7 @@ namespace SixLabors.Shapes.DrawShapesWithImageSharp
|
||||||
.ToArray();
|
.ToArray();
|
||||||
return new Polygon(new LinearLineSegment(points));
|
return new Polygon(new LinearLineSegment(points));
|
||||||
}).ToArray();
|
}).ToArray();
|
||||||
var complex = new ComplexPolygon(polys);
|
var complex = new ComplexPolygon(polys).Scale(10).GenerateOutline(6, new float[] { 2, 1 }); ;
|
||||||
complex.SaveImage("letter", "o.png");
|
complex.SaveImage("letter", "o.png");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -282,8 +282,11 @@ namespace SixLabors.Shapes
|
||||||
|
|
||||||
if (precaclulate[i].RemoveLastIntersectionAndSkip)
|
if (precaclulate[i].RemoveLastIntersectionAndSkip)
|
||||||
{
|
{
|
||||||
position--;
|
if (position > 0)
|
||||||
count++;
|
{
|
||||||
|
position--;
|
||||||
|
count++;
|
||||||
|
}
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
if (precaclulate[i].DoIntersect)
|
if (precaclulate[i].DoIntersect)
|
||||||
|
|
|
@ -0,0 +1,176 @@
|
||||||
|
using ClipperLib;
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Collections.Immutable;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Numerics;
|
||||||
|
using System.Text;
|
||||||
|
|
||||||
|
namespace SixLabors.Shapes
|
||||||
|
{
|
||||||
|
/// <summary>
|
||||||
|
/// Path extensions to generate outlines of paths.
|
||||||
|
/// </summary>
|
||||||
|
public static class Outliner
|
||||||
|
{
|
||||||
|
private const float ScalingFactor = 1000.0f;
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a outline of the path with alternating on and off segments based on the pattern.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">the path to outline</param>
|
||||||
|
/// <param name="width">The final width outline</param>
|
||||||
|
/// <param name="pattern">The pattern made of multiples of the width.</param>
|
||||||
|
/// <returns>A new path representing the outline.</returns>
|
||||||
|
public static IPath GenerateOutline(this IPath path, float width, float[] pattern)
|
||||||
|
{
|
||||||
|
return path.GenerateOutline(width, pattern, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a outline of the path with alternating on and off segments based on the pattern.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">the path to outline</param>
|
||||||
|
/// <param name="width">The final width outline</param>
|
||||||
|
/// <param name="pattern">The pattern made of multiples of the width.</param>
|
||||||
|
/// <param name="startOff">Weather the first item in the pattern is on or off.</param>
|
||||||
|
/// <returns>A new path representing the outline.</returns>
|
||||||
|
public static IPath GenerateOutline(this IPath path, float width, float[] pattern, bool startOff)
|
||||||
|
{
|
||||||
|
if (pattern == null || pattern.Length < 2)
|
||||||
|
{
|
||||||
|
return path.GenerateOutline(width);
|
||||||
|
}
|
||||||
|
|
||||||
|
ImmutableArray<ISimplePath> paths = path.Flatten();
|
||||||
|
|
||||||
|
ClipperOffset offset = new ClipperOffset();
|
||||||
|
|
||||||
|
List<IntPoint> buffer = new List<IntPoint>(3);
|
||||||
|
foreach (ISimplePath p in paths)
|
||||||
|
{
|
||||||
|
bool online = !startOff;
|
||||||
|
float targetLength = pattern[0] * width;
|
||||||
|
int patternPos = 0;
|
||||||
|
// create a new list of points representing the new outline
|
||||||
|
int pCount = p.Points.Length;
|
||||||
|
if (!p.IsClosed)
|
||||||
|
{
|
||||||
|
pCount--;
|
||||||
|
}
|
||||||
|
int i = 0;
|
||||||
|
Vector2 currentPoint = p.Points[0];
|
||||||
|
|
||||||
|
while (i < pCount)
|
||||||
|
{
|
||||||
|
int next = (i + 1) % p.Points.Length;
|
||||||
|
Vector2 targetPoint = p.Points[next];
|
||||||
|
float distToNext = Vector2.Distance(currentPoint, targetPoint);
|
||||||
|
if (distToNext > targetLength)
|
||||||
|
{
|
||||||
|
// find a point between the 2
|
||||||
|
float t = targetLength / distToNext;
|
||||||
|
|
||||||
|
Vector2 point = (currentPoint * (1 - t)) + (targetPoint * t);
|
||||||
|
buffer.Add(currentPoint.ToPoint());
|
||||||
|
buffer.Add(point.ToPoint());
|
||||||
|
// we now inset a line joining
|
||||||
|
|
||||||
|
if (online)
|
||||||
|
{
|
||||||
|
offset.AddPath(buffer, JoinType.jtSquare, EndType.etOpenButt);
|
||||||
|
}
|
||||||
|
online = !online;
|
||||||
|
|
||||||
|
buffer.Clear();
|
||||||
|
|
||||||
|
currentPoint = point;
|
||||||
|
|
||||||
|
// next length
|
||||||
|
patternPos = (patternPos + 1) % pattern.Length;
|
||||||
|
targetLength = pattern[patternPos] * width;
|
||||||
|
|
||||||
|
}
|
||||||
|
else if (distToNext <= targetLength)
|
||||||
|
{
|
||||||
|
buffer.Add(currentPoint.ToPoint());
|
||||||
|
currentPoint = targetPoint;
|
||||||
|
i++;
|
||||||
|
targetLength -= distToNext;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (buffer.Count > 0)
|
||||||
|
{
|
||||||
|
if (p.IsClosed)
|
||||||
|
{
|
||||||
|
buffer.Add(p.Points.First().ToPoint());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
buffer.Add(p.Points.Last().ToPoint());
|
||||||
|
}
|
||||||
|
|
||||||
|
if (online)
|
||||||
|
{
|
||||||
|
offset.AddPath(buffer, JoinType.jtSquare, EndType.etOpenButt);
|
||||||
|
}
|
||||||
|
online = !online;
|
||||||
|
|
||||||
|
buffer.Clear();
|
||||||
|
patternPos = (patternPos + 1) % pattern.Length;
|
||||||
|
targetLength = pattern[patternPos] * width;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExecuteOutliner(width, offset);
|
||||||
|
}
|
||||||
|
/// <summary>
|
||||||
|
/// Generates a solid outline of the path.
|
||||||
|
/// </summary>
|
||||||
|
/// <param name="path">the path to outline</param>
|
||||||
|
/// <param name="width">The final width outline</param>
|
||||||
|
/// <returns>A new path representing the outline.</returns>
|
||||||
|
public static IPath GenerateOutline(this IPath path, float width)
|
||||||
|
{
|
||||||
|
ClipperOffset offset = new ClipperLib.ClipperOffset();
|
||||||
|
|
||||||
|
//pattern can be applied to the path by cutting it into segments
|
||||||
|
System.Collections.Immutable.ImmutableArray<ISimplePath> paths = path.Flatten();
|
||||||
|
foreach (ISimplePath p in paths)
|
||||||
|
{
|
||||||
|
System.Collections.Immutable.ImmutableArray<Vector2> vectors = p.Points;
|
||||||
|
List<IntPoint> points = new List<ClipperLib.IntPoint>(vectors.Length);
|
||||||
|
foreach (Vector2 v in vectors)
|
||||||
|
{
|
||||||
|
points.Add(new IntPoint(v.X * ScalingFactor, v.Y * ScalingFactor));
|
||||||
|
}
|
||||||
|
|
||||||
|
EndType type = p.IsClosed ? EndType.etClosedLine : EndType.etOpenButt;
|
||||||
|
|
||||||
|
|
||||||
|
offset.AddPath(points, JoinType.jtMiter, type);
|
||||||
|
}
|
||||||
|
|
||||||
|
return ExecuteOutliner(width, offset);
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IPath ExecuteOutliner(float width, ClipperOffset offset)
|
||||||
|
{
|
||||||
|
List<List<IntPoint>> tree = new List<List<IntPoint>>();
|
||||||
|
offset.Execute(ref tree, width * ScalingFactor / 2);
|
||||||
|
List<Polygon> polygons = new List<Polygon>();
|
||||||
|
foreach (List<IntPoint> pt in tree)
|
||||||
|
{
|
||||||
|
Vector2[] points = pt.Select(p => new Vector2(p.X / ScalingFactor, p.Y / ScalingFactor)).ToArray();
|
||||||
|
polygons.Add(new Polygon(new LinearLineSegment(points)));
|
||||||
|
}
|
||||||
|
|
||||||
|
return new ComplexPolygon(polygons.ToArray());
|
||||||
|
}
|
||||||
|
|
||||||
|
private static IntPoint ToPoint(this Vector2 vector)
|
||||||
|
{
|
||||||
|
return new IntPoint(vector.X * ScalingFactor, vector.Y * ScalingFactor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Загрузка…
Ссылка в новой задаче