Begin work on new command engine.
This commit is contained in:
parent
78c71054b4
commit
61e4d2bd16
@ -21,7 +21,7 @@ namespace Quik.CommandMachine
|
||||
// TODO: Make a real matrix class.
|
||||
public float[] ActiveTransforms { get; }
|
||||
|
||||
public object ActiveStyle { get; }
|
||||
public StyleStack Style { get; }
|
||||
|
||||
protected CommandEngine()
|
||||
{
|
||||
|
@ -25,9 +25,13 @@ namespace Quik
|
||||
/// <summary>
|
||||
/// Indicates whether this texture contains a signed distance field.
|
||||
/// </summary>
|
||||
/// <value></value>
|
||||
public bool SignedDistanceField { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether this texture has premultiplied alpha.
|
||||
/// </summary>
|
||||
public bool PreMultipled { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Upload texture data.
|
||||
/// </summary>
|
||||
|
@ -114,16 +114,16 @@ namespace Quik
|
||||
set => this["list-marker-image"] = value;
|
||||
}
|
||||
|
||||
public float? BorderWidth
|
||||
public float? StrokeWidth
|
||||
{
|
||||
get => (float?)this["border-width"];
|
||||
set => this["border-width"] = value;
|
||||
get => (float?)this["stroke-width"];
|
||||
set => this["stroke-width"] = value;
|
||||
}
|
||||
|
||||
public QuikColor? BorderColor
|
||||
public QuikColor? StrokeColor
|
||||
{
|
||||
get => (QuikColor?)this["border-color"];
|
||||
set => this["border-color"] = value;
|
||||
get => (QuikColor?)this["stroke-color"];
|
||||
set => this["stroke-color"] = value;
|
||||
}
|
||||
|
||||
public QuikFont Font
|
||||
@ -131,6 +131,12 @@ namespace Quik
|
||||
get => (QuikFont)this["font"];
|
||||
set => this["font"] = value;
|
||||
}
|
||||
|
||||
public int? ZIndex
|
||||
{
|
||||
get => (int?)this["z-index"];
|
||||
set => this["z-index"] = value;
|
||||
}
|
||||
}
|
||||
|
||||
public class Style : StyleBase
|
||||
|
109
Quik/VertexGenerator/DrawQueue.cs
Normal file
109
Quik/VertexGenerator/DrawQueue.cs
Normal file
@ -0,0 +1,109 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace Quik.VertexGenerator
|
||||
{
|
||||
public class DrawQueue
|
||||
{
|
||||
private readonly RefList<QuikVertex> _vertices = new RefList<QuikVertex>();
|
||||
private readonly RefList<int> _elements = new RefList<int>();
|
||||
private readonly List<DrawCall> _drawCalls = new List<DrawCall>();
|
||||
private int _start;
|
||||
private int _count;
|
||||
private int _baseOffset;
|
||||
private QuikRectangle _bounds;
|
||||
private QuikTexture _texture;
|
||||
|
||||
public int ZDepth { get; private set; }
|
||||
public QuikVertex[] VertexArray => _vertices.InternalArray;
|
||||
public int VertexCount => _vertices.Count;
|
||||
public int[] ElementArray => _elements.InternalArray;
|
||||
public int ElementCount => _elements.Count;
|
||||
public int DrawCallCount => _drawCalls.Count;
|
||||
public int BaseOffset => _baseOffset;
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
_vertices.Clear();
|
||||
_elements.Clear();
|
||||
_drawCalls.Clear();
|
||||
ZDepth = 0;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void StartDrawCall(in QuikRectangle bounds, QuikTexture texture, int baseOffset)
|
||||
{
|
||||
_start = ElementCount;
|
||||
_count = 0;
|
||||
_texture = texture;
|
||||
_bounds = bounds;
|
||||
_baseOffset = baseOffset;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void StartDrawCall(in QuikRectangle bounds) => StartDrawCall(bounds, null, _vertices.Count);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void StartDrawCall(in QuikRectangle bounds, int baseOffset) => StartDrawCall(bounds, null, baseOffset);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void StartDrawCall(in QuikRectangle bounds, QuikTexture texture) => StartDrawCall(bounds, texture, _vertices.Count);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddVertex(in QuikVertex vertex)
|
||||
{
|
||||
_vertices.Add(in vertex);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void AddElement(int offset)
|
||||
{
|
||||
_elements.Add(offset + _baseOffset);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int RestoreOffset(int baseOffset)
|
||||
{
|
||||
int old = _baseOffset;
|
||||
_baseOffset = baseOffset;
|
||||
return old;
|
||||
}
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int RestoreOffset() => RestoreOffset(_vertices.Count);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int AbsoluteElement(int offset)
|
||||
{
|
||||
return _baseOffset + offset;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public int RelativeElement(int baseOffset, int offset)
|
||||
{
|
||||
return AbsoluteElement(offset) - baseOffset;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void EndDrawCall()
|
||||
{
|
||||
_drawCalls.Add(new DrawCall(_start, _count, _bounds, _texture));
|
||||
}
|
||||
}
|
||||
|
||||
public struct DrawCall
|
||||
{
|
||||
public int Start { get; }
|
||||
public int Count { get; }
|
||||
public QuikRectangle Bounds { get; }
|
||||
public QuikTexture Texture { get; }
|
||||
|
||||
public DrawCall(int start, int count, in QuikRectangle bounds, QuikTexture texture)
|
||||
{
|
||||
Start = start;
|
||||
Count = count;
|
||||
Bounds = bounds;
|
||||
Texture = texture;
|
||||
}
|
||||
}
|
||||
}
|
@ -17,15 +17,21 @@ namespace Quik.VertexGenerator
|
||||
/// Texture Coordinates.
|
||||
/// </summary>
|
||||
public QuikVec2 TextureCoordinates;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Per vertex color value.
|
||||
/// </summary>
|
||||
public QuikColor Color;
|
||||
|
||||
/// <summary>
|
||||
/// Per vertex depth index value.
|
||||
/// </summary>
|
||||
public int ZIndex;
|
||||
|
||||
public static int PositionOffset => 0;
|
||||
public static unsafe int TextureCoordinatesOffset => sizeof(QuikVec2);
|
||||
public static unsafe int ColorOffset => 2 * sizeof(QuikVec2);
|
||||
public static unsafe int ZIndexOffset => ColorOffset + sizeof(QuikColor);
|
||||
public static unsafe int Stride => sizeof(QuikVertex);
|
||||
}
|
||||
}
|
48
Quik/VertexGenerator/RefList.cs
Normal file
48
Quik/VertexGenerator/RefList.cs
Normal file
@ -0,0 +1,48 @@
|
||||
using System;
|
||||
|
||||
namespace Quik.VertexGenerator
|
||||
{
|
||||
/// <summary>
|
||||
/// A small list which whose items can be used by reference.
|
||||
/// </summary>
|
||||
/// <typeparam name="T">Container type.</typeparam>
|
||||
public class RefList<T>
|
||||
{
|
||||
private T[] _array = Array.Empty<T>();
|
||||
private int _count = 0;
|
||||
|
||||
public T[] InternalArray => _array;
|
||||
|
||||
public ref T this[int index] => ref _array[index];
|
||||
|
||||
public int Count => _count;
|
||||
|
||||
public int Capacity => _array.Length;
|
||||
|
||||
public void Add(in T item)
|
||||
{
|
||||
EnsureCapacity(Count + 1);
|
||||
this[_count++] = item;
|
||||
}
|
||||
|
||||
public void Add(T item)
|
||||
{
|
||||
EnsureCapacity(Count + 1);
|
||||
this[_count++] = item;
|
||||
}
|
||||
|
||||
public void Clear()
|
||||
{
|
||||
Array.Resize(ref _array, 0);
|
||||
_count = 0;
|
||||
}
|
||||
|
||||
private void EnsureCapacity(int needed)
|
||||
{
|
||||
while (_array.Length < needed)
|
||||
{
|
||||
Array.Resize(ref _array, Math.Max(1, _array.Length) * 2);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
410
Quik/VertexGenerator/VertexCommandEngine.cs
Normal file
410
Quik/VertexGenerator/VertexCommandEngine.cs
Normal file
@ -0,0 +1,410 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using Quik.CommandMachine;
|
||||
|
||||
namespace Quik.VertexGenerator
|
||||
{
|
||||
public class VertexGeneratorEngine : CommandEngine
|
||||
{
|
||||
public DrawQueue DrawQueue { get; } = new DrawQueue();
|
||||
|
||||
/// <summary>
|
||||
/// Granularity for rounded geometry.
|
||||
/// </summary>
|
||||
protected float CurveGranularity =>
|
||||
(Style["-vertex-curve-granularity"] is float value) ? value : 1.0f;
|
||||
protected QuikVertex StrokeVertex => new QuikVertex()
|
||||
{
|
||||
ZIndex = Style.ZIndex ?? this.ZIndex,
|
||||
Color = Style.StrokeColor ?? QuikColor.Black,
|
||||
};
|
||||
protected QuikVertex FillVertex => new QuikVertex()
|
||||
{
|
||||
ZIndex = Style.ZIndex ?? this.ZIndex,
|
||||
Color = Style.Color ?? QuikColor.White,
|
||||
};
|
||||
|
||||
protected override void ChildProcessCommand(Command name, CommandQueue queue)
|
||||
{
|
||||
base.ChildProcessCommand(name, queue);
|
||||
|
||||
switch(name)
|
||||
{
|
||||
case Command.Line: LineProc(queue); break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Gets the rounding resolution for a line segment or border radius.
|
||||
/// </summary>
|
||||
/// <param name="radius">The width of the line.</param>
|
||||
/// <param name="arc">The angle of the cap or joint arc.</param>
|
||||
/// <returns>The rounding resolution.</returns>
|
||||
protected int GetRoundingResolution(float radius, float arc)
|
||||
{
|
||||
return (int) Math.Ceiling(arc * radius * CurveGranularity);
|
||||
}
|
||||
|
||||
private readonly List<QuikLine> LineList = new List<QuikLine>();
|
||||
private void LineProc(CommandQueue queue)
|
||||
{
|
||||
Frame frame = queue.Dequeue();
|
||||
|
||||
// Clear temporary vector list and retreive all line segments.
|
||||
LineList.Clear();
|
||||
if (frame.Type == FrameType.IVec1)
|
||||
{
|
||||
int count = (int)frame;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
frame = queue.Dequeue();
|
||||
LineList.Add((QuikLine)frame);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
LineList.Add((QuikLine)frame);
|
||||
}
|
||||
|
||||
float width = Style.StrokeWidth ?? 1;
|
||||
|
||||
DrawQueue.StartDrawCall(Viewport);
|
||||
LineInfo prevBase, nextBase = default;
|
||||
for (int i = 0; i < LineList.Count; i++)
|
||||
{
|
||||
QuikLine line = LineList[i];
|
||||
// A line segment needs a start cap if it is the first segment in
|
||||
// the list, or the last end point is not the current start point.
|
||||
bool isStart = (i == 0 || line.Start != LineList[i - 1].End);
|
||||
// A line segment needs an end cap if it is the last line segment
|
||||
// in the list or if the next start point is not the current end point.
|
||||
bool isEnd = (i == LineList.Count - 1 || line.End != LineList[i+1].Start);
|
||||
|
||||
// Generate the main line segment.
|
||||
prevBase = nextBase;
|
||||
nextBase = GenerateLineSegment(line);
|
||||
|
||||
if (isStart)
|
||||
{
|
||||
// Then a start cap if necessary.
|
||||
GenerateCap(line.Start, line.Normal(), prevBase, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise generate the required joint.
|
||||
GenerateJoint(line.Start, LineList[i-1].Normal(), line.Normal(), prevBase, nextBase);
|
||||
}
|
||||
if (isEnd)
|
||||
{
|
||||
// Then generate the end cap if necessary.
|
||||
GenerateCap(line.End, line.Normal(), nextBase, true);
|
||||
}
|
||||
}
|
||||
DrawQueue.EndDrawCall();
|
||||
}
|
||||
|
||||
private LineInfo GenerateLineSegment(in QuikLine line)
|
||||
{
|
||||
QuikVertex vertex = StrokeVertex;
|
||||
QuikVertex a, b, c, d;
|
||||
QuikVec2 normal = line.Normal();
|
||||
float width = Style.StrokeWidth ?? 1;
|
||||
|
||||
a = b = c = d = vertex;
|
||||
a.Position = line.Start + width / 2 * normal;
|
||||
b.Position = line.Start - width / 2 * normal;
|
||||
c.Position = line.End + width / 2 * normal;
|
||||
d.Position = line.End - width / 2 * normal;
|
||||
|
||||
DrawQueue.RestoreOffset();
|
||||
DrawQueue.AddVertex(a);
|
||||
DrawQueue.AddVertex(b);
|
||||
DrawQueue.AddVertex(c);
|
||||
DrawQueue.AddVertex(d);
|
||||
DrawQueue.AddElement(1); DrawQueue.AddElement(2); DrawQueue.AddElement(0);
|
||||
DrawQueue.AddElement(1); DrawQueue.AddElement(3); DrawQueue.AddElement(2);
|
||||
return new LineInfo(DrawQueue.BaseOffset, 0, 1, 2, 3);
|
||||
}
|
||||
|
||||
private void GenerateJoint(
|
||||
in QuikVec2 center,
|
||||
in QuikVec2 prevNormal,
|
||||
in QuikVec2 nextNormal,
|
||||
in LineInfo prevInfo,
|
||||
in LineInfo nextInfo)
|
||||
{
|
||||
// Figure out which side needs the joint.
|
||||
QuikVec2 meanNormal = 0.5f * (prevNormal + nextNormal);
|
||||
QuikVec2 meanTangent = new QuikVec2(meanNormal.Y, -meanNormal.X);
|
||||
QuikVec2 positiveEdge = ((center + nextNormal) - (center + prevNormal)).Normalize();
|
||||
QuikVec2 negativeEdge = ((center - nextNormal) - (center - prevNormal)).Normalize();
|
||||
float positive, negative;
|
||||
positive = QuikVec2.Dot(meanTangent, positiveEdge);
|
||||
negative = QuikVec2.Dot(meanNormal, negativeEdge);
|
||||
|
||||
if (positive == negative)
|
||||
{
|
||||
// To be fair this is highly unlikely considering the nature of
|
||||
// floats, but, generate an end cap to handle a cusp.
|
||||
GenerateCap(center, nextNormal, nextInfo, true);
|
||||
return;
|
||||
}
|
||||
|
||||
QuikVertex vertex = StrokeVertex;
|
||||
float radius = Style.StrokeWidth/2 ?? 0.5f;
|
||||
float arc = MathF.Acos(QuikVec2.Dot(prevNormal, nextNormal));
|
||||
int resolution = GetRoundingResolution(radius, arc);
|
||||
bool isNegative = positive < negative;
|
||||
|
||||
vertex.Position = center;
|
||||
DrawQueue.RestoreOffset();
|
||||
DrawQueue.AddVertex(vertex);
|
||||
|
||||
int lastIndex, endIndex;
|
||||
if (isNegative)
|
||||
{
|
||||
lastIndex = DrawQueue.RelativeElement(prevInfo.BaseOffset, prevInfo.EndNegative);
|
||||
endIndex = DrawQueue.RelativeElement(nextInfo.BaseOffset, nextInfo.StartNegative);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastIndex = DrawQueue.RelativeElement(nextInfo.BaseOffset, nextInfo.StartNegative);
|
||||
endIndex = DrawQueue.RelativeElement(prevInfo.BaseOffset, prevInfo.EndPositive);
|
||||
}
|
||||
|
||||
for (int i = 0; i < resolution; i++)
|
||||
{
|
||||
float angle = (float)(i+1) / (resolution + 1) * arc;
|
||||
float cos = MathF.Cos(angle);
|
||||
float sin = MathF.Sin(angle);
|
||||
|
||||
QuikVec2 displacement;
|
||||
if (isNegative)
|
||||
{
|
||||
displacement = new QuikVec2()
|
||||
{
|
||||
X = -prevNormal.X * cos + prevNormal.Y * sin,
|
||||
Y = -prevNormal.X * sin - prevNormal.Y * cos
|
||||
} * radius;
|
||||
}
|
||||
else
|
||||
{
|
||||
displacement = new QuikVec2()
|
||||
{
|
||||
X = nextNormal.X * cos - nextNormal.Y * sin,
|
||||
Y = nextNormal.X * sin + nextNormal.Y * cos
|
||||
} * radius;
|
||||
}
|
||||
|
||||
vertex.Position = center + displacement;
|
||||
|
||||
DrawQueue.AddVertex(vertex);
|
||||
DrawQueue.AddElement(lastIndex);
|
||||
DrawQueue.AddElement(i);
|
||||
DrawQueue.AddElement(0);
|
||||
|
||||
lastIndex = i;
|
||||
}
|
||||
|
||||
DrawQueue.AddElement(lastIndex);
|
||||
DrawQueue.AddElement(endIndex);
|
||||
DrawQueue.AddElement(0);
|
||||
|
||||
}
|
||||
|
||||
private void GenerateCap(
|
||||
in QuikVec2 center,
|
||||
in QuikVec2 normal,
|
||||
in LineInfo info,
|
||||
bool endCap)
|
||||
{
|
||||
int lastIndex, startIndex;
|
||||
QuikVertex vertex = StrokeVertex;
|
||||
float radius = Style.StrokeWidth ?? 1.0f;
|
||||
int resolution = GetRoundingResolution(radius, MathF.PI);
|
||||
|
||||
DrawQueue.RestoreOffset();
|
||||
if (endCap)
|
||||
{
|
||||
lastIndex = DrawQueue.RelativeElement(info.BaseOffset, info.EndPositive);
|
||||
startIndex = DrawQueue.RelativeElement(info.BaseOffset, info.EndNegative);
|
||||
}
|
||||
else
|
||||
{
|
||||
lastIndex = DrawQueue.RelativeElement(info.BaseOffset, info.StartPositive);
|
||||
startIndex = DrawQueue.RelativeElement(info.BaseOffset, info.StartNegative);
|
||||
}
|
||||
|
||||
for (int i = 0; i < resolution; i++)
|
||||
{
|
||||
float angle = (float) (i + 1) / (resolution + 1) * MathF.PI;
|
||||
float cos = MathF.Cos(angle);
|
||||
float sin = MathF.Sin(angle);
|
||||
|
||||
QuikVec2 displacement;
|
||||
if (endCap)
|
||||
{
|
||||
displacement = new QuikVec2()
|
||||
{
|
||||
X = normal.X * cos + normal.Y * sin,
|
||||
Y = -normal.X * sin + normal.Y * cos
|
||||
} * radius;
|
||||
}
|
||||
else
|
||||
{
|
||||
displacement = new QuikVec2()
|
||||
{
|
||||
X = normal.X * cos - normal.Y * sin,
|
||||
Y = normal.X * sin + normal.Y * cos
|
||||
} * radius;
|
||||
}
|
||||
|
||||
vertex.Position = center + displacement;
|
||||
|
||||
DrawQueue.AddVertex(vertex);
|
||||
DrawQueue.AddElement(startIndex);
|
||||
DrawQueue.AddElement(lastIndex);
|
||||
DrawQueue.AddElement(i);
|
||||
|
||||
lastIndex = i;
|
||||
}
|
||||
}
|
||||
|
||||
private readonly List<QuikBezier> BezierList = new List<QuikBezier>();
|
||||
private void BezierProc(CommandQueue queue)
|
||||
{
|
||||
Frame a = queue.Dequeue();
|
||||
Frame b;
|
||||
|
||||
// Clear temporary vector list and retreive all bezier segments.
|
||||
BezierList.Clear();
|
||||
if (a.Type == FrameType.IVec1)
|
||||
{
|
||||
int count = (int)a;
|
||||
for (int i = 0; i < count; i++)
|
||||
{
|
||||
a = queue.Dequeue();
|
||||
b = queue.Dequeue();
|
||||
|
||||
BezierList.Add(
|
||||
new QuikBezier(
|
||||
new QuikVec2(a.GetF(0), a.GetF(1)),
|
||||
new QuikVec2(b.GetF(0), b.GetF(1)),
|
||||
new QuikVec2(b.GetF(2), b.GetF(3)),
|
||||
new QuikVec2(a.GetF(2), a.GetF(3))
|
||||
)
|
||||
);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
b = queue.Dequeue();
|
||||
|
||||
BezierList.Add(
|
||||
new QuikBezier(
|
||||
new QuikVec2(a.GetF(0), a.GetF(1)),
|
||||
new QuikVec2(b.GetF(0), b.GetF(1)),
|
||||
new QuikVec2(b.GetF(2), b.GetF(3)),
|
||||
new QuikVec2(a.GetF(2), a.GetF(3))
|
||||
)
|
||||
);
|
||||
}
|
||||
|
||||
float width = Style.StrokeWidth ?? 1;
|
||||
|
||||
DrawQueue.StartDrawCall(Viewport);
|
||||
LineInfo prevBase, nextBase = default;
|
||||
for (int i = 0; i < LineList.Count; i++)
|
||||
{
|
||||
QuikBezier bezier = BezierList[i];
|
||||
// A line segment needs a start cap if it is the first segment in
|
||||
// the list, or the last end point is not the current start point.
|
||||
bool isStart = (i == 0 || bezier.Start != BezierList[i - 1].End);
|
||||
// A line segment needs an end cap if it is the last line segment
|
||||
// in the list or if the next start point is not the current end point.
|
||||
bool isEnd = (i == LineList.Count - 1 || bezier.End != BezierList[i+1].Start);
|
||||
|
||||
// Generate the main line segment.
|
||||
prevBase = nextBase;
|
||||
nextBase = GenerateBezierSegment(bezier);
|
||||
|
||||
if (isStart)
|
||||
{
|
||||
// Then a start cap if necessary.
|
||||
GenerateCap(bezier.Start, bezier.GetBezierNormal(0), prevBase, false);
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise generate the required joint.
|
||||
GenerateJoint(bezier.Start, BezierList[i-1].GetBezierNormal(1), bezier.GetBezierNormal(0), prevBase, nextBase);
|
||||
}
|
||||
if (isEnd)
|
||||
{
|
||||
// Then generate the end cap if necessary.
|
||||
GenerateCap(bezier.End, bezier.GetBezierNormal(1), nextBase, true);
|
||||
}
|
||||
}
|
||||
DrawQueue.EndDrawCall();
|
||||
}
|
||||
|
||||
private LineInfo GenerateBezierSegment(in QuikBezier bezier)
|
||||
{
|
||||
QuikVec2 startTangent = bezier.GetBezierTangent(0);
|
||||
QuikVec2 endTangent = bezier.GetBezierTangent(1);
|
||||
QuikVec2 startNormal = new QuikVec2(-startTangent.Y, startTangent.X).Normalize();
|
||||
QuikVec2 endNormal = new QuikVec2(-endTangent.Y, endTangent.X).Normalize();
|
||||
|
||||
float width = Style.StrokeWidth ?? 1;
|
||||
float radius = 0.5f * width;
|
||||
int resolution = GetRoundingResolution(radius, bezier.RasterizationArc);
|
||||
|
||||
DrawQueue.RestoreOffset();
|
||||
QuikVertex v = StrokeVertex;
|
||||
int vbase = DrawQueue.BaseOffset;
|
||||
int index = 2;
|
||||
|
||||
v.Position = bezier.Start + radius * startNormal;
|
||||
DrawQueue.AddVertex(v);
|
||||
v.Position = bezier.Start - radius * startNormal;
|
||||
DrawQueue.AddVertex(v);
|
||||
|
||||
for (int i = 0; i < resolution; i++, index += 2)
|
||||
{
|
||||
float t = (i + 1.0f) / resolution;
|
||||
QuikVec2 at = bezier.GetBezierTangent(t).Normalize();
|
||||
QuikVec2 a = bezier.GetBezierPoint(t);
|
||||
QuikVec2 an = radius * new QuikVec2(-at.Y, at.X);
|
||||
|
||||
v.Position = a + an;
|
||||
DrawQueue.AddVertex(v);
|
||||
v.Position = a - an;
|
||||
DrawQueue.AddVertex(v);
|
||||
|
||||
DrawQueue.AddElement(index - 2); DrawQueue.AddElement(index - 1); DrawQueue.AddElement(index + 0);
|
||||
DrawQueue.AddElement(index - 1); DrawQueue.AddElement(index + 1); DrawQueue.AddElement(index + 0);
|
||||
}
|
||||
|
||||
return new LineInfo(vbase, 0, 1, index - 2, index - 1);
|
||||
}
|
||||
|
||||
private struct LineInfo
|
||||
{
|
||||
public int BaseOffset { get; }
|
||||
public int StartPositive { get; }
|
||||
public int StartNegative { get; }
|
||||
public int EndPositive { get; }
|
||||
public int EndNegative { get; }
|
||||
|
||||
public LineInfo(int baseOffset, int startPositive, int startNegative, int endPositive, int endNegative)
|
||||
{
|
||||
BaseOffset = baseOffset;
|
||||
StartPositive = startPositive;
|
||||
StartNegative = startNegative;
|
||||
EndPositive = endPositive;
|
||||
EndNegative = endNegative;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user