//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 }