Implement bezier lines.
This commit is contained in:
parent
502eb95278
commit
16bb3f41a7
@ -1,5 +1,4 @@
|
|||||||
using System;
|
using System;
|
||||||
using Quik.VertexGenerator;
|
|
||||||
|
|
||||||
namespace Quik
|
namespace Quik
|
||||||
{
|
{
|
||||||
@ -153,6 +152,38 @@ namespace Quik
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public QuikVec2 End;
|
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>
|
/// <summary>
|
||||||
/// Get a point in the curve segment.
|
/// Get a point in the curve segment.
|
||||||
/// </summary>
|
/// </summary>
|
||||||
@ -160,10 +191,11 @@ namespace Quik
|
|||||||
/// <returns>The point on the curve.</returns>
|
/// <returns>The point on the curve.</returns>
|
||||||
public QuikVec2 GetBezierPoint(float t)
|
public QuikVec2 GetBezierPoint(float t)
|
||||||
{
|
{
|
||||||
|
float T = 1 - t;
|
||||||
return
|
return
|
||||||
(1 - t) * (1 - t) * (1 - t) * Start +
|
T * T * T * Start +
|
||||||
(1 - t) * (1 - t) * t * ControlA +
|
3 * T * T * t * ControlA +
|
||||||
(1 - t) * t * t * ControlB +
|
3 * T * t * t * ControlB +
|
||||||
t * t * t * End;
|
t * t * t * End;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -174,9 +206,10 @@ namespace Quik
|
|||||||
/// <returns>The tangent curve.</returns>
|
/// <returns>The tangent curve.</returns>
|
||||||
public QuikVec2 GetBezierTangent(float t)
|
public QuikVec2 GetBezierTangent(float t)
|
||||||
{
|
{
|
||||||
|
float T = 1 - t;
|
||||||
return
|
return
|
||||||
3 * (1 - t) * (1 - t) * (ControlA - Start) +
|
3 * T * T * (ControlA - Start) +
|
||||||
6 * (1 - t) * (ControlB - ControlA) +
|
6 * T * t * (ControlB - ControlA) +
|
||||||
3 * t * t * (End - ControlB);
|
3 * t * t * (End - ControlB);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -65,7 +65,7 @@ namespace Quik.VertexGenerator
|
|||||||
/// </summary>
|
/// </summary>
|
||||||
public int ElementCount => _elementBufferPointer;
|
public int ElementCount => _elementBufferPointer;
|
||||||
|
|
||||||
public float CurveGranularity { get; set; } = 0.5f;
|
public float CurveGranularity { get; set; } = 0.2f;
|
||||||
|
|
||||||
public QuikContext Context { get; }
|
public QuikContext Context { get; }
|
||||||
|
|
||||||
@ -186,6 +186,9 @@ namespace Quik.VertexGenerator
|
|||||||
case QuikCommandType.Lines:
|
case QuikCommandType.Lines:
|
||||||
RenderLine(ref call, command as QuikCommandLines);
|
RenderLine(ref call, command as QuikCommandLines);
|
||||||
return call;
|
return call;
|
||||||
|
case QuikCommandType.Bezier:
|
||||||
|
RenderBezier(ref call, command as QuikCommandBezier);
|
||||||
|
return call;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
@ -502,7 +505,8 @@ namespace Quik.VertexGenerator
|
|||||||
}
|
}
|
||||||
else
|
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.Offset = (short) (startOffset * 2);
|
||||||
call.Count = (short) (_elementBufferPointer - startOffset);
|
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
|
public enum QuikRenderTarget
|
||||||
|
@ -156,6 +156,11 @@ void main()
|
|||||||
new QuikLine(64, 10, 74, 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;
|
QuikCommand command;
|
||||||
while (context.Draw.Commands.TryDequeue(out command))
|
while (context.Draw.Commands.TryDequeue(out command))
|
||||||
{
|
{
|
||||||
|
Loading…
Reference in New Issue
Block a user