The following code will illustrate how to build a bitmap in memory and then write it out to a .JPG file Please note that you will not be able to compile this code as is. You will have to take the "concepts" and use them to make your own routine. This code is used to plot and draw graphs using different color lines. It creates axis and grid lines etc... It also uses different fonts as well. This function took me days to complete because there is limited information available on the topic. This function is used in an XB2NET application that creates these graphs on the fly and delivers the PDF files back to a users browser. Regards, Jim Graham Static Function DrawPowerTapGraph(paRecords,pcDesc,pcTempFile,pnScaling,pcZones) Local loPS := XbpPresSpace():New():Create() Local loBmp := XbpBitmap():New():Create(loPS) Local lnI := 1 Local lnII := 1 Local laAttributes := Nil Local laDescriptions := { "Time", "Tourque" , "Miles/Hour" , "Watts" , "Distance" , "Cadence", "HR" , "ID" } Local lnMin := 0 Local lnMax := 0 Local lnTotal := 0 Local lnTotal_ := 0 Local lnTotalTime := 0 Local lnTotalDistance := 0 Local lcTitle1 := "Data Graphed: " + pcDesc Local loFont := Nil Local lnLegendHeight := 200 Local lnSeconds := Nil Local lnLeftMargin := 100 Local lnBoxWidth := 775 Local lnX_Max := Len(paRecords) / pnScaling Local lnY_Max := 0 Local lnX := lnX_Max + (DEF_OFFSET*2) Local lnY := 0 Local lbSmoothing := .F. Local laZones := {} Local lnSampleRate := paRecords[1][1] * 60 // Sampling rate Local lbHeartRate := .F. Local lnLap := 0 Local laIntervals := { {{0,0},{0,0},{0,0},{0,0},{0,0}} } Local lnInterval := 0 Local lnCurrentInterval := 1 Local lcZone AAdd(laZones , {0, 0 ,"0" } ) //#Define DEF_ZONE0_ARRAY 1 //AAdd(laZones , {PROFILES->MAX_HR * (PROFILES->ZONE1 * .01), 0 ,"1" } ) //#Define DEF_ZONE1_ARRAY 2 AAdd(laZones , {1, 0 ,"1" } ) //#Define DEF_ZONE1_ARRAY 2 AAdd(laZones , {PROFILES->MAX_HR * (PROFILES->ZONE2 * .01), 0 ,"2" } ) //#Define DEF_ZONE2_ARRAY 3 AAdd(laZones , {PROFILES->MAX_HR * (PROFILES->ZONE3 * .01), 0 ,"3" } ) //#Define DEF_ZONE3_ARRAY 4 AAdd(laZones , {PROFILES->MAX_HR * (PROFILES->ZONE4 * .01), 0 ,"4" } ) //#Define DEF_ZONE4_ARRAY 5 AAdd(laZones , {PROFILES->MAX_HR * (PROFILES->ZONE5 * .01), 0 ,"5" } ) //#Define DEF_ZONE5_ARRAY 6 AAdd(laZones , {PROFILES->MAX_HR * (PROFILES->ZONE6 * .01), 0 ,"6" } ) //#Define DEF_ZONE6_ARRAY 7 // Sampling rate is every 2.5 seconds... // Find Total Time, and the Min/Max values use to draw the Axis's For lnI := 1 To Len(paRecords) If paRecords[lnI][DEF_HEART_RATE] > 0 lbHeartRate := .T. Endif If paRecords[lnI][5] > lnTotalDistance lnTotalDistance := paRecords[lnI][5] Endif If paRecords[lnI][1] > lnTotalTime lnTotalTime := paRecords[lnI][1] Endif If paRecords[lnI][DEF_DATAPOINT1] > lnY_MAX lnY_MAX := paRecords[lnI][DEF_DATAPOINT1] Endif If paRecords[lnI][DEF_DATAPOINT2] > lnY_MAX lnY_MAX := paRecords[lnI][DEF_DATAPOINT2] Endif If paRecords[lnI][DEF_DATAPOINT3] > lnY_MAX lnY_MAX := paRecords[lnI][DEF_DATAPOINT3] Endif If paRecords[lnI][DEF_INTERVAL] > (lnCurrentInterval - 1) AAdd( laIntervals , {{0,0},{0,0},{0,0},{0,0},{0,0}} ) lnCurrentInterval ++ Endif If paRecords[lnI][DEF_DATAPOINT1] > 0 // MPH laIntervals[lnCurrentInterval][1][1] ++ laIntervals[lnCurrentInterval][1][2] += paRecords[lnI][DEF_DATAPOINT1] Endif If paRecords[lnI][DEF_DATAPOINT2] > 0 // Cadence laIntervals[lnCurrentInterval][2][1] ++ laIntervals[lnCurrentInterval][2][2] += paRecords[lnI][DEF_DATAPOINT2] Endif // Watts laIntervals[lnCurrentInterval][3][1] ++ laIntervals[lnCurrentInterval][3][2] += paRecords[lnI][DEF_DATAPOINT3] // Tourque laIntervals[lnCurrentInterval][4][1] ++ laIntervals[lnCurrentInterval][4][2] += paRecords[lnI][DEF_DATAPOINT4] If paRecords[lnI][DEF_DATAPOINT5] > 0 // Heart Rate laIntervals[lnCurrentInterval][5][1] ++ laIntervals[lnCurrentInterval][5][2] += paRecords[lnI][DEF_DATAPOINT5] Endif If paRecords[lnI,DEF_HEART_RATE] == 0 // Zone 0 laZones[DEF_ZONE0_ARRAY,2] += lnSampleRate Loop Endif If paRecords[lnI,DEF_HEART_RATE] >= laZones[DEF_ZONE1_ARRAY,1] .AND. paRecords[lnI][DEF_HEART_RATE] < laZones[DEF_ZONE2_ARRAY,1] // Zone 1 laZones[DEF_ZONE1_ARRAY,2] += lnSampleRate Loop Endif If paRecords[lnI,DEF_HEART_RATE] >= laZones[DEF_ZONE2_ARRAY,1] .AND. paRecords[lnI][DEF_HEART_RATE] < laZones[DEF_ZONE3_ARRAY,1] // Zone 2 laZones[DEF_ZONE2_ARRAY,2] += lnSampleRate Loop Endif If paRecords[lnI,DEF_HEART_RATE] >= laZones[DEF_ZONE3_ARRAY,1] .AND. paRecords[lnI][DEF_HEART_RATE] < laZones[DEF_ZONE4_ARRAY,1] // Zone 3 laZones[DEF_ZONE3_ARRAY,2] += lnSampleRate Loop Endif If paRecords[lnI,DEF_HEART_RATE] >= laZones[DEF_ZONE4_ARRAY,1] .AND. paRecords[lnI][DEF_HEART_RATE] < laZones[DEF_ZONE5_ARRAY,1] // Zone 4 laZones[DEF_ZONE4_ARRAY,2] += lnSampleRate Loop Endif If paRecords[lnI][DEF_HEART_RATE] >= laZones[DEF_ZONE5_ARRAY,1] .AND. paRecords[lnI][DEF_HEART_RATE] < laZones[DEF_ZONE6_ARRAY,1] // Zone 5 laZones[DEF_ZONE5_ARRAY,2] += lnSampleRate Loop Endif If paRecords[lnI,DEF_HEART_RATE] >= laZones[DEF_ZONE6_ARRAY,1] // Zone 6 laZones[DEF_ZONE6_ARRAY,2] += lnSampleRate Endif Next lnI If Len(laIntervals) > 1 pcZones += Padl("Averages:",25) + DEF_CRLF + DEF_CRLF pcZones += "Interval" + Space(1) + "Speed" + Space(1) + "Cadence" + Space(1) + "Watts" + Space(1) + "Tourque" + Space(1) + " HR" + DEF_CRLF pcZones += Replicate("-",8) + Space(1) + Replicate("-",5) + Space(1) + Replicate("-",7) + Space(1) + Replicate("-",5) + Space(1) + Replicate("-",8) + Space(1) + Replicate("-",4) + DEF_CRLF For lnI := 1 To Len(laIntervals) If laIntervals[lnI][1][1] == 0 Loop Endif lnInterval ++ pcZones += Str(lnInterval,8) + Space(1) If laIntervals[lnI][1][1] > 0 pcZones += Str( laIntervals[lnI][1][2]/laIntervals[lnI][1][1] , 5 , 2 ) + Space(1) //DCQOUT "Average Speed" Else pcZones += Space(6) Endif If laIntervals[lnI][2][1] > 0 pcZones += Str(laIntervals[lnI][2][2]/laIntervals[lnI][2][1] , 7) + Space(1) //DCQOUT "Average Cadence" Else pcZones += Space(8) Endif If laIntervals[lnI][3][1] > 0 pcZones += Str( laIntervals[lnI][3][2]/laIntervals[lnI][3][1] , 5) + Space(1) //DCQOUT "Average Watts" Else pcZones += Space(6) Endif If laIntervals[lnI][4][1] > 0 pcZones += Str( laIntervals[lnI][4][2]/laIntervals[lnI][4][1] , 7) + Space(1) //DCQOUT "Average Tourque" Else pcZones += Space(8) Endif If laIntervals[lnI][5][1] > 0 pcZones += Str( laIntervals[lnI][5][2]/laIntervals[lnI][5][1] , 4) + Space(1) //DCQOUT "Average Heart Rate" Else pcZones += Space(5) Endif pcZones += DEF_CRLF Next lnI Endif lnSeconds := lnSampleRate * Len(paRecords) //DCQOUT laZones If lbHeartRate pcZones += DEF_CRLF pcZones += DEF_CRLF pcZones += PadC("Zone",8) + Space(2) + "Time MM:SS" + Space(2) + "Percent" + Space(2) + "BPM Range" + DEF_CRLF pcZones += "--------" + Space(2) + "----------" + Space(2) + "-------" + Space(2) + "---------"+ DEF_CRLF For lnI := 1 To Len(laZones) Do Case Case lnI = 1 lcZone := " No Data" Case lnI = 2 lcZone := Str( PROFILES->MAX_HR * (PROFILES->ZONE1 * .01),3) + " - " + Str( (PROFILES->MAX_HR * ( PROFILES->ZONE2 * .01) - 1 ) ,3) Case lnI = 3 lcZone := Str( PROFILES->MAX_HR * (PROFILES->ZONE2 * .01),3) + " - " + Str( (PROFILES->MAX_HR * ( PROFILES->ZONE3 * .01) - 1 ) ,3) Case lnI = 4 lcZone := Str( PROFILES->MAX_HR * (PROFILES->ZONE3 * .01),3) + " - " + Str( (PROFILES->MAX_HR * ( PROFILES->ZONE4 * .01) - 1 ) ,3) Case lnI = 5 lcZone := Str( PROFILES->MAX_HR * (PROFILES->ZONE4 * .01),3) + " - " + Str( (PROFILES->MAX_HR * ( PROFILES->ZONE5 * .01) - 1 ) ,3) Case lnI = 6 lcZone := Str( PROFILES->MAX_HR * (PROFILES->ZONE5 * .01),3) + " - " + Str( (PROFILES->MAX_HR * ( PROFILES->ZONE6 * .01) - 1 ) ,3) Case lnI = 7 lcZone := Str( PROFILES->MAX_HR * (PROFILES->ZONE6 * .01),3) + " - " + Str( PROFILES->MAX_HR ,3) EndCase pcZones += PadC( laZones[lnI,3] ,8) + Space(2) + PadL( SecondsToElapsedTime(laZones[lnI,2],.T.) , 10) + Space(2) + PadL( Str( (laZones[lnI,2] / lnSeconds) * 100 , 6 , 2 ) +"%",7) + Space(2) + lcZone + DEF_CRLF lnTotal += laZones[lnI,2] Next lnI pcZones += Space(10) + "----------" + Space(2) + "-------" + DEF_CRLF pcZones += Space(10) + PadL( SecondsToElapsedTime( lnTotal ,.T.) , 10) + Space(2) + PadL( Str( ( lnTotal / lnSeconds) * 100 , 6 , 2 ) +"%",7) + DEF_CRLF Else pcZones := "There was no Heart Rate data for this workout" Endif lnY := lnY_Max + DEF_OFFSET + lnLegendHeight Begin Sequence loBMP:PresSpace(loPS) // Bind the presentation space to the BMP loBmp:Make( lnX , lnY ) // Set the size that you want it to be. // You must fill the presentation space with the color you want for background color. // The default is black! GraSetColor( loPS, GRA_CLR_BACKGROUND , GRA_CLR_BLUE ) GraBox( loPS, {1,1},{lnX-2 , lnY-2} , GRA_OUTLINEFILL ) laAttributes := Array( GRA_AL_COUNT ) laAttributes[GRA_AL_COLOR] := GRA_CLR_BLACK // Set Box line colors laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Box line type laAttributes[GRA_AL_WIDTH] := GRA_LINEWIDTH_THICK // Set Box line width GraSetAttrLine( loPS, laAttributes ) GraBox( loPS, { lnLeftMargin + DEF_OFFSET , lnY - 75 }, {lnBoxWidth, lnY - 160 } ) // Outlined rectangle laAttributes := Array( GRA_AL_COUNT ) laAttributes[GRA_AL_COLOR] := GRA_CLR_BLACK // Set line colors laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set line type laAttributes[GRA_AL_WIDTH] := GRA_LINEWIDTH_NORMAL // Set line width GraSetAttrLine( loPS, laAttributes ) GraLine( loPS, {lnLeftMargin + DEF_OFFSET + 1, lnY - 100 }, {lnBoxWidth , lnY - 100 } ) // Create & set the font for the Graph Title loFont := XbpFont():New(loPS) // Create font object loFont:Create(FONT_DEFPROP_MEDIUM + FONT_STYLE_BOLD) // Create the object GraSetFont ( loPS , loFont ) // Set current font // Print the Graph Description laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK // Set Label line color GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, { lnLeftMargin + 100 , lnY - 25 }, lcTitle1 ) // Print the Graph Description laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK // Set Label line color GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 50 , lnY - 50 }, "Total ride time: " + SecondsToElapsedTime( lnSeconds ) + " " ) laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK // Set Label line color GraSetAttrString( loPS, laAttributes ) //GraStringAt( loPS, {lnLeftMargin + 250, lnY - 50 }, "Total ride distance: " + AllTrim(Str(Round( lnTotalDistance, 2))) + " miles" ) GraStringAt( loPS, Nil , "Total ride distance: " + AllTrim(Str(Round( lnTotalDistance, 2))) + " miles " ) //GraStringAt( loPS, {lnLeftMargin + 550, lnY - 50 }, "Scaled: " + AllTrim(Str(Int( pnScaling ))) ) If pnScaling > 1 GraStringAt( loPS, Nil , "Scaled down: " + AllTrim(Str(Int( pnScaling - 1 ))) + "X" ) Endif // Create & set the font for all of the Axis and other text. loFont := XbpFont():New(loPS) // Create font object loFont:Create(FONT_DEFPROP_SMALL) // Create the object GraSetFont ( loPS , loFont ) // Set current font laAttributes[GRA_AM_COLOR] := GRA_CLR_WHITE // Set Grid line colors laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DOT // Set Axis line type GraSetAttrLine( loPS, laAttributes ) // Draw Grid lines and Y-Axis labels laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK // Set Graph line color GraSetAttrString( loPS, laAttributes ) For lnI := 0 To lnY_Max Step 25 GraStringAt( loPS, {5,lnI + DEF_OFFSET }, PadL(AllTrim(Str(lnI)),3) ) If lnI > 0 GraLine( loPS, {25,lnI + DEF_OFFSET}, {lnX_Max + DEF_OFFSET ,lnI + DEF_OFFSET} ) // Grid lines - moved to right by one pixel Endif Next lnI // Draw X-Axis labels For lnI := 0 To lnTotalTime Step 5 GraStringAt( loPS, { lnX_Max * (lnI/lnTotalTime) + DEF_OFFSET , 30 }, AllTrim(Str(lnI)) ) If lnI > 0 // Draw verticle grid lines GraLine( loPS, {lnX_Max * (lnI/lnTotalTime) + DEF_OFFSET, DEF_OFFSET + 1 }, { lnX_Max * (lnI/lnTotalTime) + DEF_OFFSET,lnY_Max + DEF_OFFSET } ) // Draw Verticle Axis Endif Next lnI // Draw final verticle grid line GraLine( loPS, {lnX_Max + DEF_OFFSET, DEF_OFFSET + 1 }, { lnX_Max + DEF_OFFSET,lnY_Max + DEF_OFFSET } ) // Draw the X & Y Axis lines.... laAttributes[GRA_AM_COLOR] := GRA_CLR_BLACK // Set Axis line color laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Axis line type GraSetAttrLine( loPS, laAttributes ) laAttributes := Array( GRA_AL_COUNT ) laAttributes[GRA_AL_COLOR] := GRA_CLR_BLACK // Set Axis line colors laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Axis line type laAttributes[GRA_AL_WIDTH] := GRA_LINEWIDTH_THICK // Set Axis line width GraSetAttrLine( loPS, laAttributes ) GraLine( loPS, {DEF_OFFSET,DEF_OFFSET}, {DEF_OFFSET,lnY_Max + DEF_OFFSET } ) // Draw Verticle Axis GraLine( loPS, {DEF_OFFSET,DEF_OFFSET}, {lnX_Max + DEF_OFFSET+2,DEF_OFFSET} ) // Draw Horizontal Axis laAttributes := Array( GRA_AL_COUNT ) laAttributes[GRA_AL_COLOR] := GRA_CLR_BLACK // Set Graph line colors laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Graph line type laAttributes[GRA_AL_WIDTH] := GRA_LINEWIDTH_NORMAL // Set Graph line width GraSetAttrLine( loPS, laAttributes ) // Draw the Date/Time Created text GraStringAt( loPS, {5,5 }, "Created: " + Dtoc(Date()) + " " + Time() + " EST" ) /***************************************************************/ // Line color for 1st data element laAttributes := Array( GRA_AM_COUNT ) laAttributes[GRA_AM_COLOR] := DEF_DATAPOINT1COLOR // Set Graph line color laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Graph line type GraSetAttrLine( loPS, laAttributes ) // Reset the pen position to the 1st value to be displayed. GraPos( loPS, { DEF_OFFSET + 1 ,paRecords[1][DEF_DATAPOINT1] + DEF_OFFSET } ) lnII := 1 lnMin := 0 lnMax := 0 lnTotal := 0 lnTotal_ := 0 For lnI := 1 To Len(paRecords) If paRecords[lnI][DEF_DATAPOINT1] < lnMin lnMin := paRecords[lnI][DEF_DATAPOINT1] Endif If paRecords[lnI][DEF_DATAPOINT1] > lnMax lnMax := paRecords[lnI][DEF_DATAPOINT1] Endif //lnTotal += paRecords[lnI][DEF_DATAPOINT1] If paRecords[lnI][DEF_DATAPOINT1] > 0 lnTotal += paRecords[lnI][DEF_DATAPOINT1] lnTotal_ ++ Endif If lnI % pnScaling == 0 If paRecords[lnI][DEF_DATAPOINT1] > 0 // Eliminate zero datapoints GraLine( loPS, NIL , { lnII + DEF_OFFSET + 2 ,paRecords[lnI][DEF_DATAPOINT1] + DEF_OFFSET + 2 } ) Endif lnII ++ Endif Next lnI // Create & set the font used for the captions loFont := XbpFont():New(loPS) // Create font object loFont:Create(FONT_DEFPROP_MEDIUM) // Create the object GraSetFont ( loPS , loFont ) // Set current font // Caption for 1st data element laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := DEF_DATAPOINT1COLOR // Set Label line color GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 150,lnY - 95}, laDescriptions[DEF_DATAPOINT1] ) laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 150,lnY - 125} , AllTrim(Str(Round(lnMax,1))) ) //GraStringAt( loPS, {lnLeftMargin + 150,lnY - 150} , AllTrim(Str(Round(lnTotal/Len(paRecords),1))) ) GraStringAt( loPS, {lnLeftMargin + 150,lnY - 150} , AllTrim(Str(Round(lnTotal/lnTotal_,1))) ) /***************************************************************/ // Line color for 2nd data element laAttributes := Array( GRA_AM_COUNT ) laAttributes[GRA_AM_COLOR] := DEF_DATAPOINT2COLOR // Set Graph line color laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Graph line type GraSetAttrLine( loPS, laAttributes ) // Reset the pen position to the 2nd value to be displayed. GraPos( loPS, { DEF_OFFSET + 1 ,paRecords[1][DEF_DATAPOINT2] + DEF_OFFSET } ) lnII := 1 lnMin := 0 lnMax := 0 lnTotal := 0 lnTotal_ := 0 For lnI := 1 To Len(paRecords) If paRecords[lnI][DEF_DATAPOINT2] < lnMin lnMin := paRecords[lnI][DEF_DATAPOINT2] Endif If paRecords[lnI][DEF_DATAPOINT2] > lnMax lnMax := paRecords[lnI][DEF_DATAPOINT2] Endif //lnTotal += paRecords[lnI][DEF_DATAPOINT2] If paRecords[lnI][DEF_DATAPOINT2] > 0 lnTotal += paRecords[lnI][DEF_DATAPOINT2] lnTotal_ ++ Endif If lnI % pnScaling == 0 If paRecords[lnI][DEF_DATAPOINT2] > 0 // Eliminate zero datapoints GraLine( loPS, NIL , { lnII + DEF_OFFSET + 2,paRecords[lnI][DEF_DATAPOINT2] + DEF_OFFSET + 2 } ) Endif lnII ++ Endif Next lnI // Caption for 2nd data element laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := DEF_DATAPOINT2COLOR // Set Label line color GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 250,lnY - 95}, laDescriptions[DEF_DATAPOINT2] ) laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 100,lnY - 125} , "Max:") GraStringAt( loPS, {lnLeftMargin + 100,lnY - 150} , "Avg:") GraStringAt( loPS, {lnLeftMargin + 250,lnY - 125} , AllTrim(Str(Int(lnMax))) ) //GraStringAt( loPS, {lnLeftMargin + 250,lnY - 150} , AllTrim(Str(Int(lnTotal/Len(paRecords)))) ) GraStringAt( loPS, {lnLeftMargin + 250,lnY - 150} , AllTrim(Str(Int(lnTotal/lnTotal_))) ) /***************************************************************/ // Line color for 3rd data element laAttributes := Array( GRA_AM_COUNT ) laAttributes[GRA_AM_COLOR] := DEF_DATAPOINT3COLOR // Set Graph line color laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Graph line type GraSetAttrLine( loPS, laAttributes ) // Reset the pen position to the 3nd value to be displayed. GraPos( loPS, { DEF_OFFSET + 1 ,paRecords[1][DEF_DATAPOINT3] + DEF_OFFSET } ) lnII := 1 lnMin := 0 lnMax := 0 lnTotal := 0 lnTotal_ := 0 For lnI := 1 To Len(paRecords) If paRecords[lnI][DEF_DATAPOINT3] < lnMin lnMin := paRecords[lnI][DEF_DATAPOINT3] Endif If paRecords[lnI][DEF_DATAPOINT3] > lnMax lnMax := paRecords[lnI][DEF_DATAPOINT3] Endif //lnTotal += paRecords[lnI][DEF_DATAPOINT3] //If paRecords[lnI][DEF_DATAPOINT3] > 0 // We always add zero watts whether it is zero or not so we take into account not pedaling... lnTotal += paRecords[lnI][DEF_DATAPOINT3] lnTotal_ ++ //Endif If lnI % pnScaling == 0 If paRecords[lnI][DEF_DATAPOINT3] > 0 // Eliminate zero datapoints GraLine( loPS, NIL , { lnII + DEF_OFFSET + 2 ,paRecords[lnI][DEF_DATAPOINT3] + DEF_OFFSET + 2} ) Endif lnII ++ Endif Next lnI // Caption for 3rd data element laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := DEF_DATAPOINT3COLOR // Set Label line color GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 350,lnY - 95}, laDescriptions[DEF_DATAPOINT3] ) laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 350,lnY - 125} , AllTrim(Str(Int(lnMax))) ) //GraStringAt( loPS, {lnLeftMargin + 350,lnY - 150} , AllTrim(Str(Int(lnTotal/Len(paRecords)))) ) GraStringAt( loPS, {lnLeftMargin + 350,lnY - 150} , AllTrim(Str(Int(lnTotal/lnTotal_))) ) /***************************************************************/ // Line color for 4th data element laAttributes := Array( GRA_AM_COUNT ) laAttributes[GRA_AM_COLOR] := DEF_DATAPOINT4COLOR // Set Graph line color laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Graph line type GraSetAttrLine( loPS, laAttributes ) // Reset the pen position to the 4th value to be displayed. GraPos( loPS, { DEF_OFFSET + 1 ,paRecords[1][DEF_DATAPOINT4] + DEF_OFFSET } ) lnII := 1 lnMin := 0 lnMax := 0 lnTotal := 0 lnTotal_ := 0 For lnI := 1 To Len(paRecords) If paRecords[lnI][DEF_DATAPOINT4] < lnMin lnMin := paRecords[lnI][DEF_DATAPOINT4] Endif If paRecords[lnI][DEF_DATAPOINT4] > lnMax lnMax := paRecords[lnI][DEF_DATAPOINT4] Endif //lnTotal += paRecords[lnI][DEF_DATAPOINT4] //If paRecords[lnI][DEF_DATAPOINT4] > 0 // We count all tourque for average including zero - coasting.... lnTotal += paRecords[lnI][DEF_DATAPOINT4] lnTotal_ ++ //Endif If lnI % pnScaling == 0 If paRecords[lnI][DEF_DATAPOINT4] > 0 // Eliminate zero datapoints GraLine( loPS, NIL , { lnII + DEF_OFFSET + 2 ,paRecords[lnI][DEF_DATAPOINT4] + DEF_OFFSET + 2} ) Endif lnII ++ Endif Next lnI // Caption for 4rd data element laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := DEF_DATAPOINT4COLOR // Set Label line color GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 450,lnY - 95}, laDescriptions[DEF_DATAPOINT4] ) laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK GraSetAttrString( loPS, laAttributes ) // http://www.bondhus.com/tech-library/body-1b.htm to find Torque conversions GraStringAt( loPS, {lnLeftMargin + 450,lnY - 125} , AllTrim(Str(Int(lnMax))) + " in/lbs" ) //GraStringAt( loPS, {lnLeftMargin + 450,lnY - 150} , AllTrim(Str(Int(lnTotal /Len(paRecords)))) + " in/lbs" ) GraStringAt( loPS, {lnLeftMargin + 450,lnY - 150} , AllTrim(Str(Int(lnTotal /lnTotal_))) + " in/lbs" ) /***************************************************************/ // Line color for 5th data element laAttributes := Array( GRA_AM_COUNT ) laAttributes[GRA_AM_COLOR] := DEF_DATAPOINT5COLOR // Set Graph line color laAttributes[GRA_AL_TYPE] := GRA_LINETYPE_DEFAULT // Set Graph line type GraSetAttrLine( loPS, laAttributes ) // Reset the pen position to the 4th value to be displayed. GraPos( loPS, { DEF_OFFSET + 1 ,paRecords[1][DEF_DATAPOINT5] + DEF_OFFSET } ) lnII := 1 lnMin := 0 lnMax := 0 lnTotal := 0 lnTotal_ := 0 For lnI := 1 To Len(paRecords) If paRecords[lnI][DEF_DATAPOINT5] < lnMin lnMin := paRecords[lnI][DEF_DATAPOINT5] Endif If paRecords[lnI][DEF_DATAPOINT5] > lnMax lnMax := paRecords[lnI][DEF_DATAPOINT5] Endif //lnTotal += paRecords[lnI][DEF_DATAPOINT5] If paRecords[lnI][DEF_DATAPOINT5] > 0 lnTotal += paRecords[lnI][DEF_DATAPOINT5] lnTotal_ ++ Endif If lnI % pnScaling == 0 If paRecords[lnI][DEF_DATAPOINT5] > 0 // Eliminate zero datapoints GraLine( loPS, NIL , { lnII + DEF_OFFSET + 2 ,paRecords[lnI][DEF_DATAPOINT5] + DEF_OFFSET + 2} ) Endif lnII ++ Endif Next lnI // Caption for 5th data element laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := DEF_DATAPOINT5COLOR // Set Label line color GraSetAttrString( loPS, laAttributes ) GraStringAt( loPS, {lnLeftMargin + 550,lnY - 95}, laDescriptions[DEF_DATAPOINT5] ) laAttributes := Array( GRA_AS_COUNT ) laAttributes[GRA_AS_COLOR] := GRA_CLR_BLACK GraSetAttrString( loPS, laAttributes ) // http://www.bondhus.com/tech-library/body-1b.htm to find Torque conversions GraStringAt( loPS, {lnLeftMargin + 550,lnY - 125} , AllTrim(Str(Int(lnMax))) + " BPM" ) //GraStringAt( loPS, {lnLeftMargin + 550,lnY - 150} , AllTrim(Str(Int(lnTotal /Len(paRecords)))) + " BPM" ) GraStringAt( loPS, {lnLeftMargin + 550,lnY - 150} , AllTrim(Str(Int(lnTotal /lnTotal_))) + " BPM" ) /***************************************************************/ FErase(pcTempFile) loBmp:SaveFile( pcTempFile , XBPBMP_FORMAT_JPG, XBPBMP_DEF_COMPRESSION) End Sequence Return {lnY , lnX }