Author Topic: FB Graphics #4.1: Colorful Lines  (Read 3771 times)

0 Members and 1 Guest are viewing this topic.

Offline rdc

  • Pentium
  • *****
  • Posts: 1495
  • Karma: 140
  • Yes, it is me.
    • View Profile
    • Clark Productions
FB Graphics #4.1: Colorful Lines
« on: March 02, 2010 »
Introduction

In the last tutorial we created a line object for our screen object. We defined a line as two point objects and Bresenham's line algorithm to draw the line to the buffer. Remember that our point object has a color component to it; we didn't use this in our line object last time, but we can use the point object's color to draw a line with interpolated colors. In this tutorial we will implement a line drawing method that takes the colors of the two point objects and interpolates the colors between the two points for a colorful line.

Interpolating DrawLine Method

There are various methods for interpolating between two points, but for the line object we are going to implement a linear interpolation. All of our code for the new functionality will take place in the screen object since that is where the line drawing routines reside.

Code: [Select]
'Draws a line to the buffer interpolating color of end points.
Sub screenobject.DrawLine (ln As lineobj2D, Alpha As UByte = 255)
   Dim As Integer num = NumPoints(ln)
   Dim As clrobj clr(num), cl1, cl2
   Dim As Integer x = ln.PointX1, y = ln.PointY1, d = 0, dx = ln.PointX2 - ln.PointX1, dy = ln.PointY2 -ln.PointY1, c, m, xinc = 1, yinc = 1
   Dim As Integer cnt = 0
   
   'Get the palette.
   cl1 = ln.ColorValue1
   cl2 = ln.ColorValue2
   GenPalette clr(), cl1, cl2
   
   'Draw the line.   
   If dx < 0 Then
      xinc = -1
      dx = -dx
   EndIf
   If dy < 0 Then
      yinc = -1
      dy = -dy
   EndIf
   If dy < dx Then
      c = 2 * dx
      m = 2 * dy
      Do While x <> ln.PointX2
         If Alpha < 255 Then
            PokeAlpha x, y, clr(cnt).ColorValue, Alpha
            cnt += 1
         Else
            PokeBuffer x, y, clr(cnt).ColorValue
            cnt += 1
         EndIf
         x += xinc
         d += m
         If d > dx Then
            y += yinc
            d -= c
         EndIf
      Loop
   Else
      c = 2 * dy
      m = 2 * dx
      Do While y <> ln.PointY2
         If Alpha < 255 Then
            PokeAlpha x, y, clr(cnt).ColorValue, Alpha
            cnt += 1
         Else
            PokeBuffer x, y, clr(cnt).ColorValue
            cnt += 1
         EndIf
         y += yinc
         d += m
         If d > dy Then
            x += xinc
            d -= c
         EndIf
      Loop
   EndIf
   If Alpha < 255 Then
      PokeAlpha x, y, clr(cnt).ColorValue, Alpha
   Else
      PokeBuffer x, y, clr(cnt).ColorValue
   EndIf
     
End Sub

The bulk of the code is Bresenham's line drawing algorithm which is the same as the previous versions of the DrawLine methods. Here though, we are taking the color of each individual point from an array of color objects.

Code: [Select]
Dim As Integer num = NumPoints(ln)
Dim As clrobj clr(num), cl1, cl2
Dim As Integer cnt = 0

Notice that the variable num calls the function NumPoints to determine the number of points in the line. We will look at this function in a moment. Once we know the number of points we need to color, we create an array of color objects.

Code: [Select]
'Get the palette.
cl1 = ln.ColorValue1
cl2 = ln.ColorValue2
GenPalette clr(), cl1, cl2

The variables cl1 and cl2 are color objects and contain the colors for the line end points. The color array is then passed to the GenPalette subroutine which will fill in the array with a range of colors. Here is the GenPalette code.

Code: [Select]
'Returns a array of colors.
Sub screenobject.GenPalette(pal() As clrobj, stcolor As clrobj, edcolor As clrobj)
   Dim As Double cstep, cstepr, cstepg, cstepb
   Dim As Double valr, valg, valb
   
   'Get the overall step value.
   cstep = UBound(pal)
   'Get the step value for each color channel.
   cstepr = Abs((stcolor.ChannelRed - edcolor.ChannelRed))  / cstep
   cstepg = Abs((stcolor.ChannelGreen - edcolor.ChannelGreen)) / cstep
   cstepb = Abs((stcolor.ChannelBlue - edcolor.ChannelBlue)) / cstep
   'Set the start color components.
   valr = stcolor.ChannelRed
   valg = stcolor.ChannelGreen
   valb = stcolor.ChannelBlue
   'Create the palette.
   For i As Integer = LBound(pal) To UBound(pal)
      pal(i).ColorValue = RGB(CUByte(valr), CuByte(valg), CUByte(valb))

      If stcolor.ChannelRed <= edcolor.ChannelRed Then
         valr += cstepr
      Else
         valr -= cstepr
      EndIf

      If stcolor.ChannelGreen <= edcolor.ChannelGreen Then
         valg += cstepg
      Else
         valg -= cstepg
      EndIf     

      If stcolor.ChannelBlue <= edcolor.ChannelBlue Then
         valb += cstepb
      Else
         valb -= cstepb
      EndIf
   Next
   
End Sub

Generating the color palette is quite easy. We simply break the color range into discrete steps for each color channel, start at the beginning color and then increment or decrement the various color channels until we reach the target color. Once the subroutine exits we have our palette of colors.

Before we can create a list of colors to use in the new line drawing method, we need to know how many points the line will occupy. The NumPoints function will give use the number of points our line contains, which is also the number of colors we need to create. Since we are using Bresenham's line algorithm to plot the points, we can use that to determine how many points our line will contain.

Code: [Select]
'Returns the number of points in line.
Function screenobject.NumPoints(ln As lineobj2D) As Integer
   Dim As Integer x = ln.PointX1, y = ln.PointY1, d = 0, dx = ln.PointX2 - ln.PointX1, dy = ln.PointY2 -ln.PointY1, c, m, xinc = 1, yinc = 1
   Dim As Integer cnt = 0
   
   If dx < 0 Then
      xinc = -1
      dx = -dx
   EndIf
   If dy < 0 Then
      yinc = -1
      dy = -dy
   EndIf
   If dy < dx Then
      c = 2 * dx
      m = 2 * dy
      Do While x <> ln.PointX2
         cnt += 1
         x += xinc
         d += m
         If d > dx Then
            y += yinc
            d -= c
         EndIf
      Loop
   Else
      c = 2 * dy
      m = 2 * dx
      Do While y <> ln.PointY2
         cnt += 1
         y += yinc
         d += m
         If d > dy Then
            x += xinc
            d -= c
         EndIf
      Loop
   EndIf
   cnt += 1
   
   Return cnt
End Function

Here we are using the exact same drawing algorithm we use to draw the individual line pixels. In each place that we normally would plot a point, we increment the cnt variable by 1 so that at the end of the function we have the total number of points that will be plotted for the line. Once we have the number of points, we can create a palette of colors based on the colors of the line end points.

Why use the line algorithm to determine the points? Because we are working on a pixel-based screen. A true line can cross half-pixel points, but we can't plot half-pixels. By using the line algorithm to calculate the points, we will know exactly how many points we need, since the same algorithm will be used to draw the actual line.

Custom Palettes

There are times when you might not want to use a linear interpolation but still want to plot a line in a series of colors. We can use the same DrawLine code, but instead of generating a palette, we can pass a palette to the method to use. The following code implements this version of the DrawLine method.

Code: [Select]
'Draws a line to the buffer using color points.
Sub screenobject.DrawLine (ln As lineobj2D, clr() As clrobj)   
   Dim As Integer x = ln.PointX1, y = ln.PointY1, d = 0, dx = ln.PointX2 - ln.PointX1, dy = ln.PointY2 -ln.PointY1, c, m, xinc = 1, yinc = 1
   Dim As Integer cnt = LBound(clr)
   
   'Draw the line.   
   If dx < 0 Then
      xinc = -1
      dx = -dx
   EndIf
   If dy < 0 Then
      yinc = -1
      dy = -dy
   EndIf
   If dy < dx Then
      c = 2 * dx
      m = 2 * dy
      Do While x <> ln.PointX2
         If clr(cnt).ChannelAlpha < 255 Then
            PokeAlpha x, y, clr(cnt)
            cnt += 1
            If cnt > UBound(clr) Then cnt = LBound(clr)
         Else
            PokeBuffer x, y, clr(cnt)
            cnt += 1
            If cnt > UBound(clr) Then cnt = LBound(clr)
         EndIf
         x += xinc
         d += m
         If d > dx Then
            y += yinc
            d -= c
         EndIf
      Loop
   Else
      c = 2 * dy
      m = 2 * dx
      Do While y <> ln.PointY2
         If clr(cnt).ChannelAlpha < 255 Then
            PokeAlpha x, y, clr(cnt)
            cnt += 1
            If cnt > UBound(clr) Then cnt = LBound(clr)
         Else
            PokeBuffer x, y, clr(cnt)
            cnt += 1
            If cnt > UBound(clr) Then cnt = LBound(clr)
         EndIf
         y += yinc
         d += m
         If d > dy Then
            x += xinc
            d -= c
         EndIf
      Loop
   EndIf
   If clr(cnt).ChannelAlpha < 255 Then
      PokeAlpha x, y, clr(cnt)
      cnt += 1
      If cnt > UBound(clr) Then cnt = LBound(clr)
   Else
      PokeBuffer x, y, clr(cnt)
      cnt += 1
      If cnt > UBound(clr) Then cnt = LBound(clr)
   EndIf
End Sub

The code for this version is identical except that the colors come from the passed color object array. The one thing we need to watch for here is overrunning the palette array. If the line comes to the end of the palette, we wrap around to beginning for the rest of the points.

Summary

This wraps up the code for the line object. By using OOP principles we have created a flexible line object that will serve well in a host of applications. The nice part is that the majority of  coding required to get all this functionality was already done in the color and point objects. We just utilized this code and added some extensions and created a robust line object for our screen object.

Now that we have lines, we can move on to the next step, a rectangle object. Again, we can build upon what we have coded to ease the creation of the rectangle object. Not only will we create code to draw squares and rectangles on the screen, we will implement a filling function to fill our squares and rectangles with color.

You can get the FB source code here. Look for screenobject41.zip.
« Last Edit: March 02, 2010 by rdc »