IMHO, drawing graph on the window itself is an outdated HMG graphics functionality.
The permanent solution for drawing graph is using the ultimate and versatile Bos Taurus library.
I have tried to convert the existing draw graph function ( only BAR type and not 3D) using BT library and saved the graph in a bitmap file. That bitmap can be used to show or print.
May be we can extend this to all other types and we have a NEW graph functionality to save the graph easily to a bitmap in HMG.
Code: Select all
#include <hmg.ch>
Function Main
define window graph at 0, 0 width 1000 height 700 main
define button ok
row 10
col 10
width 80
caption 'Draw'
action drawgraph()
end button
define image grapharea
row 50
col 50
width 800
height 600
end image
end window
graph.center
graph.activate
Return
function drawgraph
local cImage := 'graph.bmp'
local nImageWidth := 800
local nImageHeight := 600
local cTitle := 'Sample Graph'
local aYValues := {"Jan","Feb","Mar","Apr","May"}
local aData := { {14280,20420,12870,25347, 7640},;
{ 8350,10315,15870, 5347,12340},;
{12345, -8945,10560,15600,17610} }
local nBarDepth := 15
local nBarWidth := 15
local nHValues := 5
local l3D := .f.
local lGrid := .t.
local lXGrid := .t.
local lYGrid := .t.
local lXVal := .t.
local lYVal := .t.
local nGraphType := BARS
local lViewVal := .t.
local lLegends := .t.
local cPicture := '99,999.99'
local aSeries := {"Serie 1","Serie 2","Serie 3"}
local aColors := { {128,128,255}, {255,102, 10}, {55,201, 48} }
local lNoBorder := .f.
local nLegendWidth := 50
graphsave( cImage, nImageWidth, nImageHeight, aData, cTitle, aYValues, nBarDepth, nBarWidth, nil, nHValues, l3d, lGrid, lXGrid, lYGrid, ;
lXVal, lYVal, lLegends, aSeries, aColors, nGraphType, lViewVal, cPicture, nLegendWidth ,lNoBorder )
graph.grapharea.picture := 'graph.bmp'
return nil
function graphsave( cImageFileName, nWidth, nHeight, aData, cTitle, aYVals, nBarD, nWideB, nSep, nXRanges, ;
l3D, lGrid, lxGrid, lyGrid, lxVal, lyVal, lLegends, aSeries, aColors, nType, lViewVal, cPicture , nLegendWindth , lNoborder )
LOCAL nI, nJ, nPos, nMax, nMin, nMaxBar, nDeep
LOCAL nRange, nResH, nResV, nWide, aPoint, cName
LOCAL nXMax, nXMin, nHigh, nRel, nZero, nRPos, nRNeg
local hBitMap, hDC, BTStruct
local nTop := 0
local nLeft := 0
local nBottom := nHeight
local nRight := nWidth
DEFAULT cTitle := ""
DEFAULT nSep := 0
DEFAULT cPicture := "999,999.99"
DEFAULT nLegendWindth := 50
private nPenWidth := 0.25
If ( HMG_LEN (aSeries) != HMG_LEN ( aData ) ) .or. ;
( HMG_LEN ( aSeries ) != HMG_LEN ( aColors ) )
MsgHMGError("DRAW GRAPH: 'Series' / 'SerieNames' / 'Colors' arrays size mismatch. Program terminated", "HMG Error" )
EndIf
hBitMap := BT_BitmapCreateNew ( nWidth, nHeight, { 255, 255, 255 } )
hDC := BT_CreateDC( hBitMap, BT_HDC_BITMAP, @BTStruct )
IF lGrid
lxGrid := lyGrid := .T.
ENDIF
IF nBottom <> NIL .AND. nRight <> NIL
nHeight := nBottom - nTop / 2
nWidth := nRight - nLeft / 2
nBottom -= IF( lyVal, 42, 32)
nRight -= IF( lLegends, 32 + nLegendWindth , 32 )
ENDIF
nTop += 1 + IF( Empty( cTitle ), 30, 44 ) // Top gap
nLeft += 1 + IF( lxVal, 80 + nBarD, 30 + nBarD ) // LEFT
DEFAULT nBottom := nHeight - 2 - IF( lyVal, 40, 30 ) // Bottom
DEFAULT nRight := nWidth - 2 - IF( lLegends, 30 + nLegendWindth , 30 ) // RIGHT
l3D := IF( nType == POINTS, .F., l3D )
nDeep := IF( l3D, nBarD, 1 )
nMaxBar := nBottom - nTop - nDeep - 5
nResH := nResV := 1
nWide := ( nRight - nLeft )* nResH / ( nMax( aData ) + 1 ) * nResH
// Graph area
//
IF ! lNoborder
DrawWindowBoxInBitMap( hDC, Max( 1, nTop - 44 ), Max( 1, nLeft - 80 - nBarD ), nHeight - 1, nWidth - 1 )
ENDIF
// Back area
//
IF l3D
drawrectinbitmap( hDC, nTop+1, nLeft, nBottom - nDeep, nRight, { 255, 255, 255 } )
ELSE
drawrectinbitmap( hDC, nTop-5, nLeft, nBottom, nRight, { 255, 255, 255 } )
ENDIF
IF l3D
// Bottom area
FOR nI := 1 TO nDeep+1
DrawLineinbitmap( hDC, nBottom-nI, nLeft-nDeep+nI, nBottom-nI, nRight-nDeep+nI, { 255, 255, 255 } )
NEXT nI
// Lateral
FOR nI := 1 TO nDeep
DrawLineinbitmap( hDC, nTop+nI, nLeft-nI, nBottom-nDeep+nI, nLeft-nI, {192, 192, 192} )
NEXT nI
// Graph borders
FOR nI := 1 TO nDeep+1
DrawLineinbitmap( hDC, nBottom-nI ,nLeft-nDeep+nI-1 ,nBottom-nI ,nLeft-nDeep+nI ,GRAY )
DrawLineinbitmap( hDC, nBottom-nI ,nRight-nDeep+nI-1,nBottom-nI ,nRight-nDeep+nI ,BLACK )
DrawLineinbitmap( hDC, nTop+nDeep-nI+1,nLeft-nDeep+nI-1 ,nTop+nDeep-nI+1,nLeft-nDeep+nI ,BLACK )
DrawLineinbitmap( hDC, nTop+nDeep-nI+1,nLeft-nDeep+nI-3 ,nTop+nDeep-nI+1,nLeft-nDeep+nI-2,BLACK )
NEXT nI
FOR nI=1 TO nDeep+2
DrawLineinbitmap( hDC, nTop+nDeep-nI+1,nLeft-nDeep+nI-3,nTop+nDeep-nI+1,nLeft-nDeep+nI-2 ,BLACK )
DrawLineinbitmap( hDC, nBottom+ 2-nI+1,nRight-nDeep+nI ,nBottom+ 2-nI+1,nRight-nDeep+nI-2,BLACK )
NEXT nI
DrawLineinbitmap( hDC, nTop ,nLeft ,nTop ,nRight ,BLACK )
DrawLineinbitmap( hDC, nTop- 2 ,nLeft ,nTop- 2 ,nRight+ 2 ,BLACK )
DrawLineinbitmap( hDC, nTop ,nLeft ,nBottom-nDeep ,nLeft ,GRAY )
DrawLineinbitmap( hDC, nTop+nDeep ,nLeft-nDeep ,nBottom ,nLeft-nDeep ,BLACK )
DrawLineinbitmap( hDC, nTop+nDeep ,nLeft-nDeep-2,nBottom+ 2 ,nLeft-nDeep-2,BLACK )
DrawLineinbitmap( hDC, nTop ,nRight ,nBottom-nDeep ,nRight ,BLACK )
DrawLineinbitmap( hDC, nTop- 2 ,nRight+ 2 ,nBottom-nDeep+2,nRight+ 2 ,BLACK )
DrawLineinbitmap( hDC, nBottom-nDeep,nLeft ,nBottom-nDeep ,nRight ,GRAY )
DrawLineinbitmap( hDC, nBottom ,nLeft-nDeep ,nBottom ,nRight-nDeep ,BLACK )
DrawLineinbitmap( hDC, nBottom+ 2 ,nLeft-nDeep-2,nBottom+ 2 ,nRight-nDeep ,BLACK )
ENDIF
// Graph info
//
IF !Empty(cTitle)
DrawTextInBitmap( hDC, nTop - 30 * nResV, nLeft, cTitle, 'Arial', 12, RED, 2 )
ENDIF
// Legends
//
IF lLegends
nPos := nTop
FOR nI := 1 TO HMG_LEN( aSeries )
DrawBarinbitmap( hDC, nRight+(8*nResH), nPos+(9*nResV), 8*nResH, 7*nResV, l3D, 1, aColors[nI] )
DrawTextInBitmap( hDC, nPos, nRight+(20*nResH), aSeries[nI], 'Arial', 8, BLACK, 0 )
nPos += 18*nResV
NEXT nI
ENDIF
// Max, Min values
nMax := 0
FOR nJ := 1 TO HMG_LEN(aSeries)
FOR nI :=1 TO HMG_LEN(aData[nJ])
nMax := Max( aData[nJ][nI], nMax )
NEXT nI
NEXT nJ
nMin := 0
FOR nJ := 1 TO HMG_LEN(aSeries)
FOR nI :=1 TO HMG_LEN(aData[nJ])
nMin := Min( aData[nJ][nI], nMin )
NEXT nI
NEXT nJ
nXMax := IF( nMax > 0, DetMaxVal( nMax ), 0 )
nXMin := IF( nMin < 0, DetMaxVal( nMin ), 0 )
nHigh := nXMax + nXMin
nMax := Max( nXMax, nXMin )
nRel := ( nMaxBar / nHigh )
nMaxBar := nMax * nRel
nZero := nTop + (nMax*nRel) + nDeep + 5 // Zero pos
IF l3D
FOR nI := 1 TO nDeep+1
DrawLineinbitmap( hDC, nZero-nI+1, nLeft-nDeep+nI , nZero-nI+1, nRight-nDeep+nI, {192, 192, 192} )
NEXT nI
FOR nI := 1 TO nDeep+1
DrawLineinbitmap( hDC, nZero-nI+1, nLeft-nDeep+nI-1 , nZero-nI+1, nLeft -nDeep+nI, GRAY )
DrawLineinbitmap( hDC, nZero-nI+1, nRight-nDeep+nI-1, nZero-nI+1, nRight-nDeep+nI, BLACK )
NEXT nI
DrawLineinbitmap( hDC, nZero-nDeep, nLeft, nZero-nDeep, nRight, GRAY )
ENDIF
aPoint := Array( HMG_LEN( aSeries ), HMG_LEN( aData[1] ), 2 )
nRange := nMax / nXRanges
// xLabels
nRPos := nRNeg := nZero - nDeep
FOR nI := 0 TO nXRanges
IF lxVal
IF nRange*nI <= nXMax
DrawTextInBitmap( hDC, nRPos, nLeft-nDeep-70, Transform(nRange*nI, cPicture), 'Arial', 8, BLUE )
ENDIF
IF nRange*(-nI) >= nXMin*(-1)
DrawTextInBitmap( hDC, nRNeg, nLeft-nDeep-70, Transform(nRange*-nI, cPicture), 'Arial', 8, BLUE )
ENDIF
ENDIF
IF lxGrid
IF nRange*nI <= nXMax
IF l3D
FOR nJ := 0 TO nDeep + 1
DrawLineinbitmap( hDC, nRPos + nJ, nLeft - nJ - 1, nRPos + nJ, nLeft - nJ, BLACK )
NEXT nJ
ENDIF
DrawLineinbitmap( hDC, nRPos, nLeft, nRPos, nRight, BLACK )
ENDIF
IF nRange*-nI >= nXMin*-1
IF l3D
FOR nJ := 0 TO nDeep + 1
DrawLineinbitmap( hDC, nRNeg + nJ, nLeft - nJ - 1, nRNeg + nJ, nLeft - nJ, BLACK )
NEXT nJ
ENDIF
DrawLineinbitmap( hDC, nRNeg, nLeft, nRNeg, nRight, BLACK )
ENDIF
ENDIF
nRPos -= ( nMaxBar / nXRanges )
nRNeg += ( nMaxBar / nXRanges )
NEXT nI
IF lYGrid
nPos:=IF(l3D, nTop, nTop-5 )
nI := nLeft + nWide
FOR nJ := 1 TO nMax(aData)
DrawlineinBitmap( hDC, nBottom-nDeep, nI, nPos, nI, {100,100,100} )
DrawlineinBitmap( hDC, nBottom, nI-nDeep, nBottom-nDeep, nI, {100,100,100} )
nI += nWide
NEXT
ENDIF
DO WHILE .T. // Bar adjust
nPos = nLeft + ( nWide / 2 )
nPos += ( nWide + nSep ) * ( HMG_LEN(aSeries) + 1 ) * HMG_LEN(aData[1])
IF nPos > nRight
nWide--
ELSE
EXIT
ENDIF
ENDDO
nMin := nMax / nMaxBar
nPos := nLeft + ( ( nWide + nSep ) / 2 ) // first point graph
nRange := ( ( nWide + nSep ) * HMG_LEN(aSeries) ) / 2
IF lyVal .AND. HMG_LEN( aYVals ) > 0 // Show yLabels
nWideB := ( nRight - nLeft ) / ( nMax(aData) + 1 )
nI := nLeft + nWideB
FOR nJ := 1 TO nMax(aData)
cName := "yVal_Name_"+LTRIM(STR(nJ))
DrawTextInBitmap( hDC, nBottom + 8, nI - nDeep - IF(l3D, 0, 8), aYVals[nJ], 'Arial', 8, BLUE )
nI += nWideB
NEXT
ENDIF
// Bars
//
IF nType == BARS
if nMin <> 0
nPos := nLeft + ( ( nWide + nSep ) / 2 )
FOR nI=1 TO HMG_LEN(aData[1])
FOR nJ=1 TO HMG_LEN(aSeries)
DrawBarinbitmap( hDC, nPos, nZero, aData[nJ,nI] / nMin + nDeep, nWide, l3D, nDeep, aColors[nJ] )
nPos += nWide+nSep
NEXT nJ
nPos += nWide+nSep
NEXT nI
endif
ENDIF
BT_BitmapSaveFile(hBitmap, cImageFileName )
BT_DeleteDC( BTstruct )
BT_BitmapRelease( hBitmap )
&& // Lines
&& //
&& IF nType == LINES
&& if nMin <> 0
&& nWideB := ( nRight - nLeft ) / ( nMax(aData) + 1 )
&& nPos := nLeft + nWideB
&& FOR nI := 1 TO HMG_LEN(aData[1])
&& FOR nJ=1 TO HMG_LEN(aSeries)
&& IF !l3D
&& DrawPoint( parent, nType, nPos, nZero, aData[nJ,nI] / nMin + nDeep, aColors[nJ] )
&& ENDIF
&& aPoint[nJ,nI,2] := nPos
&& aPoint[nJ,nI,1] := nZero - ( aData[nJ,nI] / nMin + nDeep )
&& NEXT nJ
&& nPos += nWideB
&& NEXT nI
&& FOR nI := 1 TO HMG_LEN(aData[1])-1
&& FOR nJ := 1 TO HMG_LEN(aSeries)
&& IF l3D
&& drawpolygon(parent,{{aPoint[nJ,nI,1],aPoint[nJ,nI,2]},{aPoint[nJ,nI+1,1],aPoint[nJ,nI+1,2]}, ;
&& {aPoint[nJ,nI+1,1]-nDeep,aPoint[nJ,nI+1,2]+nDeep},{aPoint[nJ,nI,1]-nDeep,aPoint[nJ,nI,2]+nDeep}, ;
&& {aPoint[nJ,nI,1],aPoint[nJ,nI,2]}},,,aColors[nJ])
&& ELSE
&& DrawLine(parent,aPoint[nJ,nI,1],aPoint[nJ,nI,2],aPoint[nJ,nI+1,1],aPoint[nJ,nI+1,2],aColors[nJ])
&& ENDIF
&& NEXT nI
&& NEXT nI
&& endif
&& ENDIF
&& // Points
&& //
&& IF nType == POINTS
&& if nMin <> 0
&& nWideB := ( nRight - nLeft ) / ( nMax(aData) + 1 )
&& nPos := nLeft + nWideB
&& FOR nI := 1 TO HMG_LEN(aData[1])
&& FOR nJ=1 TO HMG_LEN(aSeries)
&& DrawPoint( parent, nType, nPos, nZero, aData[nJ,nI] / nMin + nDeep, aColors[nJ] )
&& aPoint[nJ,nI,2] := nPos
&& aPoint[nJ,nI,1] := nZero - aData[nJ,nI] / nMin
&& NEXT nJ
&& nPos += nWideB
&& NEXT nI
&& ENDIF
&& IF lViewVal
&& IF nType == BARS
&& nPos := nLeft + nWide + ( (nWide+nSep) * ( HMG_LEN(aSeries) / 2 ) )
&& ELSE
&& nWideB := ( nRight - nLeft ) / ( nMax(aData) + 1 )
&& nPos := nLeft + nWideB
&& ENDIF
&& FOR nI := 1 TO HMG_LEN(aData[1])
&& FOR nJ := 1 TO HMG_LEN(aSeries)
&& cName := "Data_Name_"+LTRIM(STR(nI))+LTRIM(STR(nJ))
&& @ nZero - ( aData[nJ,nI] / nMin + nDeep ), IF(nType == BARS, nPos - IF(l3D, 8, 10), nPos + 10) ;
&& LABEL &cName OF &parent ;
&& VALUE Transform(aData[nJ,nI], cPicture) AUTOSIZE ;
&& FONT "Arial" SIZE 8 BOLD TRANSPARENT
&& nPos+=IF( nType == BARS, nWide + nSep, 0)
&& NEXT nJ
&& IF nType == BARS
&& nPos += nWide + nSep
&& ELSE
&& nPos += nWideB
&& ENDIF
&& NEXT nI
&& ENDIF
&& IF l3D
&& DrawLine( parent, nZero, nLeft-nDeep, nZero, nRight-nDeep, BLACK )
&& ELSE
&& IF nXMax<>0 .AND. nXMin<>0
&& DrawLine( parent, nZero-1, nLeft-2, nZero-1, nRight, RED )
&& ENDIF
&& endif
&& ENDIF
RETURN
function DrawWindowBoxInBitMap( hDC, row, col, rowr, colr )
BT_DrawRectangle ( hDC, Row, Col, Colr - col, rowr - row, { 0, 0, 0 }, nPenWidth )
return nil
function drawrectinbitmap( hDC, row, col, row1, col1, aColor )
BT_DrawFillRectangle (hDC, Row, Col, col1 - col, row1 - row, aColor, aColor, nPenWidth )
return nil
function DrawLineinbitmap( hDC, Row1, Col1, Row2, Col2, aColor )
BT_DrawLine ( hDC, Row1, Col1, Row2, Col2, aColor, nPenWidth )
return nil
function DrawTextInBitmap( hDC, Row, Col, cText, cFontName, nFontSize, aColor, nAlign )
default nAlign := 0
do case
case nAlign == 0
BT_DrawText ( hDC, Row, Col, cText, cFontName, nFontSize, aColor, )
case nAlign == 1
BT_DrawText ( hDC, Row, Col, cText, cFontName, nFontSize, aColor, , BT_TEXT_RIGHT )
case nAlign == 2
BT_DrawText ( hDC, Row, Col, cText, cFontName, nFontSize, aColor, , BT_TEXT_CENTER )
endcase
return nil
function DrawBarinbitmap( hDC, nY, nX, nHigh, nWidth, l3D, nDeep, aColor )
LOCAL nI, nColTop, nShadow, nH := nHigh
&& nColTop := ClrShadow( RGB(aColor[1],aColor[2],aColor[3]), 15 )
&& nShadow := ClrShadow( nColTop, 15 )
&& nColTop := {GetRed(nColTop),GetGreen(nColTop),GetBlue(nColTop)}
&& nShadow := {GetRed(nShadow),GetGreen(nShadow),GetBlue(nShadow)}
FOR nI=1 TO nWidth
DrawLineinbitmap( hDC, nX, nY+nI, nX+nDeep-nHigh, nY+nI, aColor ) // front
NEXT nI
&& IF l3D
&& // Lateral
&& drawpolygoninBitmap( parent,{{nX-1,nY+nWidth+1},{nX+nDeep-nHigh,nY+nWidth+1},;
&& {nX-nHigh+1,nY+nWidth+nDeep},{nX-nDeep,nY+nWidth+nDeep},;
&& {nX-1,nY+nWidth+1}},nShadow,,nShadow )
&& // Superior
&& nHigh := Max( nHigh, nDeep )
&& drawpolygon( parent,{{nX-nHigh+nDeep,nY+1},{nX-nHigh+nDeep,nY+nWidth+1},;
&& {nX-nHigh+1,nY+nWidth+nDeep},{nX-nHigh+1,nY+nDeep},;
&& {nX-nHigh+nDeep,nY+1}},nColTop,,nColTop )
&& // Border
&& DrawBox( parent, nY, nX, nH, nWidth, l3D, nDeep )
&& ENDIF
RETURN
STATIC FUNCTION nMax(aData)
LOCAL nI, nMax := 0
FOR nI :=1 TO HMG_LEN( aData )
nMax := Max( HMG_LEN(aData[nI]), nMax )
NEXT nI
RETURN( nMax )
STATIC FUNCTION DetMaxVal(nNum)
LOCAL nE, nMax, nMan, nVal, nOffset
nE:=9
nVal:=0
nNum:=Abs(nNum)
DO WHILE .T.
nMax := 10**nE
IF Int(nNum/nMax)>0
nMan:=(nNum/nMax)-Int(nNum/nMax)
nOffset:=1
nOffset:=IF(nMan<=.75,.75,nOffset)
nOffset:=IF(nMan<=.50,.50,nOffset)
nOffset:=IF(nMan<=.25,.25,nOffset)
nOffset:=IF(nMan<=.00,.00,nOffset)
nVal := (Int(nNum/nMax)+nOffset)*nMax
EXIT
ENDIF
nE--
ENDDO
RETURN (nVal)