зеркало из https://github.com/SixLabors/Shapes.git
add path outlining
This commit is contained in:
Родитель
922b5a61d0
Коммит
d8bb364084
|
@ -15,8 +15,8 @@
|
|||
</ItemGroup>
|
||||
|
||||
<ItemGroup>
|
||||
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha5-00040" />
|
||||
<PackageReference Include="ImageSharp" Version="1.0.0-alpha5-00046" />
|
||||
<PackageReference Include="ImageSharp" Version="1.0.0-alpha6-00065" />
|
||||
<PackageReference Include="ImageSharp.Drawing" Version="1.0.0-alpha6-00026" />
|
||||
</ItemGroup>
|
||||
|
||||
</Project>
|
||||
|
|
|
@ -33,9 +33,28 @@ namespace SixLabors.Shapes.DrawShapesWithImageSharp
|
|||
DrawSerializedOPenSansLetterShape_a();
|
||||
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()
|
||||
{
|
||||
var shape = new Polygon(new LinearLineSegment(new Vector2(8, 8),
|
||||
|
@ -80,7 +99,7 @@ namespace SixLabors.Shapes.DrawShapesWithImageSharp
|
|||
.ToArray();
|
||||
return new Polygon(new LinearLineSegment(points));
|
||||
}).ToArray();
|
||||
var complex = new ComplexPolygon(polys);
|
||||
var complex = new ComplexPolygon(polys).Scale(10).GenerateOutline(6, new float[] { 2, 1 }); ;
|
||||
complex.SaveImage("letter", "o.png");
|
||||
}
|
||||
|
||||
|
|
|
@ -282,8 +282,11 @@ namespace SixLabors.Shapes
|
|||
|
||||
if (precaclulate[i].RemoveLastIntersectionAndSkip)
|
||||
{
|
||||
position--;
|
||||
count++;
|
||||
if (position > 0)
|
||||
{
|
||||
position--;
|
||||
count++;
|
||||
}
|
||||
continue;
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
}
|
Загрузка…
Ссылка в новой задаче