I've been working on filling in some of the missing tools in my draw library and one thing I noticed that I was missing was a really good filled arc routine. QB64's method was to draw a circle, choose a point inside the arc, and then use PAINT to fill that circle....
and it didn't work as it never seemed to properly close the arcs and then the paint bled out and colored the entire screen.
The CircleFill that I shared and everyone has used for ages and ages works off LINE statements and thus can never bleed outside the circle itself. It's fast and lovely and nice.
And so, I thought, can I come up with a similar way to do ARCs?
After a few different tries, this is what I've came up with -- a scanline polygon fill routine for arcs.
This seems to work for me, and it seems to work nice and fast enough as to be usable in any program I might have.
You math guru's make take a look at this, go over it with a fine tooth comb like everyone did the CircleFill routine and see what might be improved with it, and share your thoughts with us, if you would. Without a whole lot of other versions to compare against, I don't know whether to call this really speedy or not.
At the moment, it seems suitable for my personal usage. I dunno if others have better or faster versions which do the same thing. If so, please share and let's compare!
(Note that you can really see the polygon code in action if you change the CONST inside the routine. Heck, there might even be times where you WANT to do so, for some particular reason or the other. Change it to something like 10! or 20! and then see how our circle looks. It'll be obvious with those values as to what it's doing for us. (You can also make the routine *faster* if desired by going with a rougher circle. Change the value to 2 or 5 and it's not that visually different, but it changes our polygon count by that step.)
CircleFill is nice and fast. This is my attempt to see if I can do the same style fastness for an ArcFill. Let me know what you guys think about it.
EDIT: Condensed version which wraps all commands to the main routine can be found here: https://qb64phoenix.com/forum/showthread...6#pid39566
I'll leave the original up here so folks can just grab whatever they need from it with each routine holding independent, but I honestly recommend just using the one routine to do it all. It's all you honestly need now.
and it didn't work as it never seemed to properly close the arcs and then the paint bled out and colored the entire screen.
The CircleFill that I shared and everyone has used for ages and ages works off LINE statements and thus can never bleed outside the circle itself. It's fast and lovely and nice.
And so, I thought, can I come up with a similar way to do ARCs?
After a few different tries, this is what I've came up with -- a scanline polygon fill routine for arcs.
Code: (Select All)
$Color:32
Screen _NewImage(1024, 720, 32)
FilledArc 400, 300, 200, 150, 30, _RGB32(0, 180, 255)
Sleep
Cls , Red
FilledArc 400, 300, 200, 0, 360, _RGBA32(125, 0, 180, 255)
Sleep
Cls , White
' Draw a ring arc from 30 to 300, inner radius 100, outer radius 200
FilledRingArc 400, 300, 150, 200, 30, 300, _RGB32(255, 180, 0)
Sleep
' ---------------------------------------------------------
' TEST DRAWING
' ---------------------------------------------------------
Cls
Color _RGB32(255, 255, 255)
Print "Elliptical Arc / Ring Arc Test"
Print "Press any key to exit."
' Pie slice
FilledEllipseArc 250, 250, 200, 120, 20, 200, _RGB32(0, 180, 255)
Print "(1) Filled Elliptical Arc at (250,250)"
' Ring arc
FilledEllipseRingArc 700, 250, 80, 40, 200, 120, 45, 300, _RGB32(255, 150, 0)
Print "(2) Filled Elliptical Ring Arc at (700,250)"
' Another test arc
FilledEllipseArc 250, 300, 150, 250, 270, 360, _RGB32(0, 255, 120)
Print "(3) Tall Elliptical Arc at (250,300)"
' Another ring arc
FilledEllipseRingArc 700, 500, 50, 100, 180, 250, 0, 180, _RGB32(200, 80, 255)
Print "(4) Vertical Ring Arc at (700,500)"
Sleep
End
Sub FilledRingArc (cx As Long, cy As Long, r1 As Long, r2 As Long, a1 As Single, a2 As Single, col As _Unsigned Long)
' Draws a filled ring arc (donut slice)
' cx, cy = center
' r1 = inner radius
' r2 = outer radius
' a1, a2 = start/end angles in degrees
' col = fill color
Const angStep! = 1! ' smaller = smoother arc
Dim As Single vx(0 To 2000), vy(0 To 2000), interX(0 To 2000)
Dim As Single angle, x, y, x1, y1, x2, y2, temp
Dim As Long count, i, j, n, minY, maxY, yScan
' Normalize angles
If a1 < 0 _OrElse a1 > 360 Then a1 = a1 Mod 360
If a2 < 0 _OrElse a2 > 360 Then a2 = a2 Mod 360
If a2 < a1 Then a2 = a2 + 360
' ---- Outer arc (A1 ? A2) ----
For angle = a1 To a2 Step angStep
x = cx + r2 * Cos(_D2R(angle)): y = cy - r2 * Sin(_D2R(angle)): vx(n) = x: vy(n) = y: n = n + 1
Next
' Ensure exact endpoint
x = cx + r2 * Cos(_D2R(a2)): y = cy - r2 * Sin(_D2R(a2)): vx(n) = x: vy(n) = y: n = n + 1
' ---- Inner arc (A2 ? A1, reversed) ----
For angle = a2 To a1 Step -angStep
x = cx + r1 * Cos(_D2R(angle)): y = cy - r1 * Sin(_D2R(angle)): vx(n) = x: vy(n) = y: n = n + 1
Next
' Ensure exact endpoint
x = cx + r1 * Cos(_D2R(a1)): y = cy - r1 * Sin(_D2R(a1)): vx(n) = x: vy(n) = y: n = n + 1
' ---- Scanline fill ----
minY = vy(0): maxY = vy(0)
For i = 1 To n - 1: maxY = _Max(maxY, vy(i)): minY = _Min(minY, vy(i)): Next
For yScan = minY To maxY
count = 0
' Find intersections
For i = 0 To n - 1
j = (i + 1) Mod n: x1 = vx(i): y1 = vy(i): x2 = vx(j): y2 = vy(j)
If (y1 <= yScan And y2 > yScan) Or (y2 <= yScan And y1 > yScan) Then
If y2 <> y1 Then interX(count) = x1 + (yScan - y1) * (x2 - x1) / (y2 - y1): count = count + 1
End If
Next
' Sort intersections
For i = 0 To count - 2
For j = i + 1 To count - 1
If interX(j) < interX(i) Then Swap interX(i), interX(j)
Next j, i
' Draw spans
For i = 0 To count - 2 Step 2
Line (CLng(interX(i)), yScan)-(CLng(interX(i + 1)), yScan), col, BF
Next
Next yScan
End Sub
Sub FilledArc (cx As Long, cy As Long, r As Long, a1 As Single, a2 As Single, col As _Unsigned Long)
$Checking:Off
' Filled arc (pie slice) using only LINE (scanline polygon fill)
' cx, cy = center of circle
' r = radius
' a1, a2 = start and end angles in degrees (0 = right, 90 = up)
' col = color
Const angStep! = 1! ' angle step in degrees (smaller = smoother arc)
Dim As Single vx(0 To 720), vy(0 To 720), angle, x, y
Dim As Single interX(0 To 720), x1, y1, x2, y2
Dim As Long i, n, count, yScan, j, k, temp, minY, maxY
' Normalize angles
If a1 < 0 _OrElse a1 > 360 Then a1 = a1 Mod 360
If a2 < 0 _OrElse a2 > 360 Then a2 = a2 Mod 360
If a2 < a1 Then a2 = a2 + 360
' Build polygon: start at center
vx(0) = cx: vy(0) = cy: n = 1
' Arc edge points
For angle = a1 To a2 Step angStep 'with a higher anglestep we have a less rounded arc and more of a polygon figure
x = cx + r * Cos(_D2R(angle)): y = cy - r * Sin(_D2R(angle))
vx(n) = x: vy(n) = y: n = n + 1
Next angle
' Ensure last point exactly at a2
x = cx + r * Cos(_D2R(a2)): y = cy - r * Sin(_D2R(a2))
vx(n) = x: vy(n) = y: n = n + 1
' Close polygon back to center
vx(n) = cx: vy(n) = cy: n = n + 1
' --- Scanline fill of polygon ---
minY = vy(0): maxY = vy(0)
For i = 1 To n - 1: maxY = _Max(maxY, vy(i)): minY = _Min(minY, vy(i)): Next
For yScan = minY To maxY
' Find intersections of scanline with each edge
count = 0
For i = 0 To n - 1
j = (i + 1) Mod n
x1 = vx(i): y1 = vy(i): x2 = vx(j): y2 = vy(j)
' Check if edge crosses this scanline
If (y1 <= yScan And y2 > yScan) Or (y2 <= yScan And y1 > yScan) Then
If y2 <> y1 Then interX(count) = x1 + (yScan - y1) * (x2 - x1) / (y2 - y1): count = count + 1
End If
Next
' Sort intersections (simple bubble sort; count is small)
For i = 0 To count - 2
For j = i + 1 To count - 1
If interX(j) < interX(i) Then Swap interX(i), interX(j)
Next j, i
' Draw horizontal spans between pairs of intersections
For i = 0 To count - 2 Step 2
Line ((interX(i)), yScan)-((interX(i + 1)), yScan), col, BF
Next
Next yScan
$Checking:On
End Sub
Sub FilledEllipseArc (cx As Long, cy As Long, a As Long, b As Long, a1 As Single, a2 As Single, col As _Unsigned Long)
$Checking:Off
' Filled elliptical arc (pie slice) using only LINE
' cx, cy = center
' a, b = ellipse radii (horizontal, vertical)
' a1, a2 = start/end angles in degrees
' col = color
Const angStep! = 1! ' angle step in degrees (smaller = smoother arc)
Dim As Single vx(0 To 2000), vy(0 To 2000), angle, x, y
Dim As Single interX(0 To 2000), x1, y1, x2, y2
Dim As Long i, n, count, yScan, j, k, temp, minY, maxY
' Normalize angles
If a1 < 0 _OrElse a1 > 360 Then a1 = a1 Mod 360
If a2 < 0 _OrElse a2 > 360 Then a2 = a2 Mod 360
If a2 < a1 Then a2 = a2 + 360
' Build polygon: start at center
vx(0) = cx: vy(0) = cy: n = 1
' Arc edge points
For angle = a1 To a2 Step angStep 'with a higher anglestep we have a less rounded arc and more of a polygon figure
x = cx + a * Cos(_D2R(angle)): y = cy - b * Sin(_D2R(angle))
vx(n) = x: vy(n) = y: n = n + 1
Next angle
' Ensure last point exactly at a2
x = cx + a * Cos(_D2R(a2)): y = cy - b * Sin(_D2R(a2))
vx(n) = x: vy(n) = y: n = n + 1
' Close polygon back to center
vx(n) = cx: vy(n) = cy: n = n + 1
' --- Scanline fill of polygon ---
minY = vy(0): maxY = vy(0)
For i = 1 To n - 1: maxY = _Max(maxY, vy(i)): minY = _Min(minY, vy(i)): Next
For yScan = minY To maxY
' Find intersections of scanline with each edge
count = 0
For i = 0 To n - 1
j = (i + 1) Mod n
x1 = vx(i): y1 = vy(i): x2 = vx(j): y2 = vy(j)
' Check if edge crosses this scanline
If (y1 <= yScan And y2 > yScan) Or (y2 <= yScan And y1 > yScan) Then
If y2 <> y1 Then interX(count) = x1 + (yScan - y1) * (x2 - x1) / (y2 - y1): count = count + 1
End If
Next
' Sort intersections (simple bubble sort; count is small)
For i = 0 To count - 2
For j = i + 1 To count - 1
If interX(j) < interX(i) Then Swap interX(i), interX(j)
Next j, i
' Draw horizontal spans between pairs of intersections
For i = 0 To count - 2 Step 2
Line ((interX(i)), yScan)-((interX(i + 1)), yScan), col, BF
Next
Next yScan
$Checking:On
End Sub
' Filled elliptical ring arc using only LINE
' cx, cy = center
' a1, b1 = inner ellipse radii
' a2, b2 = outer ellipse radii
' ang1, ang2 = start/end angles
' col = fill color
Sub FilledEllipseRingArc (cx As Long, cy As Long, a1 As Long, b1 As Long, a2 As Long, b2 As Long, ang1 As Single, ang2 As Single, col As _Unsigned Long)
Const angStep! = 2!
Dim vx(0 To 4000) As Single
Dim vy(0 To 4000) As Single
Dim interX(0 To 4000) As Single
Dim count As Long
Dim yScan As Long
Dim x1 As Single, y1 As Single, x2 As Single, y2 As Single
Dim j As Long, temp As Single
Dim minY As Long, maxY As Long
Dim i As Long
Dim n As Long
Dim angle As Single
Dim x As Single, y As Single
If ang2 < ang1 Then ang2 = ang2 + 360
n = 0
' ---- Outer ellipse arc (ang1 ? ang2) ----
For angle = ang1 To ang2 Step angStep
x = cx + a2 * Cos(angle * _Pi / 180)
y = cy - b2 * Sin(angle * _Pi / 180)
vx(n) = x: vy(n) = y: n = n + 1
Next
' Exact endpoint
x = cx + a2 * Cos(ang2 * _Pi / 180)
y = cy - b2 * Sin(ang2 * _Pi / 180)
vx(n) = x: vy(n) = y: n = n + 1
' ---- Inner ellipse arc (ang2 ? ang1, reversed) ----
For angle = ang2 To ang1 Step -angStep
x = cx + a1 * Cos(angle * _Pi / 180)
y = cy - b1 * Sin(angle * _Pi / 180)
vx(n) = x: vy(n) = y: n = n + 1
Next
' Exact endpoint
x = cx + a1 * Cos(ang1 * _Pi / 180)
y = cy - b1 * Sin(ang1 * _Pi / 180)
vx(n) = x: vy(n) = y: n = n + 1
' ---- Scanline fill (same as before) ----
minY = vy(0): maxY = vy(0)
For i = 1 To n - 1
If vy(i) < minY Then minY = vy(i)
If vy(i) > maxY Then maxY = vy(i)
Next
For yScan = minY To maxY
count = 0
For i = 0 To n - 1
j = (i + 1) Mod n
x1 = vx(i): y1 = vy(i)
x2 = vx(j): y2 = vy(j)
If (y1 <= yScan And y2 > yScan) Or (y2 <= yScan And y1 > yScan) Then
If y2 <> y1 Then
interX(count) = x1 + (yScan - y1) * (x2 - x1) / (y2 - y1)
count = count + 1
End If
End If
Next
' Sort intersections
For i = 0 To count - 2
For j = i + 1 To count - 1
If interX(j) < interX(i) Then Swap interX(i), interX(j)
Next j, i
' Draw spans
For i = 0 To count - 2 Step 2
Line (CLng(interX(i)), yScan)-(CLng(interX(i + 1)), yScan), col, BF
Next
Next yScan
End Sub
It does all the math first and calculates all those points which make up the edges of the circle and the lines of the sides of the arc, and then it draws the lines which makes up the circle.This seems to work for me, and it seems to work nice and fast enough as to be usable in any program I might have.
You math guru's make take a look at this, go over it with a fine tooth comb like everyone did the CircleFill routine and see what might be improved with it, and share your thoughts with us, if you would. Without a whole lot of other versions to compare against, I don't know whether to call this really speedy or not.
At the moment, it seems suitable for my personal usage. I dunno if others have better or faster versions which do the same thing. If so, please share and let's compare!
(Note that you can really see the polygon code in action if you change the CONST inside the routine. Heck, there might even be times where you WANT to do so, for some particular reason or the other. Change it to something like 10! or 20! and then see how our circle looks. It'll be obvious with those values as to what it's doing for us. (You can also make the routine *faster* if desired by going with a rougher circle. Change the value to 2 or 5 and it's not that visually different, but it changes our polygon count by that step.)
CircleFill is nice and fast. This is my attempt to see if I can do the same style fastness for an ArcFill. Let me know what you guys think about it.

EDIT: Condensed version which wraps all commands to the main routine can be found here: https://qb64phoenix.com/forum/showthread...6#pid39566
I'll leave the original up here so folks can just grab whatever they need from it with each routine holding independent, but I honestly recommend just using the one routine to do it all. It's all you honestly need now.



