Implement bezier lines.
This commit is contained in:
parent
502eb95278
commit
16bb3f41a7
@ -1,5 +1,4 @@
|
||||
using System;
|
||||
using Quik.VertexGenerator;
|
||||
|
||||
namespace Quik
|
||||
{
|
||||
@ -153,6 +152,38 @@ namespace Quik
|
||||
/// </summary>
|
||||
public QuikVec2 End;
|
||||
|
||||
/// <summary>
|
||||
/// An approximation of the arc length of the bezier curve, for calculating rasterization resolution.
|
||||
/// </summary>
|
||||
public float RasterizationArc =>
|
||||
0.5f * (End - Start).Magnitude +
|
||||
0.5f * ((ControlA - Start).Magnitude + (ControlB - ControlA).Magnitude + (End - ControlB).Magnitude);
|
||||
|
||||
public QuikBezier(QuikVec2 start, QuikVec2 controlA, QuikVec2 controlB, QuikVec2 end)
|
||||
{
|
||||
Start = start;
|
||||
ControlA = controlA;
|
||||
ControlB = controlB;
|
||||
End = end;
|
||||
}
|
||||
|
||||
public QuikBezier(
|
||||
float startX,
|
||||
float startY,
|
||||
float controlAx,
|
||||
float controlAy,
|
||||
float controlBx,
|
||||
float controlBy,
|
||||
float endX,
|
||||
float endY)
|
||||
: this(
|
||||
new QuikVec2(startX, startY),
|
||||
new QuikVec2(controlAx, controlAy),
|
||||
new QuikVec2(controlBx, controlBy),
|
||||
new QuikVec2(endX, endY))
|
||||
{
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Get a point in the curve segment.
|
||||
/// </summary>
|
||||
@ -160,11 +191,12 @@ namespace Quik
|
||||
/// <returns>The point on the curve.</returns>
|
||||
public QuikVec2 GetBezierPoint(float t)
|
||||
{
|
||||
float T = 1 - t;
|
||||
return
|
||||
(1 - t) * (1 - t) * (1 - t) * Start +
|
||||
(1 - t) * (1 - t) * t * ControlA +
|
||||
(1 - t) * t * t * ControlB +
|
||||
t * t * t * End;
|
||||
T * T * T * Start +
|
||||
3 * T * T * t * ControlA +
|
||||
3 * T * t * t * ControlB +
|
||||
t * t * t * End;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@ -174,10 +206,11 @@ namespace Quik
|
||||
/// <returns>The tangent curve.</returns>
|
||||
public QuikVec2 GetBezierTangent(float t)
|
||||
{
|
||||
float T = 1 - t;
|
||||
return
|
||||
3 * (1 - t) * (1 - t) * (ControlA - Start) +
|
||||
6 * (1 - t) * (ControlB - ControlA) +
|
||||
3 * t * t * (End - ControlB);
|
||||
3 * T * T * (ControlA - Start) +
|
||||
6 * T * t * (ControlB - ControlA) +
|
||||
3 * t * t * (End - ControlB);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -65,7 +65,7 @@ namespace Quik.VertexGenerator
|
||||
/// </summary>
|
||||
public int ElementCount => _elementBufferPointer;
|
||||
|
||||
public float CurveGranularity { get; set; } = 0.5f;
|
||||
public float CurveGranularity { get; set; } = 0.2f;
|
||||
|
||||
public QuikContext Context { get; }
|
||||
|
||||
@ -186,6 +186,9 @@ namespace Quik.VertexGenerator
|
||||
case QuikCommandType.Lines:
|
||||
RenderLine(ref call, command as QuikCommandLines);
|
||||
return call;
|
||||
case QuikCommandType.Bezier:
|
||||
RenderBezier(ref call, command as QuikCommandBezier);
|
||||
return call;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
@ -502,7 +505,8 @@ namespace Quik.VertexGenerator
|
||||
}
|
||||
else
|
||||
{
|
||||
// There is no need to generate anything because the lines are the same???
|
||||
// There is a cusp. Generate an end cap.
|
||||
GenerateEndCap(baseVertex, focus, prevNormal.Normalize(), width, GetRoundingResolution(width, MathF.PI), prevPositiveEndIndex, prevNegativeEndIndex);
|
||||
}
|
||||
}
|
||||
|
||||
@ -595,6 +599,134 @@ namespace Quik.VertexGenerator
|
||||
call.Offset = (short) (startOffset * 2);
|
||||
call.Count = (short) (_elementBufferPointer - startOffset);
|
||||
}
|
||||
|
||||
private void RenderBezier(ref QuikDrawCall call, QuikCommandBezier bezier)
|
||||
{
|
||||
QuikStrokeStyle style = bezier.Style ?? Context.DefaultStroke;
|
||||
QuikVertex baseVertex = new QuikVertex() { Color = style.Color };
|
||||
bool isStart;
|
||||
bool isEnd;
|
||||
short startOffset = (short)_elementBufferPointer;
|
||||
int capResolution = GetRoundingResolution(style.Width, MathF.PI);
|
||||
|
||||
short lastEndPositive = 0;
|
||||
short lastEndNegative = 0;
|
||||
|
||||
for (int i = 0; i < bezier.Segments.Length; i++)
|
||||
{
|
||||
QuikBezier segment = bezier.Segments[i];
|
||||
|
||||
isStart = i == 0 || segment.Start != bezier.Segments[i - 1].End;
|
||||
isEnd = i == bezier.Segments.Length - 1 || segment.End != bezier.Segments[i + 1].Start;
|
||||
|
||||
GenerateBezierSegment(
|
||||
baseVertex,
|
||||
segment,
|
||||
style.Width,
|
||||
out short startPositive,
|
||||
out short startNegative,
|
||||
out short endPositive,
|
||||
out short endNegative,
|
||||
out QuikVec2 startNormal,
|
||||
out QuikVec2 endNormal);
|
||||
|
||||
if (isStart)
|
||||
{
|
||||
GenerateStartCap(baseVertex, segment.Start, startNormal, style.Width, capResolution,
|
||||
startPositive, startNegative);
|
||||
}
|
||||
else
|
||||
{
|
||||
GenerateJoint(
|
||||
baseVertex,
|
||||
segment.Start,
|
||||
bezier.Segments[i-1].GetBezierTangent(1),
|
||||
segment.GetBezierTangent(0),
|
||||
style.Width,
|
||||
lastEndPositive,
|
||||
lastEndNegative,
|
||||
startPositive,
|
||||
startNegative
|
||||
);
|
||||
}
|
||||
|
||||
if (isEnd)
|
||||
{
|
||||
GenerateEndCap(
|
||||
baseVertex,
|
||||
segment.End,
|
||||
endNormal,
|
||||
style.Width,
|
||||
capResolution,
|
||||
endPositive,
|
||||
endNegative
|
||||
);
|
||||
}
|
||||
|
||||
lastEndPositive = endPositive;
|
||||
lastEndNegative = endNegative;
|
||||
}
|
||||
|
||||
call.Offset = (short) (startOffset * 2);
|
||||
call.Count = (short) (_elementBufferPointer - startOffset);
|
||||
}
|
||||
|
||||
private void GenerateBezierSegment(
|
||||
QuikVertex baseVertex,
|
||||
QuikBezier bezier,
|
||||
float width,
|
||||
out short startPositiveIndex,
|
||||
out short startNegativeIndex,
|
||||
out short endPositiveIndex,
|
||||
out short endNegativeIndex,
|
||||
out QuikVec2 startNormal,
|
||||
out QuikVec2 endNormal)
|
||||
{
|
||||
QuikVec2 startTangent = bezier.GetBezierTangent(0);
|
||||
QuikVec2 endTangent = bezier.GetBezierTangent(1);
|
||||
|
||||
startNormal = new QuikVec2(-startTangent.Y, startTangent.X).Normalize();
|
||||
endNormal = new QuikVec2(-endTangent.Y, endTangent.X).Normalize();
|
||||
|
||||
int resolution = GetRoundingResolution(width, bezier.RasterizationArc);
|
||||
|
||||
startPositiveIndex = (short)_vertexBufferPointer;
|
||||
startNegativeIndex = (short) (startPositiveIndex + 1);
|
||||
|
||||
QuikVertex startPositive = baseVertex;
|
||||
QuikVertex startNegative = baseVertex;
|
||||
startPositive.Position = bezier.Start + 0.5f * width * startNormal;
|
||||
startNegative.Position = bezier.Start - 0.5f * width * startNormal;
|
||||
|
||||
AddVertex(startPositive, startNegative);
|
||||
|
||||
for (int i = 0; i < resolution; i++)
|
||||
{
|
||||
float t = (float) (i + 1) / resolution;
|
||||
|
||||
QuikVec2 at = bezier.GetBezierTangent(t).Normalize();
|
||||
|
||||
QuikVec2 a = bezier.GetBezierPoint(t);
|
||||
QuikVec2 an = 0.5f * width * new QuikVec2(-at.Y, at.X);
|
||||
short index = (short) _vertexBufferPointer;
|
||||
|
||||
QuikVertex apv = baseVertex, anv = baseVertex;
|
||||
apv.Position = a + an;
|
||||
anv.Position = a - an;
|
||||
|
||||
AddVertex(apv, anv);
|
||||
AddElement(
|
||||
(short)(index - 2),
|
||||
(short)(index - 1),
|
||||
(short)(index + 0),
|
||||
(short)(index - 1),
|
||||
(short)(index + 1),
|
||||
(short)(index + 0));
|
||||
}
|
||||
|
||||
endNegativeIndex = (short) (_vertexBufferPointer - 1);
|
||||
endPositiveIndex = (short) (_vertexBufferPointer - 2);
|
||||
}
|
||||
}
|
||||
|
||||
public enum QuikRenderTarget
|
||||
|
@ -155,6 +155,11 @@ void main()
|
||||
new QuikLine(64, 30, 64, 10),
|
||||
new QuikLine(64, 10, 74, 10)
|
||||
);
|
||||
|
||||
context.Draw.Bezier(
|
||||
new QuikBezier(46, 10, 56, 10, 46, 30, 56, 30),
|
||||
new QuikBezier(56, 30, 46, 30, 56, 60, 46, 60),
|
||||
new QuikBezier(46, 60, 56, 60, 46, 90, 56, 90));
|
||||
|
||||
QuikCommand command;
|
||||
while (context.Draw.Commands.TryDequeue(out command))
|
||||
|
Loading…
Reference in New Issue
Block a user