I've put in many hours for a thick line routine and then extended it for dashed lines. Moreover, I wanted the routines to work for sparse points (e.g. end points of a long line) or dense point representation of non-linear functions.
My best solution to date build on Ecplizer's thick line routine (slow, not good for games, but the cleanest I've seen). I modified his routine to handle floating point coordinates, then coupled it with my own dashed line routine (much more complicated than you might originally think).
Alas... none of this build on the paint-flood approach we've been discussing.
Code: Select all
' Thick line routine
' Slow, but very clean
'
' Author: Quinton Roberts (Eclipzer)
' Original operates on integer only. No scaling for View, Window
'
' Modified: Steve Cannon
' Accepts floating point x,y and scaling by View, Window
'
SUB Thick_Line (byval sx1 as single, byval sy1 as single, byval sx2 as single, byval sy2 as single, _
byval thickness as integer, byval c as uinteger)
'Avoid the dumb cases
If thickness < 1 then thickness = 1
If thickness = 1 then
Line (sx1, sy1) - (sx2, sy2), C
exit sub
end if
'Conversion to integer (physical screen) coordinates
DIM as integer x1, y1, x2, y2
DIM as integer vx1, vy1, vx2, vy2
DIM as single x_min, y_min, x_max, y_max
DIM as integer screen_width, screen_height
ScreenInfo Screen_Width, Screen_Height
'Get viewport
ScreenControl (11, vx1, vy1, vx2, vy2)
'Get Window Extremes
' This method works if Window (xmin, ymin) - (xmax, ymax) has been defined
x_min = pmap(0,2) : x_max = pmap(vx2-vx1+1,2)
y_min = pmap(vy2-vy1,3) : y_max = pmap(-1,3)
IF y_min > y_max then ' Oops, default Window with y increasing DOWN the page
' Use this method to get max values
x_max = pmap(vx2-vx1,2)
y_max = pmap(0,3)
end if
'Convert to native screen (pixel) coordinates
x1 = (sx1-x_min)/(x_max-x_min)*(vx2-vx1+1)
x2 = (sx2-x_min)/(x_max-x_min)*(vx2-vx1+1)
'Y-axis is tricky
'If necessary, invert to cartesian coordinates with increasing y going upward
IF y_min > y_max then
y1 = screen_Height - (sy1-y_min)/(y_max-y_min)*(vy2-vy1+1)
y2 = Screen_Height - (sy2-y_min)/(y_max-y_min)*(vy2-vy1+1)
ELSE
y1 = (y_max - sy1)/(y_max-y_min)*(vy2-vy1+1) -1
y2 = (y_max - sy2)/(y_max-y_min)*(vy2-vy1+1) -1
end if
'Set default scale
Window
'Eclipzer routine f rom here
Dim As single t2=(thickness/2)
Dim As integer bx(1)={x1,x2}
Dim As integer by(1)={y1,y2}
Dim As Integer LI=0,RI=1
Dim As Integer TI=0,BI=1
If bx(LI)>bx(RI) Then Swap LI,RI
If by(TI)>by(BI) Then Swap TI,BI
Dim As Single dx=(bx(RI)-bx(LI))
Dim As Single dy=(by(RI)-by(LI))
Dim As Single dydx=dy/dx
Dim As Single dydx2=dydx*dydx
Dim As Single b=y1-dydx*x1,d
Dim As Single ndx=-dy
Dim As Single ndy= dx
Dim As Single length=Sqr(dx*dx+dy*dy)
Dim As Single nx=ndx/length
Dim As Single ny=ndy/length
Dim As Single px,py
For y As single =by(TI)-t2 To by(BI)+t2 'modified here with + 1
For x As single =bx(LI)-t2 To bx(RI)+t2 'modified here with + 1
If dx Then 'non-vertical line
d=(dydx*x-y+b)/Sqr(dydx2+1) 'point-to-line distance equation
px=x+d*nx 'projected x
py=y+d*ny 'projected y
Select Case px
Case Is < bx(LI)
Dim As Single xx=x-bx(LI)
Dim As Single yy=y-by(LI)
d=Sqr(xx*xx+yy*yy)
Case Is > bx(RI)
Dim As Single xx=x-bx(RI)
Dim As Single yy=y-by(RI)
d=Sqr(xx*xx+yy*yy)
Case Else: d=Abs(d)
End Select
Else 'vertical line
Select Case y
Case Is < by(TI)
Dim As Single xx=x-bx(TI)
Dim As Single yy=y-by(TI)
d=Sqr(xx*xx+yy*yy)
Case Is > by(BI)
Dim As Single xx=x-bx(BI)
Dim As Single yy=y-by(BI)
d=Sqr(xx*xx+yy*yy)
Case Else: d=x-x1
End Select
End If
IF d<t2 then Pset (x,y), C
IF ((d-t2) <= 1) then Pset (x,y),c
'Fancy stuff if alpha channel is used.
'If d<t2 Then
' colour.a=alpha
' Pset(x,y),colour
'Elseif (d-t2)<=1 Then
' colour.a=alpha*(1-(d-t2))
' Pset (x,y),colour
'End If
Next
Next
'If necessary, reset Window before leaving
IF y_min < y_max then window (x_min,y_min)-(x_max,y_max)
END SUB
' Dashed Line Routine
'
' dlen is the length of line segment (and blank) for dashed line, in units of pixels
' Line_Len is the length of the line segments
' Thick is line thickness in pixels
'
Sub Dash_Line(Byval x1 As single, Byval y1 As single, Byval x2 As single, Byval y2 As single, _
Byval dlen As Integer, Byval pcolor As Uinteger, byval thick as integer = 1)
Dim As Double dx, xnow, ynow, slope, Dist, Dist_Raw
Static Line_len As Double 'Length of current line segment being rendered
Static Draw_Flag As Byte 'Flag to track drawing a segment vs blank gap
Dim As Byte TRUE = 1, FALSE = 0
Dim as double delta_x = pmap(1,2) - pmap(0,2) 'delta X for 1 pixel displacement
Dim as double delta_y = abs(pmap(1,3) - pmap(0,3)) 'delta Y for 1 pixel displacement
If x1 = x2 Then 'vertical line, pathological case for slope
If Y2 < y1 Then Swap y1,y2
Dist = (y2-y1) / delta_y 'in units of pixels
ynow = y1
IF Dist >= (dlen - Line_Len) then
Do
IF Draw_Flag = TRUE then
Thick_Line (x1, ynow + (Thick\2)*Delta_y, x1, ynow + ((dlen - Line_len -1) - Thick\2)*Delta_Y, thick,pcolor)
Draw_Flag = FALSE
ELSE
Draw_Flag = True
end if
Dist = Dist - (dlen - Line_Len)
ynow = ynow + (dlen - Line_len)*Delta_Y
'Line_Len = 0
IF Dist <= dlen then
IF Draw_Flag = TRUE then 'complete line of partial dlen length
Thick_Line (x1, ynow+thick\2, x1, ynow + Dist-1-Thick\2, thick,pcolor)
end if
exit do
end if
Line_Len = 0
loop
Line_Len = Dist
ELSE
IF Draw_Flag = TRUE then
'small compensation for line thickness
IF Thick > 1 then
'Skip rendering on screen if Line_Len <= Thickness
IF Line_Len > thick then Thick_Line (x1, y1 , x1, y2 , thick, pcolor)
ELSE
Thick_Line (x1, y1 , x1, y2 , thick, pcolor)
END IF
end if
Line_Len = Line_Len + Dist
end if
Else
' Diagonal line segment
Dist = Sqr(((x2-x1)/delta_x)^2 + ((y2-y1)/Delta_Y)^2) 'in units of pixels
Dist_Raw = SQR((x2-x1)^2 + (y2-y1)^2)
xnow = x1
ynow = y1
slope = (y2-y1)/(x2-x1)
If Dist >= (dlen - Line_Len) Then
dx = (dlen - Line_Len)*Delta_x * ((x2-x1)/delta_x)/Dist 'in screen units
Do
If Draw_Flag = TRUE Then
Thick_Line (xnow, ynow, xnow+dx, ynow + dx*slope, thick,pcolor)
Draw_Flag = FALSE
Else
Draw_Flag = TRUE
End If
'Reset Line
Dist = Dist - (dlen - Line_len)
xnow = xnow + dx
ynow = ynow + dx*slope
IF Dist <= dlen then
IF Draw_Flag = TRUE then 'complete line of partial dlen length
dx = x2 -xnow
Thick_Line (xnow, ynow, xnow+dx, ynow + dx*slope, thick,pcolor)
end if
exit do
END IF
dx = (dlen*delta_x) * ((x2-xnow)/Delta_x)/Dist
Line_Len = 0
Loop
Line_Len = Dist
Else
If Draw_Flag = TRUE Then
Thick_Line (x1, y1, x2, y2, thick,pcolor)
End If
Line_Len = Line_Len + Dist
End If
End If
End Sub
'-------- MAIN STARTS HERE ----------
Dim As Uinteger bckcolor = RGB(240,238,235)
ScreenRes 640, 480, 32
window (0,-200)-(10,200)
Paint (1,1), bckcolor
open cons for output as #99
'print #99, "Viewport"
'print #99, vx1, vy1, vx2, vy2
'Horiz Line
Dash_Line (1,0,9,0,10,rgb(255,0,0), 2)
For i as integer = 0 to 200
Dash_Line (i/50+1, -100, (i+1)/50+1, -100, 10,rgb(0,0,255), 2)
next
'Vertical Line
For i as integer = 0 to 150
Dash_Line (9, -i, 9, -(i+1), 10, rgb(0,0,255), 2)
next
Dash_Line (1, -150, 1, 150, 10, rgb(255,0,0), 2)
'Diagonal
Dash_Line (1,150, 9, -150, 10, RGB(255,0,0),2)
For i as integer = 0 to 200
Dash_Line (i/25+1, i*(3/2)-150, (i+1)/25+1,(i+1)*(3/2)-150, 10, rgb(0,0,255),2)
next
'Sinewave
For i As Integer = 0 To 200
Dash_Line (i*(8/200)+1, 100*Sin(i*6.28/80), (i+1)*(8/200)+1, 100*Sin((i+1)*6.28/80),20,rgb(0,0,0), 2)
Next
Sleep