rwg
New Member
Posts: 49
|
Post by rwg on Mar 3, 2023 19:21:35 GMT -5
hi all - when I draw a line using print #handle, "line X1 Y1 X2 Y2" I get a jagged line, serrated edges. is there any way I can produce smooth lines? many thanks rwg
|
|
|
Post by Rod on Mar 4, 2023 5:17:07 GMT -5
There are only so many pixels on the screen. A single line uses all available pixels to draw the fastest slimmest line. If you want it smoother you need to fill in adjacent pixels. This slows down the drawing process and thickens the line. The jaggedness is usually lost in the printing process and some screens auto anti alias.
It is quite a complex process to anti alias the line, there are a couple of formula to google though I cant recall anyone coding them in Liberty.
The alternative is to "dither" the entire scene. Try a paint package and dither some graphics to get a feel for it. You could dither a scene in Liberty but it would be slow.
perhaps a simpler fix is just to increase the line drawing size.
|
|
|
Post by Brandon Parker on Mar 4, 2023 9:22:39 GMT -5
There was definitely one that I worked on for the Rosetta Code "Bitmap/Bresenham's line algorithm" section with some interaction from Anatoly (on the previous forum site I believe), but it seems to be missing from Rosetta Code. Others have implemented their own as well, but as Rod mentioned, these routines are slower than drawing with LB itself.
{:0)
Brandon Parker
|
|
|
Post by Rod on Mar 4, 2023 10:12:40 GMT -5
Here from Rosetta Wo!NoMainWin WindowWidth = 270 WindowHeight = 290 UpperLeftX=int((DisplayWidth-WindowWidth)/2) UpperLeftY=int((DisplayHeight-WindowHeight)/2)
Global variablesInitialized : variablesInitialized = 0 Global BackColor$ : BackColor$ = "0 0 0" ' BackColor$ = "255 255 255" 'now, right click randomizes BG Global size : size = 1'4 global mousepoints.mouseX0, mousepoints.mouseY0, mousepoints.mouseX1, mousepoints.mouseY1
'StyleBits #main.gbox, 0, _WS_BORDER, 0, 0 GraphicBox #main.gbox, 0, 0, 253, 252
Open "Click Twice to Form Line" For Window As #main Print #main, "TrapClose quit" Print #main.gbox, "Down; Color Black" Print #main.gbox, "Down; fill ";BackColor$ Print #main.gbox, "When leftButtonUp gBoxClick" Print #main.gbox, "When rightButtonUp RandomBG" Print #main.gbox, "Size "; size
result = drawAntiAliasedLine(126.5, 0, 126.5, 252, "255 0 0") result = drawAntiAliasedLine(0, 126, 253, 126, "255 0 0") result = drawAntiAliasedLine(0, 0, 253, 252, "255 0 0") result = drawAntiAliasedLine(253, 0, 0, 252, "255 0 0") Wait
Sub quit handle$ Close #main End End Sub
sub RandomBG handle$, MouseX, MouseY BackColor$ = int(rnd(1)*256);" ";int(rnd(1)*256);" ";int(rnd(1)*256) Print #main.gbox, "CLS; fill ";BackColor$ variablesInitialized = 0 end sub
Sub gBoxClick handle$, MouseX, MouseY 'We will use the mousepoints "struct" to hold the values 'that way they are retained between subroutine calls If variablesInitialized = 0 Then Print #main.gbox, "CLS; fill ";BackColor$ mousepoints.mouseX0 = MouseX mousepoints.mouseY0 = MouseY variablesInitialized = 1 Else If variablesInitialized = 1 Then mousepoints.mouseX1 = MouseX mousepoints.mouseY1 = MouseY variablesInitialized = 0 result = drawAntiAliasedLine(mousepoints.mouseX0, mousepoints.mouseY0, mousepoints.mouseX1, mousepoints.mouseY1, "255 0 0") End If End If End Sub
Function Swap(Byref a,Byref b) aTemp = b b = a a = aTemp End Function
Function RoundtoInt(val) RoundtoInt = Int(val + 0.5) End Function
Function PlotAntiAliased(x, y, RGB$, b, steep)
RGB$ = Int(Val(Word$(BackColor$, 1))*(1-b) + Val(Word$(RGB$, 1)) * b) ; " " ; _ Int(Val(Word$(BackColor$, 2))*(1-b) + Val(Word$(RGB$, 3)) * b) ; " " ; _ Int(Val(Word$(BackColor$, 3))*(1-b) + Val(Word$(RGB$, 2)) * b)
if steep then 'x and y reversed Print #main.gbox, "Down; Color " + RGB$ + "; Set " + str$(y) + " " + str$(x) else Print #main.gbox, "Down; Color " + RGB$ + "; Set " + str$(x) + " " + str$(y) end if End Function
Function fracPart(x) fracPart = (x Mod 1) End function
Function invFracPart(x) invFracPart = (1 - fracPart(x)) End Function
Function drawAntiAliasedLine(x1, y1, x2, y2, RGB$) If (x2 - x1)=0 Or (y2 - y1)=0 Then Print #main.gbox, "Down; Color " + RGB$ result = BresenhamLine(x1, y1, x2, y2) Exit Function End If steep = abs(x2 - x1) < abs(y2 - y1) if steep then 'x and y should be reversed result = Swap(x1, y1) result = Swap(x2, y2) end if
If (x2 < x1) Then result = Swap(x1, x2) result = Swap(y1, y2) End If dx = (x2 - x1) dy = (y2 - y1) grad = (dy/ dx) 'Handle the First EndPoint xend = RoundtoInt(x1) yend = y1 + grad * (xend - x1) xgap = invFracPart(x1 + 0.5) ix1 = xend iy1 = Int(yend) result = PlotAntiAliased(ix1, iy1, RGB$, invFracPart(yend) * xgap, steep ) result = PlotAntiAliased(ix1, (iy1 + size), RGB$, fracPart(yend) * xgap, steep ) yf = (yend + grad) 'Handle the Second EndPoint xend = RoundtoInt(x2) yend = y2 + grad * (xend - x2) xgap = fracPart(x2 + 0.5) ix2 = xend iy2 = Int(yend) result = PlotAntiAliased(ix2, iy2, RGB$, invFracPart(yend) * xgap, steep ) result = PlotAntiAliased(ix2, (iy2 + size), RGB$, fracPart(yend) * xgap, steep ) For x = ix1 + 1 To ix2 - 1 result = PlotAntiAliased(x, Int(yf), RGB$, invFracPart(yf), steep ) result = PlotAntiAliased(x, (Int(yf) + size), RGB$, fracPart(yf), steep ) yf = (yf + grad) Next x End Function
Function BresenhamLine(x0, y0, x1, y1) dx = Abs(x1 - x0) dy = Abs(y1 - y0) sx = ((x1 > x0) + Not(x0 < x1)) sy = ((y1 > y0) + Not(y0 < y1)) errornum = (dx - dy) Do While 1 Print #main.gbox, "Set " + str$(x0) + " " + str$(y0) If (x0 = x1) And (y0 = y1) Then Exit Do errornum2 = (2 * errornum) If errornum2 > (-1 * dy) Then errornum = (errornum - dy) x0 = (x0 + sx) End If If errornum2 < dx Then errornum = (errornum + dx) y0 = (y0 + sy) End If Loop End Function
|
|
|
Post by tsh73 on Mar 4, 2023 11:43:31 GMT -5
It definitely does work but to see it I actually had to install ZoomIt (and make BASIC draw ordinary parallel line) (I think this is 2x zoom)
|
|
|
Post by Brandon Parker on Mar 4, 2023 14:02:50 GMT -5
That's it, Rod!! I guess I got the name mixed up (Xiaolin Wu's line algorithm does use the Bresenham algorithm though )... All you have to do is change the size variable to "really" see it in action although it is not necessarily great looking when the size is too large. {:0) Brandon Parker
|
|
|
Post by tsh73 on Mar 4, 2023 14:20:20 GMT -5
Doesn't quite work for random angle. Points are big, but they are only 1 pix apart so they merge to a some structure (no distinct big points, often no visible color change - just small pixels of black and red)
|
|
|
Post by Brandon Parker on Mar 4, 2023 14:28:12 GMT -5
Exactly! One would have to rewrite it to draw the Xaolin Wu algorithm for the edges and then each adjacent line internal to that to the center would be faded to the actual line color. Or, something along those lines...
{:0)
Brandon Parker
|
|
rwg
New Member
Posts: 49
|
Post by rwg on Mar 4, 2023 21:20:09 GMT -5
Thanks Rod et al. I did find the Zaowin algorithm and it worked fine but the complexity is beyond me. I'll put up with a bit of jaggedness
|
|
|
Post by Rod on Mar 5, 2023 2:27:28 GMT -5
Yes, best decision. Not sure why the examples use 45o lines since these have least jagging.
|
|
|
Post by tsh73 on Mar 5, 2023 15:24:56 GMT -5
I made big visible points easiest way - divided coords by size on entering into line drawing, then magnifying coords back while plotting pixels. Also added Bresenham line (blue one) parallel to antialiased red one (you just click first point, then you click second point and it draws two lines - antialiased red and jagged blue) NoMainWin WindowWidth = 270 WindowHeight = 290 UpperLeftX=int((DisplayWidth-WindowWidth)/2) UpperLeftY=int((DisplayHeight-WindowHeight)/2)
Global variablesInitialized : variablesInitialized = 0 Global BackColor$ : BackColor$ = "0 0 0" ' BackColor$ = "255 255 255" 'now, right click randomizes BG Global size : size = 6 global mousepoints.mouseX0, mousepoints.mouseY0, mousepoints.mouseX1, mousepoints.mouseY1
'StyleBits #main.gbox, 0, _WS_BORDER, 0, 0 GraphicBox #main.gbox, 0, 0, 253, 252
Open "Click Twice to Form Line" For Window As #main Print #main, "TrapClose quit" Print #main.gbox, "Down; Color Black" Print #main.gbox, "Down; fill ";BackColor$ Print #main.gbox, "When leftButtonUp gBoxClick" Print #main.gbox, "When rightButtonUp RandomBG" Print #main.gbox, "Size "; size
' result = drawAntiAliasedLine(126.5, 0, 126.5, 252, "255 0 0") ' result = drawAntiAliasedLine(0, 126, 253, 126, "255 0 0") ' result = drawAntiAliasedLine(0, 0, 253, 252, "255 0 0") ' result = drawAntiAliasedLine(253, 0, 0, 252, "255 0 0")
result = drawAntiAliasedLine(50, 0, 200, 250, "255 0 0") result = drawAntiAliasedLine(0, 50, 250, 200, "255 0 0") result = drawAntiAliasedLine(0, 100, 250, 150, "255 0 0")
Wait
Sub quit handle$ Close #main End End Sub
sub RandomBG handle$, MouseX, MouseY BackColor$ = int(rnd(1)*256);" ";int(rnd(1)*256);" ";int(rnd(1)*256) Print #main.gbox, "CLS; fill ";BackColor$ variablesInitialized = 0 end sub
Sub gBoxClick handle$, MouseX, MouseY 'We will use the mousepoints "struct" to hold the values 'that way they are retained between subroutine calls If variablesInitialized = 0 Then Print #main.gbox, "CLS; fill ";BackColor$ mousepoints.mouseX0 = MouseX mousepoints.mouseY0 = MouseY variablesInitialized = 1 Else If variablesInitialized = 1 Then mousepoints.mouseX1 = MouseX mousepoints.mouseY1 = MouseY variablesInitialized = 0 result = drawAntiAliasedLine(mousepoints.mouseX0, mousepoints.mouseY0, mousepoints.mouseX1, mousepoints.mouseY1, "255 0 0") #main.gbox, "color blue" x1=mousepoints.mouseX0 y1=mousepoints.mouseY0 x2=mousepoints.mouseX1 y2=mousepoints.mouseY1 d=sqr((x1-x2)^2+(y1-y2)^2) dx=(x2-x1)*2*size/d dy=(y2-y1)*2*size/d result = BresenhamLine(mousepoints.mouseX0-dy, mousepoints.mouseY0+dx, mousepoints.mouseX1-dy, mousepoints.mouseY1+dx) End If End If End Sub
Function Swap(Byref a,Byref b) aTemp = b b = a a = aTemp End Function
Function RoundtoInt(val) RoundtoInt = Int(val + 0.5) End Function
Function PlotAntiAliased(x, y, RGB$, b, steep)
RGB$ = Int(Val(Word$(BackColor$, 1))*(1-b) + Val(Word$(RGB$, 1)) * b) ; " " ; _ Int(Val(Word$(BackColor$, 2))*(1-b) + Val(Word$(RGB$, 3)) * b) ; " " ; _ Int(Val(Word$(BackColor$, 3))*(1-b) + Val(Word$(RGB$, 2)) * b)
if steep then 'x and y reversed Print #main.gbox, "Down; Color " + RGB$ + "; Set " + str$(y*size) + " " + str$(x*size) else Print #main.gbox, "Down; Color " + RGB$ + "; Set " + str$(x*size) + " " + str$(y*size) end if End Function
Function fracPart(x) fracPart = (x Mod 1) End function
Function invFracPart(x) invFracPart = (1 - fracPart(x)) End Function
Function drawAntiAliasedLine(x1, y1, x2, y2, RGB$) x1=x1/size y1=y1/size x2=x2/size y2=y2/size 'so vertical or horisontal fall back to Bresenham 'thich is weird actually If (x2 - x1)=0 Or (y2 - y1)=0 Then Print #main.gbox, "Down; Color " + RGB$ result = BresenhamLine(x1, y1, x2, y2) Exit Function End If steep = abs(x2 - x1) < abs(y2 - y1) if steep then 'x and y should be reversed result = Swap(x1, y1) result = Swap(x2, y2) end if
If (x2 < x1) Then result = Swap(x1, x2) result = Swap(y1, y2) End If dx = (x2 - x1) dy = (y2 - y1) grad = (dy/ dx) 'Handle the First EndPoint xend = RoundtoInt(x1) yend = y1 + grad * (xend - x1) xgap = invFracPart(x1 + 0.5) ix1 = xend iy1 = Int(yend) result = PlotAntiAliased(ix1, iy1, RGB$, invFracPart(yend) * xgap, steep ) result = PlotAntiAliased(ix1, (iy1 + 1), RGB$, fracPart(yend) * xgap, steep ) yf = (yend + grad) 'Handle the Second EndPoint xend = RoundtoInt(x2) yend = y2 + grad * (xend - x2) xgap = fracPart(x2 + 0.5) ix2 = xend iy2 = Int(yend) result = PlotAntiAliased(ix2, iy2, RGB$, invFracPart(yend) * xgap, steep ) result = PlotAntiAliased(ix2, (iy2 + 1), RGB$, fracPart(yend) * xgap, steep ) For x = ix1 + 1 To ix2 - 1 result = PlotAntiAliased(x, Int(yf), RGB$, invFracPart(yf), steep ) result = PlotAntiAliased(x, (Int(yf) + 1), RGB$, fracPart(yf), steep ) yf = (yf + grad) Next x End Function
Function BresenhamLine(x0, y0, x1, y1) x1=int(x1/size+0.5) y1=int(y1/size+0.5) x0=int(x0/size+0.5) y0=int(y0/size+0.5) dx = Abs(x1 - x0) dy = Abs(y1 - y0) sx = ((x1 > x0) + Not(x0 < x1)) sy = ((y1 > y0) + Not(y0 < y1)) errornum = (dx - dy) Do While 1 Print #main.gbox, "Set " + str$(x0*size) + " " + str$(y0*size) If (x0 = x1) And (y0 = y1) Then Exit Do errornum2 = (2 * errornum) If errornum2 > (-1 * dy) Then errornum = (errornum - dy) x0 = (x0 + sx) End If If errornum2 < dx Then errornum = (errornum + dx) y0 = (y0 + sy) End If Loop End Function
|
|
rwg
New Member
Posts: 49
|
Post by rwg on Mar 6, 2023 0:25:06 GMT -5
Very clever Anatoly
|
|