Fix arc segment fuckery when it's near full

This commit is contained in:
Asriel Camora
2023-11-23 20:49:31 -08:00
parent fd80536ec6
commit 1fe8db7da9
+29 -26
View File
@@ -145,16 +145,21 @@ internal static class ImGuiUtils
private static float Lerp(float a, float b, float t) => private static float Lerp(float a, float b, float t) =>
MathF.FusedMultiplyAdd(b - a, t, a); MathF.FusedMultiplyAdd(b - a, t, a);
private static void ArcSegment(Vector2 o, Vector2 prev, Vector2 cur, Vector2? next, float radius, float ratio, uint color) private readonly record struct ArcEdge(float Angle, Vector2 Point)
{
public ArcEdge(float angle) : this(angle, UnitCircle(angle)) { }
}
private static void ArcSegment(Vector2 o, ArcEdge prev, ArcEdge cur, ArcEdge? next, float radius, float ratio, uint color)
{ {
var d = ImGui.GetWindowDrawList(); var d = ImGui.GetWindowDrawList();
d.PathLineTo(o + cur * radius); d.PathLineTo(o + cur.Point * radius);
d.PathLineTo(o + prev * radius); d.PathLineTo(o + prev.Point * radius);
d.PathLineTo(o + prev * radius * ratio); d.PathLineTo(o + prev.Point * radius * ratio);
d.PathLineTo(o + cur * radius * ratio); d.PathLineTo(o + cur.Point * radius * ratio);
if (next is { } nextValue) if (next is { } nextValue)
d.PathLineTo(o + nextValue * radius); d.PathLineTo(o + nextValue.Point * radius);
d.PathFillConvex(color); d.PathFillConvex(color);
} }
@@ -164,24 +169,25 @@ internal static class ImGuiUtils
if (startAngle > endAngle) if (startAngle > endAngle)
(startAngle, endAngle) = (endAngle, startAngle); (startAngle, endAngle) = (endAngle, startAngle);
// Origin of circle
var offset = ImGui.GetCursorScreenPos() + new Vector2(radius); var offset = ImGui.GetCursorScreenPos() + new Vector2(radius);
// Number of segments to draw
var segments = ImGui.GetWindowDrawList()._CalcCircleAutoSegmentCount(radius); var segments = ImGui.GetWindowDrawList()._CalcCircleAutoSegmentCount(radius);
// Angle between each segment
var incrementAngle = MathF.Tau / segments; var incrementAngle = MathF.Tau / segments;
// Whether the arc is a full circle (no background or all background)
var isFullCircle = (endAngle - startAngle) % MathF.Tau == 0; var isFullCircle = (endAngle - startAngle) % MathF.Tau == 0;
var prevA = startAngle; var end = new ArcEdge(endAngle);
var prev = UnitCircle(prevA); var prev = new ArcEdge(startAngle);
for (var i = 1; i <= segments; ++i) for (var i = 1; i <= segments; ++i)
{ {
var a = startAngle + incrementAngle * i; var cur = new ArcEdge(startAngle + incrementAngle * i);
var cur = UnitCircle(a); var next = new ArcEdge(startAngle + incrementAngle * (i + 1));
var nextA = a + incrementAngle;
var next = UnitCircle(nextA);
// full segment is background // full segment is background
if (prevA >= endAngle) if (prev.Angle >= end.Angle)
{ {
// don't overlap with the first segment // don't overlap with the first segment
if (i == segments && !isFullCircle) if (i == segments && !isFullCircle)
@@ -190,29 +196,26 @@ internal static class ImGuiUtils
ArcSegment(offset, prev, cur, next, radius, ratio, backgroundColor); ArcSegment(offset, prev, cur, next, radius, ratio, backgroundColor);
} }
// segment is partially filled // segment is partially filled
else if (a > endAngle && !isFullCircle) else if (cur.Angle > end.Angle && !isFullCircle)
{ {
// we split the drawing in two // we split the drawing in two
var end = UnitCircle(endAngle);
ArcSegment(offset, prev, end, null, radius, ratio, filledColor); ArcSegment(offset, prev, end, null, radius, ratio, filledColor);
if (i == segments)
ArcSegment(offset, end, cur, null, radius, ratio, backgroundColor);
else
ArcSegment(offset, end, cur, next, radius, ratio, backgroundColor); ArcSegment(offset, end, cur, next, radius, ratio, backgroundColor);
// set the previous segment to endAngle // set the previous segment to end
a = endAngle;
cur = end; cur = end;
} }
// full segment is filled // full segment is filled
else else
{ {
// if the next segment will be partially filled, the next segment will be the endAngle // if the next segment will be partially filled, the next segment will be the end
if (nextA > endAngle && !isFullCircle) if (next.Angle > end.Angle && !isFullCircle)
{
var end = UnitCircle(endAngle);
ArcSegment(offset, prev, cur, end, radius, ratio, filledColor); ArcSegment(offset, prev, cur, end, radius, ratio, filledColor);
}
else else
ArcSegment(offset, prev, cur, next, radius, ratio, filledColor); ArcSegment(offset, prev, cur, next, radius, ratio, filledColor);
} }
prevA = a;
prev = cur; prev = cur;
} }
@@ -220,9 +223,9 @@ internal static class ImGuiUtils
ImGui.Dummy(new Vector2(radius * 2)); ImGui.Dummy(new Vector2(radius * 2));
} }
public static void ArcProgress(float value, float radiusInner, float radiusOuter, uint backgroundColor, uint filledColor) public static void ArcProgress(float value, float radius, float ratio, uint backgroundColor, uint filledColor)
{ {
Arc(MathF.PI / 2, MathF.PI / 2 - MathF.Tau * Math.Clamp(value, 0, 1), radiusInner, radiusOuter, backgroundColor, filledColor); Arc(MathF.PI / 2, MathF.PI / 2 - MathF.Tau * Math.Clamp(value, 0, 1), radius, ratio, backgroundColor, filledColor);
} }
public sealed class ViolinData public sealed class ViolinData