Bin Packing

Displaying the Data

   You may have started to wonder how I managed to generate those nifty little graphs at the bottom of the last four pages.  Well, I'm going to tell you.  I created a UserControl that completely separates the algorithm logic, and the painting logic.  The painting logic is the same no matter what algorithm was used to generate the Bins.

   The code really is self explanatory and comes with comments.  This demonstrates the proper way to display process intensive visual data.  Notice that actual drawing is done only when needed.  When a Paint event is fired, the control is merely refreshing itself with a cached Bitmap.  This cuts way down on processing time and is really the correct way of doing things.

   Private Sub UpdateGraph()
InitDrawingSurface()
DrawBins(DrawDemarcations())

pbDrawingSurface.Refresh()
End Sub

Private Sub InitDrawingSurface()
If Me.Width = 0 Or Me.Height = 0 Then Exit Sub

bmpGraph = New Bitmap(Me.Width, Me.Height)
g = Graphics.FromImage(bmpGraph)
g.Clear(Me.BackColor)

pbDrawingSurface.Size = bmpGraph.Size
End Sub

'Returns how wide the text was on the left hand side of the graph as well as draws teh Demarcations
Private Function DrawDemarcations() As Integer
Dim TextHeight As Integer = CType(g.MeasureString(Me.BinHeight.ToString, DrawingFont).Height, _
Integer)

'100% of BinHeight
g.DrawString(Me.BinHeight.ToString, DrawingFont, DrawingTextBrush, BORDER, BORDER - 8)
'75% of BinHeight
g.DrawString((Me.BinHeight * 0.75).ToString, DrawingFont, DrawingTextBrush, BORDER, _
CType((Me.Height - BORDER * 2) * 0.25, Integer) + BORDER - TextHeight + 6)
'50% of BinHeight
g.DrawString((Me.BinHeight * 0.5).ToString, DrawingFont, DrawingTextBrush, BORDER, _
CType((Me.Height - BORDER * 2) * 0.5, Integer) + BORDER - TextHeight + 6)
'25% of BinHeight
g.DrawString((Me.BinHeight * 0.25).ToString, DrawingFont, DrawingTextBrush, BORDER, _
CType((Me.Height - BORDER * 2) * 0.75, Integer) + BORDER - TextHeight + 6)
'0% of BinHeight
g.DrawString("0", DrawingFont, DrawingTextBrush, BORDER, Me.Height - BORDER - TextHeight + 5)

'If there's decimals, Width2 will be longer, else, Width1 will be
Dim Width1 As Integer = CType(g.MeasureString(Me.BinHeight.ToString, DrawingFont).Width, Integer)
Dim Width2 As Integer = CType(g.MeasureString((Me.BinHeight * 0.75).ToString, _
DrawingFont).Width, Integer)

Return Math.Max(Width1, Width2) + 5 + BORDER
End Function

Private Sub DrawBins(ByVal StartX As Integer)
Dim BinPixelHeight As Integer = Me.Height - BORDER * 2
Dim X1, X2, Y1, Y2 As Integer
Dim i As Integer

Y1 = BORDER
Y2 = Me.Height - BORDER

If Bins Is Nothing Then Exit Sub

'Draws the vertical lines that make up the bins
For i = 0 To Bins.GetUpperBound(0) + 1
X1 = StartX + (BIN_WIDTH * i)
X2 = X1

g.DrawLine(DrawingPen, X1, Y1, X2, Y2)
Next



Dim j As Integer
Dim BinValue As Integer
Dim TotalBinHeight, TotalPixelBinHeight As Integer

For i = 0 To Bins.GetUpperBound(0) + 1
X1 = StartX + (BIN_WIDTH * i)
X2 = X1 + BIN_WIDTH

'Draws the Bins
If i < Bins.GetUpperBound(0) + 1 Then

'Draws the gradient
TotalBinHeight = 0
For j = 0 To Bins(i).GetUpperBound(0)
TotalBinHeight += Bins(i)(j)
Next

TotalPixelBinHeight = CType(TotalBinHeight / Me.BinHeight * BinPixelHeight, Integer)

If TotalPixelBinHeight > 0 Then
'Draws the Bin gradient
DrawingBinBrush = New LinearGradientBrush(New Rectangle(X1 + 1, Me.Height - _
BORDER - TotalPixelBinHeight, BIN_WIDTH - 1, TotalPixelBinHeight), _
Me.BinColor1, Me.BinColor2, LinearGradientMode.ForwardDiagonal)
DrawingBinBrush.WrapMode = WrapMode.TileFlipXY
g.FillRectangle(DrawingBinBrush, New Rectangle(X1 + 1, Me.Height - BORDER - _
TotalPixelBinHeight, BIN_WIDTH - 1, TotalPixelBinHeight))


Dim LastY As Integer
For j = 0 To Bins(i).GetUpperBound(0)
BinValue += Bins(i)(j)
Y1 = CType(BinValue / Me.BinHeight * BinPixelHeight, Integer)
Y1 = Me.Height - Y1 - BORDER
Y2 = Y1

If j = 0 Then LastY = Me.Height - BORDER

'Draws the horizontal lines
g.DrawLine(DrawingPen, X1, Y1, X2, Y2)

'Draws the Element value
Dim TextSize As SizeF = g.MeasureString(Bins(i)(j).ToString, DrawingFont)
g.DrawString(Bins(i)(j).ToString, DrawingFont, Me.DrawingBinTextBrush, _
CType(X1 + (BIN_WIDTH / 2) - (TextSize.Width / 2), Integer), _
CType(Y1 + ((LastY - Y1) / 2) - (TextSize.Height / 2), Integer))
LastY = Y1
Next

'Draws the Bin Number
g.DrawString((i + 1).ToString, DrawingFont, DrawingTextBrush, _
CType(X1 + (BIN_WIDTH / 2) - (g.MeasureString((i + 1).ToString, _
DrawingFont).Width / 2), Integer), Me.Height - BORDER)

'Draws the Bin Count
g.DrawString(TotalBinHeight.ToString, DrawingFont, DrawingBinTextBrush, _
CType(X1 + (BIN_WIDTH / 2) - (g.MeasureString(TotalBinHeight.ToString, _
DrawingFont).Width / 2), Integer), 5)

BinValue = 0
End If
End If
Next

'Bottom line
g.DrawLine(DrawingPen, StartX, Me.Height - BORDER, X1, Me.Height - BORDER)
End Sub

Complete code may be found as a Resource, downloadable at the top of any page.

You might also like...

Comments

Contribute

Why not write for us? Or you could submit an event or a user group in your area. Alternatively just tell us what you think!

Our tools

We've got automatic conversion tools to convert C# to VB.NET, VB.NET to C#. Also you can compress javascript and compress css and generate sql connection strings.

“Engineers are all basically high-functioning autistics who have no idea how normal people do stuff.” - Cory Doctorow