Dashboard/Dashboard/VertexGenerator/VertexCommandEngine.cs
H. Utku Maden a1f4e6a4dc Rename from QUIK to Dashboard
Due to unforseen naming conflicts, the project has been rebranded under the ReFuel
umbrealla and will now be referred to as Dashboard from now on. Other changes will
occur to suit the library more for the engine whilst keeping the freestanding
nature of the library.

Rename folder.

Rename to Dashboard.OpenTK

Rename to Dashboard.Media.Defaults.

Do the last renames and path fixes.
2024-07-17 23:26:58 +03:00

1131 lines
45 KiB
C#

using System;
using System.Collections.Generic;
using Dashboard.CommandMachine;
using Dashboard.Media;
using Dashboard.Typography;
namespace Dashboard.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 bool BlendTextures =>
(Style["-vertex-blend-textures"] is bool value) ? value : false;
protected QuikVertex StrokeVertex => new QuikVertex()
{
ZIndex = Style.ZIndex ?? this.ZIndex,
Color = Style.StrokeColor ?? QColor.Black,
};
protected QuikVertex FillVertex => new QuikVertex()
{
ZIndex = Style.ZIndex ?? this.ZIndex,
Color = Style.Color ?? QColor.White,
};
protected QuikVertex ImageVertex => new QuikVertex()
{
ZIndex = Style.ZIndex ?? this.ZIndex,
Color = BlendTextures ? (Style.Color ?? QColor.White) : QColor.White,
};
public override void Reset()
{
base.Reset();
DrawQueue.Clear();
}
protected override void ChildProcessCommand(Command name, CommandQueue queue)
{
base.ChildProcessCommand(name, queue);
switch(name)
{
case Command.Line: LineProc(queue); break;
case Command.Bezier: BezierProc(queue); break;
case Command.Rectangle: RectangleProc(queue); break;
case Command.Image: ImageProc(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<QLine> LineList = new List<QLine>();
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((QLine)frame);
}
}
else
{
LineList.Add((QLine)frame);
}
float width = Style.StrokeWidth ?? 1;
DrawQueue.StartDrawCall(Viewport);
LineInfo prevBase, nextBase = default;
for (int i = 0; i < LineList.Count; i++)
{
QLine 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 QLine line)
{
QuikVertex vertex = StrokeVertex;
QuikVertex a, b, c, d;
QVec2 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 QVec2 center,
in QVec2 prevNormal,
in QVec2 nextNormal,
in LineInfo prevInfo,
in LineInfo nextInfo)
{
// Figure out which side needs the joint.
QVec2 meanNormal = 0.5f * (prevNormal + nextNormal);
QVec2 meanTangent = new QVec2(meanNormal.Y, -meanNormal.X);
QVec2 positiveEdge = ((center + nextNormal) - (center + prevNormal)).Normalize();
QVec2 negativeEdge = ((center - nextNormal) - (center - prevNormal)).Normalize();
float positive, negative;
positive = QVec2.Dot(meanTangent, positiveEdge);
negative = QVec2.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(QVec2.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);
QVec2 displacement;
if (isNegative)
{
displacement = new QVec2()
{
X = -prevNormal.X * cos + prevNormal.Y * sin,
Y = -prevNormal.X * sin - prevNormal.Y * cos
} * radius;
}
else
{
displacement = new QVec2()
{
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 QVec2 center,
in QVec2 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);
QVec2 displacement;
if (endCap)
{
displacement = new QVec2()
{
X = normal.X * cos + normal.Y * sin,
Y = -normal.X * sin + normal.Y * cos
} * radius;
}
else
{
displacement = new QVec2()
{
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<QBezier> BezierList = new List<QBezier>();
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 QBezier(
new QVec2(a.GetF(0), a.GetF(1)),
new QVec2(b.GetF(0), b.GetF(1)),
new QVec2(b.GetF(2), b.GetF(3)),
new QVec2(a.GetF(2), a.GetF(3))
)
);
}
}
else
{
b = queue.Dequeue();
BezierList.Add(
new QBezier(
new QVec2(a.GetF(0), a.GetF(1)),
new QVec2(b.GetF(0), b.GetF(1)),
new QVec2(b.GetF(2), b.GetF(3)),
new QVec2(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++)
{
QBezier 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 QBezier bezier)
{
QVec2 startTangent = bezier.GetBezierTangent(0);
QVec2 endTangent = bezier.GetBezierTangent(1);
QVec2 startNormal = new QVec2(-startTangent.Y, startTangent.X).Normalize();
QVec2 endNormal = new QVec2(-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;
QVec2 at = bezier.GetBezierTangent(t).Normalize();
QVec2 a = bezier.GetBezierPoint(t);
QVec2 an = radius * new QVec2(-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 readonly List<QRectangle> RectangleList = new List<QRectangle>();
private void RectangleProc(CommandQueue queue)
{
Frame frame = queue.Dequeue();
RectangleList.Clear();
if (frame.Type == FrameType.IVec1)
{
int count = (int)frame;
for (int i = 0; i < count; i++)
{
frame = queue.Dequeue();
RectangleList.Add((QRectangle)frame);
}
}
else
{
RectangleList.Add((QRectangle)frame);
}
float stroke = Style.StrokeWidth ?? 1.0f;
float radius = (float?)Style["radius"] ?? 0.0f; // TODO: not this.
DrawQueue.StartDrawCall(Viewport);
for (int i = 0; i < RectangleList.Count; i++)
{
QRectangle outer = RectangleList[i];
QRectangle inner = new QRectangle(
outer.Right - stroke, outer.Bottom - stroke,
outer.Left + stroke, outer.Top + stroke);
GenerateRectangleBase(inner, Math.Max(radius - stroke, 0.0f));
if (stroke == 0.0f)
continue;
if (radius == 0.0f)
{
GenerateRectangleStripStraight(outer);
}
else if (radius < stroke)
{
GenerateRectangleStripNarrow(outer, radius);
}
else
{
GenerateRectangleStripWide(outer, radius);
}
}
DrawQueue.EndDrawCall();
}
private void GenerateRectangleBase(in QRectangle rectangle, float radius)
{
/*
+--j-------i--+
|NW| N |NE|
k--d-------c--h
| | | |
|W | C | E|
| | | |
l--a-------b--g
|SW| S |SE|
+--e-------f--+
a b c d e f g h i j k l
0 1 2 3 4 5 6 7 8 9 10 11
*/
DrawQueue.RestoreOffset();
// Draw center rectangle.
QVec2 aPos, bPos, cPos, dPos;
QuikVertex v = FillVertex;
aPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
bPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
cPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Top + radius);
DrawQueue.AddVertex(v);
dPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Top + radius);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(0); DrawQueue.AddElement(1); DrawQueue.AddElement(2);
DrawQueue.AddElement(0); DrawQueue.AddElement(2); DrawQueue.AddElement(3);
if (radius == 0.0f)
return;
// Draw south rectangle.
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(4); DrawQueue.AddElement(5); DrawQueue.AddElement(1);
DrawQueue.AddElement(4); DrawQueue.AddElement(1); DrawQueue.AddElement(0);
// Draw east rectangle.
v.Position = new QVec2(rectangle.Right, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right, rectangle.Top + radius);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(1); DrawQueue.AddElement(6); DrawQueue.AddElement(7);
DrawQueue.AddElement(1); DrawQueue.AddElement(7); DrawQueue.AddElement(3);
// Draw north rectangle.
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(3); DrawQueue.AddElement(2); DrawQueue.AddElement(8);
DrawQueue.AddElement(3); DrawQueue.AddElement(8); DrawQueue.AddElement(0);
// Draw west rectangle.
v.Position = new QVec2(rectangle.Left, rectangle.Top + radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(11); DrawQueue.AddElement(0); DrawQueue.AddElement(3);
DrawQueue.AddElement(11); DrawQueue.AddElement(3); DrawQueue.AddElement(10);
// Draw north east corner.
int resolution = GetRoundingResolution(radius, 0.5f * MathF.PI);
int previous = 7, current = 12;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = MathF.Cos(theta) * radius;
float yoff = MathF.Sin(theta) * radius;
v.Position = cPos + new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(2); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
}
DrawQueue.AddElement(2); DrawQueue.AddElement(previous); DrawQueue.AddElement(8);
// Draw the north west corner.
previous = 9;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = -MathF.Sin(theta) * radius;
float yoff = MathF.Cos(theta) * radius;
v.Position = dPos + new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(3); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
}
DrawQueue.AddElement(3); DrawQueue.AddElement(previous); DrawQueue.AddElement(10);
// Draw south west corner.
previous = 11;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = -MathF.Cos(theta) * radius;
float yoff = -MathF.Sin(theta) * radius;
v.Position = aPos + new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(0); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
}
DrawQueue.AddElement(0); DrawQueue.AddElement(previous); DrawQueue.AddElement(4);
// Draw the south east corner.
previous = 5;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = -MathF.Sin(theta) * radius;
float yoff = MathF.Cos(theta) * radius;
v.Position = bPos + new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(1); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
}
DrawQueue.AddElement(1); DrawQueue.AddElement(previous); DrawQueue.AddElement(6);
}
private void GenerateRectangleStripStraight(in QRectangle rectangle)
{
/*
h---------g
|\ N /|
| \ / |
| d---c |
|W | | E|
| a---b |
| / \ |
|/ S \|
e---------f
a b c d e f g h
0 1 2 3 4 5 6 7
*/
QuikVertex v = StrokeVertex;
float stroke = Style.StrokeWidth ?? 1.0f;
DrawQueue.RestoreOffset();
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom - stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom - stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top + stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top + stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left, rectangle.Bottom);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right, rectangle.Bottom);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right, rectangle.Top);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left, rectangle.Top);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(4); DrawQueue.AddElement(5); DrawQueue.AddElement(1); // SSW
DrawQueue.AddElement(4); DrawQueue.AddElement(1); DrawQueue.AddElement(0); // SSE
DrawQueue.AddElement(1); DrawQueue.AddElement(5); DrawQueue.AddElement(6); // SEE
DrawQueue.AddElement(1); DrawQueue.AddElement(6); DrawQueue.AddElement(2); // NEE
DrawQueue.AddElement(3); DrawQueue.AddElement(2); DrawQueue.AddElement(6); // NNE
DrawQueue.AddElement(3); DrawQueue.AddElement(6); DrawQueue.AddElement(7); // NNW
DrawQueue.AddElement(4); DrawQueue.AddElement(0); DrawQueue.AddElement(3); // NWW
DrawQueue.AddElement(4); DrawQueue.AddElement(3); DrawQueue.AddElement(7); // SWW
}
private void GenerateRectangleStripNarrow(in QRectangle rectangle, float radius)
{
/*
v-j---i-u
| | | |
x-w | N | t-s
| \| |/ |
k---d---c---h
| | | |
| W | | E |
| | | |
l---a---b---g
| /| |\ |
m-n | S | q-r
| | | |
o-e---f-p
a b c d e f g h i j
00: 0 1 2 3 4 5 6 7 8 9
k l m n o p q r s t
10: 0 1 2 3 4 5 6 7 8 9
u v w x
20: 0 1 2 3
*/
QuikVertex v = StrokeVertex;
QVec2 nPos, qPos, tPos, wPos;
float stroke = Style.StrokeWidth ?? 1.0f;
DrawQueue.RestoreOffset();
// a-b-c-d
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom - stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom - stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top + stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top + stroke);
DrawQueue.AddVertex(v);
// ef-gh-ij-kl
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Bottom);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right, rectangle.Bottom - stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right, rectangle.Top + stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Top);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left, rectangle.Top + stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left, rectangle.Bottom - stroke);
DrawQueue.AddVertex(v);
// mno
v.Position = new QVec2(rectangle.Left, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
nPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom);
DrawQueue.AddVertex(v);
// pqr
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
DrawQueue.AddVertex(v);
qPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
// stu
v.Position = new QVec2(rectangle.Right, rectangle.Top + radius);
DrawQueue.AddVertex(v);
tPos = v.Position = new QVec2(rectangle.Right - radius, rectangle.Top + radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
DrawQueue.AddVertex(v);
// vwx
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top);
DrawQueue.AddVertex(v);
wPos = v.Position = new QVec2(rectangle.Left + radius, rectangle.Top + radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left, rectangle.Top + radius);
DrawQueue.AddVertex(v);
// E
DrawQueue.AddElement(1); DrawQueue.AddElement(6); DrawQueue.AddElement(7);
DrawQueue.AddElement(1); DrawQueue.AddElement(7); DrawQueue.AddElement(2);
// N
DrawQueue.AddElement(3); DrawQueue.AddElement(2); DrawQueue.AddElement(8);
DrawQueue.AddElement(3); DrawQueue.AddElement(8); DrawQueue.AddElement(9);
// W
DrawQueue.AddElement(11); DrawQueue.AddElement(0); DrawQueue.AddElement(3);
DrawQueue.AddElement(11); DrawQueue.AddElement(3); DrawQueue.AddElement(10);
// S
DrawQueue.AddElement(4); DrawQueue.AddElement(5); DrawQueue.AddElement(1);
DrawQueue.AddElement(4); DrawQueue.AddElement(1); DrawQueue.AddElement(0);
// NEE
DrawQueue.AddElement(2); DrawQueue.AddElement(7); DrawQueue.AddElement(18);
DrawQueue.AddElement(2); DrawQueue.AddElement(18); DrawQueue.AddElement(19);
// NNE
DrawQueue.AddElement(2); DrawQueue.AddElement(19); DrawQueue.AddElement(20);
DrawQueue.AddElement(2); DrawQueue.AddElement(20); DrawQueue.AddElement(8);
// NNW
DrawQueue.AddElement(22); DrawQueue.AddElement(3); DrawQueue.AddElement(19);
DrawQueue.AddElement(22); DrawQueue.AddElement(19); DrawQueue.AddElement(21);
// NWW
DrawQueue.AddElement(10); DrawQueue.AddElement(3); DrawQueue.AddElement(22);
DrawQueue.AddElement(10); DrawQueue.AddElement(22); DrawQueue.AddElement(23);
// SWW
DrawQueue.AddElement(12); DrawQueue.AddElement(13); DrawQueue.AddElement(0);
DrawQueue.AddElement(12); DrawQueue.AddElement(0); DrawQueue.AddElement(11);
// SSW
DrawQueue.AddElement(14); DrawQueue.AddElement(4); DrawQueue.AddElement(0);
DrawQueue.AddElement(14); DrawQueue.AddElement(0); DrawQueue.AddElement(13);
// NE
int resolution = GetRoundingResolution(radius, 0.5f * MathF.PI);
int previous = 18, current = 24;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = MathF.Cos(theta) * radius;
float yoff = MathF.Sin(theta) * radius;
v.Position = tPos + new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(19); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
}
DrawQueue.AddElement(19); DrawQueue.AddElement(previous); DrawQueue.AddElement(20);
// NW
previous = 21;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = -MathF.Sin(theta) * radius;
float yoff = MathF.Cos(theta) * radius;
v.Position = wPos + new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(22); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
}
DrawQueue.AddElement(22); DrawQueue.AddElement(previous); DrawQueue.AddElement(23);
// SW
previous = 12;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = -MathF.Cos(theta) * radius;
float yoff = -MathF.Sin(theta) * radius;
v.Position = nPos + new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(23); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
}
DrawQueue.AddElement(23); DrawQueue.AddElement(previous); DrawQueue.AddElement(14);
// SE
previous = 15;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = -MathF.Sin(theta) * radius;
float yoff = MathF.Cos(theta) * radius;
v.Position = qPos + new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(16); DrawQueue.AddElement(previous); DrawQueue.AddElement((previous = current++));
}
DrawQueue.AddElement(16); DrawQueue.AddElement(previous); DrawQueue.AddElement(17);
}
private void GenerateRectangleStripWide(in QRectangle rectangle, float radius)
{
/*
l---k
| N |
i---j
p---o h---g
| W | | E |
m---n e---f
d---c
| S |
a---b
a b c d e f g h i j
00: 0 1 2 3 4 5 6 7 8 9
k l m n o p q r s t
10: 0 1 2 3 4 5
*/
QuikVertex v = StrokeVertex;
float stroke = Style.StrokeWidth ?? 1.0f;
float innerRadius = radius - stroke;
DrawQueue.RestoreOffset();
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - radius, rectangle.Bottom - stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + radius, rectangle.Bottom - stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right, rectangle.Top + radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right, rectangle.Top + radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - stroke, rectangle.Bottom - radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top + stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top + stroke);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Right - radius, rectangle.Top);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + radius, rectangle.Top);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top + radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left + stroke, rectangle.Top + radius);
DrawQueue.AddVertex(v);
v.Position = new QVec2(rectangle.Left, rectangle.Bottom + radius);
DrawQueue.AddVertex(v);
// S
DrawQueue.AddElement(0); DrawQueue.AddElement(1); DrawQueue.AddElement(2);
DrawQueue.AddElement(0); DrawQueue.AddElement(2); DrawQueue.AddElement(3);
// E
DrawQueue.AddElement(4); DrawQueue.AddElement(5); DrawQueue.AddElement(6);
DrawQueue.AddElement(4); DrawQueue.AddElement(6); DrawQueue.AddElement(7);
// N
DrawQueue.AddElement(8); DrawQueue.AddElement(9); DrawQueue.AddElement(10);
DrawQueue.AddElement(8); DrawQueue.AddElement(10); DrawQueue.AddElement(11);
// W
DrawQueue.AddElement(12); DrawQueue.AddElement(13); DrawQueue.AddElement(14);
DrawQueue.AddElement(12); DrawQueue.AddElement(14); DrawQueue.AddElement(15);
// Draw NE arc.
int resolution = GetRoundingResolution(radius, 0.5f * MathF.PI);
int current = 16;
QVec2 center = new QVec2(rectangle.Right - radius, rectangle.Top - radius);
int s1 = 7, s2 = 6;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = MathF.Cos(theta);
float yoff = MathF.Sin(theta);
v.Position = center + radius * new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
v.Position = center + innerRadius * new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 1);
s1 = current; s2 = current + 1;
current += 2;
}
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(10);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(9);
// Draw NW arc
center = new QVec2(rectangle.Left + radius, rectangle.Top - radius);
s1 = 8; s2 = 11;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = -MathF.Sin(theta);
float yoff = MathF.Cos(theta);
v.Position = center + radius * new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
v.Position = center + innerRadius * new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 1);
s1 = current; s2 = current + 1;
current += 2;
}
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(15);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(14);
// Draw SW arc
center = new QVec2(rectangle.Left + radius, rectangle.Bottom + radius);
s1 = 13; s2 = 12;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = -MathF.Cos(theta);
float yoff = -MathF.Sin(theta);
v.Position = center + radius * new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
v.Position = center + innerRadius * new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 1);
s1 = current; s2 = current + 1;
current += 2;
}
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(0);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(3);
// Draw SW arc
center = new QVec2(rectangle.Right - radius, rectangle.Bottom + radius);
s1 = 2; s2 = 1;
for (int i = 0; i < resolution - 1; i++)
{
float theta = (i + 1.0f) / (resolution + 1);
float xoff = MathF.Sin(theta);
float yoff = -MathF.Cos(theta);
v.Position = center + radius * new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
v.Position = center + innerRadius * new QVec2(xoff, yoff);
DrawQueue.AddVertex(v);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 0);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(current + 1);
s1 = current; s2 = current + 1;
current += 2;
}
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(5);
DrawQueue.AddElement(s1); DrawQueue.AddElement(s2); DrawQueue.AddElement(4);
}
private void ImageProc(CommandQueue queue)
{
Frame frame = queue.Dequeue();
ImageCommandFlags flags = (ImageCommandFlags)frame.I1;
QImage image = queue.Dequeue().As<QImage>();
int count = flags.HasFlag(ImageCommandFlags.Single) ? 1 : frame.I2;
if (flags.HasFlag(ImageCommandFlags.Image3d))
{
Image3d(queue, image, count);
}
else
{
Image2d(queue, image, count, flags.HasFlag(ImageCommandFlags.UVs));
}
}
private void Image2d(CommandQueue queue, QImage image, int count, bool uv)
{
DrawQueue.StartDrawCall(Viewport, image);
for (int i = 0; i < count; i++)
{
QRectangle rect = (QRectangle)queue.Dequeue();
QRectangle uvs;
if (uv)
{
uvs = (QRectangle)queue.Dequeue();
}
else
{
uvs = new QRectangle(1, 1, 0, 0);
}
DrawQueue.RestoreOffset();
QuikVertex vertex = ImageVertex;
vertex.Position = new QVec2(rect.Left, rect.Top);
vertex.TextureCoordinates = new QVec2(uvs.Left, uvs.Top);
DrawQueue.AddVertex(vertex);
vertex.Position = new QVec2(rect.Left, rect.Bottom);
vertex.TextureCoordinates = new QVec2(uvs.Left, uvs.Bottom);
DrawQueue.AddVertex(vertex);
vertex.Position = new QVec2(rect.Right, rect.Bottom);
vertex.TextureCoordinates = new QVec2(uvs.Right, uvs.Bottom);
DrawQueue.AddVertex(vertex);
vertex.Position = new QVec2(rect.Right, rect.Top);
vertex.TextureCoordinates = new QVec2(uvs.Right, uvs.Top);
DrawQueue.AddVertex(vertex);
DrawQueue.AddElement(0); DrawQueue.AddElement(2); DrawQueue.AddElement(3);
DrawQueue.AddElement(0); DrawQueue.AddElement(1); DrawQueue.AddElement(2);
}
DrawQueue.EndDrawCall();
}
private void Image3d(CommandQueue queue, QImage image, int count)
{
DrawQueue.StartDrawCall(Viewport, image);
for (int i = 0; i < count; i++)
{
QRectangle rect = (QRectangle)queue.Dequeue();
QRectangle uvs = (QRectangle)queue.Dequeue();
int layer = (int)queue.Dequeue();
DrawQueue.RestoreOffset();
QuikVertex vertex = ImageVertex;
vertex.TextureLayer = layer;
vertex.Position = new QVec2(rect.Top, rect.Left);
vertex.TextureCoordinates = new QVec2(uvs.Top, uvs.Left);
DrawQueue.AddVertex(vertex);
vertex.Position = new QVec2(rect.Bottom, rect.Left);
vertex.TextureCoordinates = new QVec2(uvs.Bottom, uvs.Left);
DrawQueue.AddVertex(vertex);
vertex.Position = new QVec2(rect.Bottom, rect.Right);
vertex.TextureCoordinates = new QVec2(uvs.Bottom, uvs.Right);
DrawQueue.AddVertex(vertex);
vertex.Position = new QVec2(rect.Top, rect.Right);
vertex.TextureCoordinates = new QVec2(uvs.Top, uvs.Right);
DrawQueue.AddVertex(vertex);
DrawQueue.AddElement(0); DrawQueue.AddElement(2); DrawQueue.AddElement(3);
DrawQueue.AddElement(0); DrawQueue.AddElement(1); DrawQueue.AddElement(2);
}
DrawQueue.EndDrawCall();
}
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;
}
}
}
}