/*****************************************************************************
* Module for Microchip Graphics Library
* GOL Layer
* Chart
*****************************************************************************
* FileName: Chart.c
* Dependencies: Chart.h
* Processor: PIC24F, PIC24H, dsPIC, PIC32
* Compiler: MPLAB C30 Version 3.00, C32
* Linker: MPLAB LINK30, LINK32
* Company: Microchip Technology Incorporated
*
* Software License Agreement
*
* Copyright © 2008 Microchip Technology Inc. All rights reserved.
* Microchip licenses to you the right to use, modify, copy and distribute
* Software only when embedded on a Microchip microcontroller or digital
* signal controller, which is integrated into your product or third party
* product (pursuant to the sublicense terms in the accompanying license
* agreement).
*
* You should refer to the license agreement accompanying this Software
* for additional information regarding your rights and obligations.
*
* SOFTWARE AND DOCUMENTATION ARE PROVIDED AS IS WITHOUT WARRANTY OF ANY
* KIND, EITHER EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION, ANY WARRANTY
* OF MERCHANTABILITY, TITLE, NON-INFRINGEMENT AND FITNESS FOR A PARTICULAR
* PURPOSE. IN NO EVENT SHALL MICROCHIP OR ITS LICENSORS BE LIABLE OR
* OBLIGATED UNDER CONTRACT, NEGLIGENCE, STRICT LIABILITY, CONTRIBUTION,
* BREACH OF WARRANTY, OR OTHER LEGAL EQUITABLE THEORY ANY DIRECT OR INDIRECT
* DAMAGES OR EXPENSES INCLUDING BUT NOT LIMITED TO ANY INCIDENTAL, SPECIAL,
* INDIRECT, PUNITIVE OR CONSEQUENTIAL DAMAGES, LOST PROFITS OR LOST DATA,
* COST OF PROCUREMENT OF SUBSTITUTE GOODS, TECHNOLOGY, SERVICES, OR ANY
* CLAIMS BY THIRD PARTIES (INCLUDING BUT NOT LIMITED TO ANY DEFENSE THEREOF),
* OR OTHER SIMILAR COSTS.
*
* Author Date Comment
*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
* Paolo A. Tamayo
* Anton Alkhimenok 4/8/08 ...
* PAT 8/8/08 Centered values displayed on bar charts
* Removed line drawn on pie chart when no slices
* are present.
* PAT 9/30/08 3-D bar depth is now equal to chart depth.
* Flushed 2-D Bars to equal max height of chart
* when equal or greater than.
* PAT 6/29/09 Modified Draw Sector function to be state based.
*****************************************************************************/
#include "Graphics\Graphics.h"
#include <math.h>
#ifdef USE_CHART
// internal functions and macros
WORD word2xchar(WORD pSmple, XCHAR *xcharArray, WORD cnt);
void GetCirclePoint(SHORT radius, SHORT angle, SHORT *x, SHORT *y);
WORD DrawSector(SHORT cx, SHORT cy, SHORT radius, SHORT angleFrom, SHORT angleTo, WORD outLineColor);
WORD GetColorShade(WORD color, BYTE shade);
WORD ChParseShowData(DATASERIES *pData);
DATASERIES *ChGetNextShowData(DATASERIES *pData);
SHORT ChSetDataSeries(CHART *pCh, WORD seriesNum, BYTE status);
// array used to define the default colors used to draw the bars or sectors of the chart
const WORD ChartVarClr[16] = { CH_CLR0, CH_CLR1, CH_CLR2, CH_CLR3,
CH_CLR4, CH_CLR5, CH_CLR6, CH_CLR7,
CH_CLR8, CH_CLR9, CH_CLR10,CH_CLR11,
CH_CLR12,CH_CLR13,CH_CLR14,CH_CLR15};
/*********************************************************************
* Function: CHART *ChCreate(WORD ID, SHORT left, SHORT top, SHORT right,
* SHORT bottom, WORD state ,CHARTDATA *pData,
* GOL_SCHEME *pScheme)
*
*
* Notes: Creates a CHART object and adds it to the current active list.
* If the creation is successful, the pointer to the created Object
* is returned. If not successful, NULL is returned.
*
********************************************************************/
CHART *ChCreate
(
WORD ID,
SHORT left,
SHORT top,
SHORT right,
SHORT bottom,
WORD state,
DATASERIES *pData,
CHARTPARAM *pParam,
GOL_SCHEME *pScheme
)
{
CHART *pCh = NULL;
pCh = (CHART *)GFX_malloc(sizeof(CHART));
if(pCh == NULL)
return (NULL);
pCh->hdr.ID = ID; // unique id assigned for referencing
pCh->hdr.pNxtObj = NULL; // initialize pointer to NULL
pCh->hdr.type = OBJ_CHART; // set object type
pCh->hdr.left = left; // left position
pCh->hdr.top = top; // top position
pCh->hdr.right = right; // right position
pCh->hdr.bottom = bottom; // bottom position
pCh->hdr.state = state; // state
if(pParam != NULL)
{
pCh->prm.pTitle = pParam->pTitle;
pCh->prm.pSmplLabel = pParam->pSmplLabel;
pCh->prm.pValLabel = pParam->pValLabel;
pCh->prm.smplStart = pParam->smplStart;
pCh->prm.smplEnd = pParam->smplEnd;
pCh->prm.valMax = pParam->valMax;
pCh->prm.valMin = pParam->valMin;
pCh->prm.pColor = pParam->pColor;
pCh->prm.pTitleFont = pParam->pTitleFont;
pCh->prm.pAxisLabelsFont = pParam->pAxisLabelsFont;
pCh->prm.pGridLabelsFont = pParam->pGridLabelsFont;
}
else
{
pCh->prm.pTitle = NULL;
pCh->prm.pSmplLabel = NULL;
pCh->prm.pValLabel = NULL;
pCh->prm.smplStart = 0;
pCh->prm.smplEnd = 0;
pCh->prm.valMax = 0;
pCh->prm.valMin = 0;
// use the default color table
pCh->prm.pColor = (WORD *)ChartVarClr;
pCh->prm.pTitleFont = _pDefaultGolScheme->pFont;
pCh->prm.pAxisLabelsFont = _pDefaultGolScheme->pFont;
pCh->prm.pGridLabelsFont = _pDefaultGolScheme->pFont;
}
pCh->pChData = pData; // assign the chart data
// check if how variables have SHOW_DATA flag set
pCh->prm.seriesCount = ChParseShowData(pData);
// Set the color scheme to be used
if(pScheme == NULL)
{
pCh->hdr.pGolScheme = _pDefaultGolScheme;
}
else
{
pCh->hdr.pGolScheme = (GOL_SCHEME *)pScheme;
pCh->prm.pTitleFont = pCh->hdr.pGolScheme->pFont;
pCh->prm.pAxisLabelsFont = pCh->hdr.pGolScheme->pFont;
pCh->prm.pGridLabelsFont = pCh->hdr.pGolScheme->pFont;
}
GOLAddObject((OBJ_HEADER *)pCh);
return (pCh);
}
/*********************************************************************
* Function: WORD ChTranslateMsg(CHART *pCh, GOL_MSG *pMsg)
*
* Notes: Evaluates the message if the object will be affected by the
* message or not.
*
********************************************************************/
WORD ChTranslateMsg(CHART *pCh, GOL_MSG *pMsg)
{
// Evaluate if the message is for the static text
// Check if disabled first
if(GetState(pCh, CH_DISABLED))
return (OBJ_MSG_INVALID);
#ifdef USE_TOUCHSCREEN
if(pMsg->type == TYPE_TOUCHSCREEN)
{
// Check if it falls in static text control borders
if
(
(pCh->hdr.left < pMsg->param1) &&
(pCh->hdr.right > pMsg->param1) &&
(pCh->hdr.top < pMsg->param2) &&
(pCh->hdr.bottom > pMsg->param2)
)
{
return (CH_MSG_SELECTED);
}
}
#endif
return (OBJ_MSG_INVALID);
}
////////////////////////////////////////////////
// internal functions
////////////////////////////////////////////////
DATASERIES *ChGetNextShowData(DATASERIES *pData)
{
DATASERIES *pVar = pData;
// find the next data series that will be shown
while(pVar->show != SHOW_DATA)
{
if((pVar = (DATASERIES *)pVar->pNextData) == NULL)
return (NULL);
}
return (pVar);
}
/* */
WORD ChParseShowData(DATASERIES *pData)
{
DATASERIES *pParse;
WORD sCnt = 0;
if(pData != NULL)
{
pParse = pData;
while(pParse != NULL)
{
if(pParse->show == SHOW_DATA)
sCnt++;
pParse = (DATASERIES *)pParse->pNextData;
}
}
return (sCnt);
}
/* */
WORD GetLongestNameLength(CHART *pCh)
{
WORD temp = 0;
DATASERIES *pVar;
if(!GetState(pCh, CH_LEGEND))
return (0);
// find the data series with the longest name
pVar = pCh->pChData;
while(pVar)
{
// check if the data series is to be shown
if(pVar->show == SHOW_DATA)
{
if(temp < GetTextWidth((XCHAR *)pVar->pSData, pCh->hdr.pGolScheme->pFont))
temp = GetTextWidth((XCHAR *)pVar->pSData, pCh->hdr.pGolScheme->pFont);
}
pVar = pVar->pNextData;
}
return (temp);
}
/* */
WORD word2xchar(WORD pSmple, XCHAR *xcharArray, WORD cnt)
{
WORD j, z;
static XCHAR *pXchar;
pXchar = xcharArray;
// this implements sprintf(strVal, "%d", temp); faster
// note that this is just for values >= 0, while sprintf covers negative values.
j = 1;
z = pSmple;
pXchar = &(*xcharArray) + (cnt - j);
do
{
*pXchar = (z % 10) + '0';
*pXchar--;
if((z /= 10) == 0)
break;
j++;
} while(j <= cnt);
return (j);
}
/*********************************************************************
* Function: WORD GetColorShade(WORD color, BYTE shade)
*
*
* Notes: This function gets the given color in 5-6-5 (RGB) format
* and computes the shade of the same color by shifting
* the rgb values depending on the shade value.
* The idea here is to get the given r,g and b colors and
* make them approach the gray color by shifting the each value
* closer to 128. If rgb values are > 128 we subtract and add
* if < 128 we add.
*
********************************************************************/
WORD GetColorShade(WORD color, BYTE shade)
{
WORD newColor;
BYTE rgb[3];
rgb[0] = ((color >> 11) << 3); // red
rgb[1] = ((color >> 5) << 2); // green
rgb[2] = ((color) << 3); // blue
BYTE i;
for(i = 0; i < 3; i++)
{
if(rgb[i] > 128)
rgb[i] = rgb[i] - ((rgb[i] - 128) >> (shade));
else
rgb[i] = rgb[i] + ((128 - rgb[i]) >> (shade));
}
newColor = RGB565CONVERT(rgb[0], rgb[1], rgb[2]);
return (newColor);
}
/*********************************************************************
* Function: WORD ChDraw(CHART *pCh)
*
*
* Notes: This is the state machine to draw the button.
*
********************************************************************/
#define STR_CHAR_CNT 11
#define DCLR_STR_CHAR_CNT (STR_CHAR_CNT + 1)
/* */
WORD ChDraw(CHART *pCh)
{
typedef enum
{
REMOVE,
FRAME_DRAW_PREP,
FRAME_DRAW,
CHECK_CHART_TYPE,
// BAR type states
GRID_PREP,
SAMPLE_GRID_DRAW1,
VALUE_GRID_DRAW1,
SAMPLE_GRID_DRAW2,
VALUE_GRID_DRAW2,
VALUE_GRID_3D_DRAW,
TITLE_LABEL_DRAW_SET,
TITLE_LABEL_DRAW_RUN,
SAMPLE_LABEL_DRAW_SET,
SAMPLE_LABEL_DRAW_RUN,
VALUE_LABEL_DRAW_INIT,
VALUE_LABEL_DRAW_SET,
VALUE_LABEL_DRAW_RUN,
XAXIS_LABEL_DRAW_RUN,
XAXIS_LABEL_DRAW_SET,
YAXIS_LABEL_DRAW_RUN,
YAXIS_LABEL_DRAW_SET,
LEGEND_DRAW_BOX,
LEGEND_DRAW_RUN,
LEGEND_DRAW_UPDATE_VAR,
DATA_DRAW_INIT,
DATA_DRAW_SET,
BAR_DATA_DRAW,
BAR_DATA_DRAW_CHECK,
BAR_DATA_DRAW_3D_PREP,
BAR_DATA_DRAW_3D_LOOP_1,
BAR_DATA_DRAW_3D_LOOP_2,
BAR_DATA_DRAW_3D_OUTLINE1,
BAR_DATA_DRAW_3D_OUTLINE2,
BAR_DATA_DRAW_3D_OUTLINE3,
BAR_DATA_DRAW_3D_OUTLINE4,
BAR_DATA_DRAW_3D_OUTLINE5,
PIE_DONUT_HOLE_DRAW,
BAR_DATA_DRAW_VALUE,
BAR_DATA_DRAW_VALUE_RUN,
// PIE type states
PIE_PREP,
PIE_DRAW_OUTLINE1,
PIE_DRAW_SECTOR,
PIE_DRAW_SECTOR_LOOP,
PIE_DRAW_SECTOR_ACTUAL,
PIE_DRAW_SECTOR_LOOP_CONTINUE,
PIE_DRAW_SECTOR_LOOP_CREATE_STRINGS,
PIE_DRAW_SECTOR_LOOP_STRINGS_RUN,
} CH_DRAW_STATES;
static XCHAR tempXchar[2] = {'B',0};
static XCHAR temp2Xchar[2] = {'M',0};
static XCHAR tempStr[DCLR_STR_CHAR_CNT] = {'0','0','0','0','0','0','0','0','0','0',0};
static CH_DRAW_STATES state = REMOVE;
static WORD x, y, z, xStart, yStart, ctr, ctry, samplesMax, temp;
static SHORT uLocator;
static WORD splDelta, valDelta;
static WORD barWidth, barDepth, chart3DDepth;
static DATASERIES *pVar;
static WORD *pSmple;
static XCHAR *pXcharTemp;
static DWORD dTemp;
static SHORT varCtr, pieX, pieY;
static DWORD dPercent;
static WORD j = 0, k = 0, h = 0, i, m;
static void *pVarFont;
static WORD pieLabelXPos;
static WORD pieLabelYPos, pieLabelYPos2, pieLabelYPos3;
static WORD pieSectorXPos;
static WORD pieSectorYPos;
if(IsDeviceBusy())
return (0);
switch(state)
{
case REMOVE:
if(IsDeviceBusy())
return (0);
if(GetState(pCh, CH_HIDE))
{ // Hide the Chart (remove from screen)
SetColor(pCh->hdr.pGolScheme->CommonBkColor);
if(!Bar(pCh->hdr.left, pCh->hdr.top, pCh->hdr.right, pCh->hdr.bottom))
return (0);
return (1);
}
SetLineThickness(NORMAL_LINE);
SetLineType(SOLID_LINE);
// check if we only need to refresh the data on the chart
if(GetState(pCh, CH_DRAW_DATA))
{
// this is only performed when refreshing data in the chart
// erase the current contents
SetColor(pCh->hdr.pGolScheme->CommonBkColor);
// get the ending x position where redraw will take place (if legend is drawn,
// we do not need to redraw this area)
i = GetLongestNameLength(pCh) + GetTextHeight(pCh->hdr.pGolScheme->pFont) + GOL_EMBOSS_SIZE + (CH_MARGIN << 1);
if(GetState(pCh, CH_BAR))
{
// get the starting x position where redraw will take place
h = xStart - GetTextWidth(tempXchar, pCh->prm.pGridLabelsFont) - (GetTextWidth(temp2Xchar, pCh->prm.pGridLabelsFont) >> 1);
// get the starting y position
j = yStart - ((ChGetSampleRange(pCh) + 1) * splDelta);
if(GetState(pCh, CH_3D_ENABLE))
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
// adjust for 3D effects
j -= chart3DDepth;
if((GetState(pCh, CH_LEGEND)) && (ChGetShowSeriesCount(pCh) != 1))
{
if(!Bar(h, j, pCh->hdr.right - i, yStart))
return (0);
}
else
{
if(!Bar(h, j, pCh->hdr.right - CH_MARGIN, yStart))
return (0);
}
}
else
{
if
(
!Bar
(
xStart,
yStart - ((CH_YGRIDCOUNT - 1) * valDelta) - chart3DDepth - (GetTextHeight(pCh->hdr.pGolScheme->pFont)),
xStart + splDelta * (ChGetSampleRange(pCh) + 1) + chart3DDepth,
yStart + GetTextHeight(pCh->prm.pGridLabelsFont)
)
) return (0);
}
}
else
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if((GetState(pCh, CH_LEGEND)) && (ChGetShowSeriesCount(pCh) != 1))
{
if(!Bar(h, j, pCh->hdr.right - i, yStart))
return (0);
}
else
{
if(!Bar(h, j, pCh->hdr.right - CH_MARGIN, yStart))
return (0);
}
}
else
{
if
(
!Bar
(
xStart,
yStart - ((CH_YGRIDCOUNT - 1) * valDelta) - (GetTextHeight(pCh->hdr.pGolScheme->pFont)),
xStart + splDelta * (ChGetSampleRange(pCh) + 1),
yStart + GetTextHeight(pCh->prm.pGridLabelsFont)
)
) return (0);
}
}
}
else
{
if((GetState(pCh, CH_LEGEND)) && (ChGetShowSeriesCount(pCh) != 1))
i = pCh->hdr.right - i;
else
i = pCh->hdr.right - GOL_EMBOSS_SIZE - CH_MARGIN;
h = pCh->hdr.left + GOL_EMBOSS_SIZE;
j = pCh->hdr.top + GOL_EMBOSS_SIZE + CH_MARGIN + GetTextHeight(pCh->prm.pTitleFont);
// erase the current pie chart drawn
if(!Bar(h, j, i, pCh->hdr.bottom - CH_MARGIN))
return (0);
}
// check the type of chart
if(GetState(pCh, CH_BAR))
{
state = GRID_PREP;
goto chrt_grid_prep;
}
else
{
state = PIE_PREP;
goto chrt_pie_prep;
}
}
state = FRAME_DRAW_PREP;
/*========================================================================*/
// Draw the frame
/*========================================================================*/
case FRAME_DRAW_PREP:
// check how many data series do we need to display
pCh->prm.seriesCount = ChParseShowData(pCh->pChData);
// set up the frame drawing
GOLPanelDraw
(
pCh->hdr.left,
pCh->hdr.top,
pCh->hdr.right,
pCh->hdr.bottom,
0,
pCh->hdr.pGolScheme->CommonBkColor,
pCh->hdr.pGolScheme->EmbossLtColor,
pCh->hdr.pGolScheme->EmbossDkColor,
NULL,
1
);
state = FRAME_DRAW;
case FRAME_DRAW:
if(!GOLPanelDrawTsk())
{
return (0);
}
state = TITLE_LABEL_DRAW_SET;
/*========================================================================*/
// Draw the Chart Title
/*========================================================================*/
case TITLE_LABEL_DRAW_SET:
// find the location of the title
MoveTo
(
pCh->hdr.left + ((pCh->hdr.right + pCh->hdr.left - GetTextWidth((XCHAR *)pCh->prm.pTitle, pCh->prm.pTitleFont)) >> 1),
pCh->hdr.top + CH_MARGIN
);
state = TITLE_LABEL_DRAW_RUN;
case TITLE_LABEL_DRAW_RUN:
// Set the font
SetFont(pCh->prm.pTitleFont);
// NOTE: we use the emboss dark color here to draw the chart title.
SetColor(pCh->hdr.pGolScheme->EmbossDkColor);
if(!OutText(pCh->prm.pTitle))
return (0);
// check if legend will be drawn
if(GetState(pCh, CH_LEGEND) && (ChGetShowSeriesCount(pCh) != 1))
{
// position the x and y points to the start of the first variable
temp = GetLongestNameLength(pCh);
x = pCh->hdr.right - (CH_MARGIN << 1) - temp - GetTextHeight(pCh->hdr.pGolScheme->pFont);
y = ((pCh->hdr.bottom + pCh->hdr.top) >> 1) - ((pCh->prm.seriesCount * GetTextHeight(pCh->hdr.pGolScheme->pFont)) >> 1);
// initialize the variable counter for the legend drawing
temp = 0;
pVar = (DATASERIES *)pCh->pChData;
state = LEGEND_DRAW_BOX;
}
else
{
// legend will not be drawn, go to data drawing next
state = CHECK_CHART_TYPE;
goto chrt_check_chart_type;
}
/*========================================================================*/
// Draw the Legend
/*========================================================================*/
chrt_draw_legend:
case LEGEND_DRAW_BOX:
// check if we will be showing this data series
if(ChGetShowSeriesStatus(pVar) == SHOW_DATA)
{
SetColor(*(&(*pCh->prm.pColor) + temp));
if((ChGetShowSeriesCount(pCh) == 1) && (GetState(pCh, CH_PIE)))
{ }
else
{
if
(
!Bar
(
x,
y + (GetTextHeight(pCh->hdr.pGolScheme->pFont) >> 2),
x + (GetTextHeight(pCh->hdr.pGolScheme->pFont) >> 1),
y +
(
GetTextHeight(pCh->hdr.pGolScheme->pFont) -
(GetTextHeight(pCh->hdr.pGolScheme->pFont) >> 2)
)
)
) return (0);
}
MoveTo(x + 2 + (GetTextHeight(pCh->hdr.pGolScheme->pFont) >> 1), y);
state = LEGEND_DRAW_RUN;
}
else
{
state = LEGEND_DRAW_UPDATE_VAR;
goto chrt_draw_legend_update;
}
case LEGEND_DRAW_RUN:
SetColor(pCh->hdr.pGolScheme->TextColor1);
SetFont(pCh->hdr.pGolScheme->pFont);
if(!OutText(pVar->pSData))
return (0);
// increment the variable counter
temp++;
if(temp == ChGetShowSeriesCount(pCh))
{
state = CHECK_CHART_TYPE;
goto chrt_check_chart_type;
}
else
state = LEGEND_DRAW_UPDATE_VAR;
chrt_draw_legend_update:
case LEGEND_DRAW_UPDATE_VAR:
// update the data series pointer and y position
if(ChGetShowSeriesStatus(pVar) == SHOW_DATA)
y += GetTextHeight(pCh->hdr.pGolScheme->pFont);
pVar = (DATASERIES *)pVar->pNextData;
state = LEGEND_DRAW_BOX;
goto chrt_draw_legend;
chrt_check_chart_type:
case CHECK_CHART_TYPE:
if(GetState(pCh, CH_BAR))
{
state = GRID_PREP;
}
else if(GetState(pCh, CH_PIE))
{
state = PIE_PREP;
goto chrt_pie_prep;
}
else
{
state = REMOVE;
return (1);
}
/**************************************************************************/
// BAR CHART states
/**************************************************************************/
/*========================================================================*/
// Draw the grids
/*========================================================================*/
chrt_grid_prep:
case GRID_PREP:
/* NOTE: X or Y Grid Labels - label for each division in the x or y axis
X or Y Axis Labels - label to name the x or y axis. Text is
user given in the CHART structure,
CHARTPARAM member.
*/
// count the number of characters needed for the axis label that
// represents the value of the samples (or bars)
// get the width of one character
temp = GetTextWidth((XCHAR *)tempXchar, pCh->prm.pGridLabelsFont);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
// if in the horizontal orientation width will only be
// max of 2 characters (1, 2, 3...10, 11... or A, B, C...)
if((ChGetSampleEnd(pCh) - ChGetSampleStart(pCh)) > 9)
y = 2;
else
y = 1;
}
else
{
if(GetState(pCh, CH_PERCENT))
{
// include in the computation the space that will be occupied by '%' sign
y = 4;
}
else
{
x = ChGetValueRange(pCh);
y = 1;
// count the number of characters needed
while(x /= 10)
++y;
}
}
// estimate the space that will be occupied by the y grid labels
temp = temp * y;
// get x starting position
xStart = CH_MARGIN + temp + pCh->hdr.left;
// adjust x start to accomodate Y axis label
xStart += (GetTextHeight(pCh->prm.pAxisLabelsFont) + (GetTextWidth(temp2Xchar, pCh->prm.pGridLabelsFont) >> 1));
// now get y starting position
yStart = pCh->hdr.bottom - (GetTextHeight(pCh->prm.pGridLabelsFont) + GetTextHeight(pCh->prm.pAxisLabelsFont) + CH_MARGIN);
// =======================================================================
// Sample delta (splDelta) and Value delta (valDelta) will depend if the
// chart is drawn horizontally or vertically.
// =======================================================================
// find the variable with the longest name
// to add space for the names of variables in the legend
// Text Height here refers to the legend for colors (the drawn filled rectangle)
if((ChGetShowSeriesCount(pCh) == 1) || (GetState(pCh, CH_LEGEND) != CH_LEGEND))
temp = 0;
else
temp = GetLongestNameLength(pCh) + GetTextHeight(pCh->hdr.pGolScheme->pFont);
// get sample delta (space between data) and value delta (defines the grid for the value)
// check first if we compute in the x-axis or y-axis
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
// if horizontally drawn sample delta is not affected by the legend
splDelta = (yStart - (pCh->hdr.top + CH_MARGIN + GetTextHeight(pCh->prm.pTitleFont)));
// adjust space for displayed values
if(GetState(pCh, CH_VALUE))
{
temp += (GetTextWidth(tempXchar, pCh->hdr.pGolScheme->pFont) * 4);
}
// get the value delta,
valDelta = (pCh->hdr.right - xStart - CH_MARGIN - temp);
}
else
{
if(GetState(pCh, CH_LEGEND) && (ChGetShowSeriesCount(pCh) != 1))
{
splDelta =
(
pCh->hdr.right -
xStart -
((CH_MARGIN << 2) + temp + GetTextHeight(pCh->hdr.pGolScheme->pFont))
);
}
else
{
splDelta = (pCh->hdr.right - xStart - (CH_MARGIN << 2));
}
// get the value delta
valDelta =
(
yStart -
(pCh->hdr.top + CH_MARGIN + GetTextHeight(pCh->prm.pTitleFont) + GetTextHeight(pCh->hdr.pGolScheme->pFont))
);
}
// adjust the splDelta for 3D effects, 12 here is the maximum depth of a bar for the 3D effect
if(GetState(pCh, CH_3D_ENABLE))
{
splDelta -= 12;
valDelta -= 12;
}
// get the final splDelta value by checking the number of samples to display
splDelta /= (ChGetSampleRange(pCh) + 1);
// get the final valDelta value by checking the number of samples to display
valDelta /= (CH_YGRIDCOUNT - 1);
// initilize the counter for the sample axis drawing
temp = ChGetSampleRange(pCh) + 2;
x = xStart;
y = yStart;
state = SAMPLE_GRID_DRAW1;
case SAMPLE_GRID_DRAW1:
// draw the small grids on the x-axis
while(temp)
{
SetColor(pCh->hdr.pGolScheme->Color0);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Bar(x, y, x + 3, y + 1))
return (0);
y -= splDelta;
}
else
{
if(!Bar(x, y, x + 1, y + 3))
return (0);
x += splDelta;
}
--temp;
}
// get the bar width (bar here refers to the sample data represented as bars, where the height
// of the bar represents the value of the sample)
barWidth = splDelta / (2 + ChGetShowSeriesCount(pCh));
temp = CH_YGRIDCOUNT;
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
y = yStart;
else
x = xStart;
if(GetState(pCh, CH_3D_ENABLE))
{
// limit the 3-D depth to only 12 pixels.
chart3DDepth = (barWidth > 12) ? 12 : barWidth;
chart3DDepth = chart3DDepth >> 1;
// set the bar 3-D depth.
barDepth = chart3DDepth;
state = VALUE_GRID_3D_DRAW;
}
else
{
state = VALUE_GRID_DRAW1;
goto chrt_value_grid_draw1;
}
case VALUE_GRID_3D_DRAW:
// draw the 3D grids on the value-axis
while(temp)
{
SetColor(pCh->hdr.pGolScheme->Color0);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if
(
!Bar
(
x + (chart3DDepth),
y - (chart3DDepth) - (splDelta * (ChGetSampleRange(pCh) + 1)),
x + (chart3DDepth),
y - (chart3DDepth)
)
) return (0);
x += valDelta;
}
else
{
if
(
!Bar
(
x + (chart3DDepth),
y - (chart3DDepth),
x + (chart3DDepth) + (splDelta * (ChGetSampleRange(pCh) + 1)),
y - (chart3DDepth)
)
) return (0);
y -= valDelta;
}
--temp;
}
temp = CH_YGRIDCOUNT;
x = xStart;
y = yStart;
state = VALUE_GRID_DRAW1;
chrt_value_grid_draw1:
case VALUE_GRID_DRAW1:
// draw the grids on the y-axis
if(GetState(pCh, CH_3D_ENABLE))
{
// just draw the first one to define the x-axis
SetColor(pCh->hdr.pGolScheme->Color0);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Bar(x, y - (splDelta * (ChGetSampleRange(pCh) + 1)), x, y))
return (0);
}
else
{
if(!Bar(x, y, x + (splDelta * (ChGetSampleRange(pCh) + 1)), y))
return (0);
}
}
else
{
while(temp)
{
SetColor(pCh->hdr.pGolScheme->Color0);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Bar(x, y - (splDelta * (ChGetSampleRange(pCh) + 1)), x, y))
return (0);
x += valDelta;
}
else
{
if(!Bar(x, y, x + (splDelta * (ChGetSampleRange(pCh) + 1)), y))
return (0);
y -= valDelta;
}
--temp;
}
}
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
x = xStart;
}
else
{
y = yStart;
}
if(GetState(pCh, CH_3D_ENABLE))
{
temp = CH_YGRIDCOUNT + 1;
state = VALUE_GRID_DRAW2;
}
else
{
temp = 2;
state = SAMPLE_GRID_DRAW2;
goto chrt_xgrid_draw2;
}
case VALUE_GRID_DRAW2:
// draw the 3-D effect on the y axis of the chart
SetColor(pCh->hdr.pGolScheme->Color0);
while(temp)
{
if(temp == (CH_YGRIDCOUNT + 1))
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if
(
!Line
(
x,
y - (splDelta * (ChGetSampleRange(pCh) + 1)),
x + chart3DDepth,
y - (splDelta * (ChGetSampleRange(pCh) + 1)) - chart3DDepth
)
) return (0);
}
else
{
if
(
!Line
(
x + (splDelta * (ChGetSampleRange(pCh) + 1)),
y,
x + (chart3DDepth) + (splDelta * (ChGetSampleRange(pCh) + 1)),
y - chart3DDepth
)
) return (0);
}
--temp;
continue;
}
else if(!Line(x, y, x + chart3DDepth, y - chart3DDepth))
return (0);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
x += valDelta;
}
else
{
y -= valDelta;
}
--temp;
}
temp = 3;
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
x = xStart;
}
else
{
y = yStart;
}
state = SAMPLE_GRID_DRAW2;
chrt_xgrid_draw2:
case SAMPLE_GRID_DRAW2:
// draw the left and right edges of the chart
while(temp)
{
if(GetState(pCh, CH_3D_ENABLE))
{
if(temp == 3)
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Bar(x, y, x + ((CH_YGRIDCOUNT - 1) * valDelta), y))
return (0);
}
else
{
if(!Bar(x, y - ((CH_YGRIDCOUNT - 1) * valDelta), x, y))
return (0);
}
--temp;
continue;
}
else if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if
(
!Bar
(
x + chart3DDepth,
y - chart3DDepth,
x + chart3DDepth + ((CH_YGRIDCOUNT - 1) * valDelta),
y - chart3DDepth
)
) return (0);
}
else
{
if
(
!Bar
(
x + chart3DDepth,
y - ((CH_YGRIDCOUNT - 1) * valDelta) - chart3DDepth,
x + chart3DDepth,
y - chart3DDepth
)
) return (0);
}
}
else
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Bar(x, y, x + ((CH_YGRIDCOUNT - 1) * valDelta), y))
return (0);
}
else
{
if(!Bar(x, y - ((CH_YGRIDCOUNT - 1) * valDelta), x, y))
return (0);
}
}
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
y -= (splDelta * (ChGetSampleRange(pCh) + 1));
}
else
{
x += (splDelta * (ChGetSampleRange(pCh) + 1));
}
--temp;
}
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
#ifdef USE_HORZ_ASCENDING_ORDER
y = yStart - (ChGetSampleRange(pCh) * splDelta);
#else
y = yStart;
#endif
else
x = xStart;
ctr = ChGetSampleStart(pCh);
state = SAMPLE_LABEL_DRAW_SET;
/*========================================================================*/
// Draw the Sample Grid labels
/*========================================================================*/
chrt_sample_label_draw_set:
case SAMPLE_LABEL_DRAW_SET:
// for data only redraw, we need to refresh the x-axis labels to indicate
// the correct sample points being shown
SetFont(pCh->prm.pGridLabelsFont);
if(GetState(pCh, CH_NUMERIC))
{
j = word2xchar(ctr, tempStr, STR_CHAR_CNT);
}
else
{
// note that we will only have A-Z labels.
tempStr[STR_CHAR_CNT - 1] = 'A' + (ctr - 1);
j = 1;
}
temp = GetTextWidth((&tempStr[STR_CHAR_CNT - j]), pCh->prm.pGridLabelsFont);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
MoveTo(x - temp, y - ((splDelta + GetTextHeight(pCh->prm.pGridLabelsFont)) >> 1));
}
else
{
MoveTo(x + ((splDelta - temp) >> 1), y);
}
state = SAMPLE_LABEL_DRAW_RUN;
case SAMPLE_LABEL_DRAW_RUN:
// draw the x axis grid numbers
SetColor(pCh->hdr.pGolScheme->TextColor0);
if(!OutText(&tempStr[STR_CHAR_CNT - j]))
return (0);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
#ifdef USE_HORZ_ASCENDING_ORDER
y += splDelta;
#else
y -= splDelta;
#endif
}
else
{
x += splDelta;
}
ctr++;
if(ctr > ChGetSampleEnd(pCh))
{
// check if we only need to redraw the data
if(GetState(pCh, CH_DRAW_DATA))
{
state = DATA_DRAW_INIT;
goto chrt_data_draw_init;
}
else
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
state = YAXIS_LABEL_DRAW_SET;
goto chrt_yaxis_label_draw_set;
}
else
{
state = XAXIS_LABEL_DRAW_SET;
}
}
}
else
{
temp = x;
goto chrt_sample_label_draw_set;
}
/*========================================================================*/
// Draw the X - Axis labels
/*========================================================================*/
chrt_xaxis_label_draw_set:
case XAXIS_LABEL_DRAW_SET:
// find the location of the label
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
pXcharTemp = pCh->prm.pValLabel;
uLocator = valDelta * (CH_YGRIDCOUNT - 1);
}
else
{
pXcharTemp = pCh->prm.pSmplLabel;
uLocator = splDelta * (ChGetSampleRange(pCh) + 1);
}
temp = GetTextWidth(pXcharTemp, pCh->prm.pAxisLabelsFont);
if(temp > uLocator)
temp = xStart;
else
temp = xStart + ((uLocator - temp) >> 1);
MoveTo(temp, yStart + GetTextHeight(pCh->prm.pGridLabelsFont));
state = XAXIS_LABEL_DRAW_RUN;
case XAXIS_LABEL_DRAW_RUN:
SetFont(pCh->prm.pAxisLabelsFont);
// enable clipping and set region
SetClip(CLIP_ENABLE);
SetClipRgn(pCh->hdr.left, pCh->hdr.top, pCh->hdr.right, pCh->hdr.bottom);
SetColor(pCh->hdr.pGolScheme->TextColor1);
if(!OutText(pXcharTemp))
return (0);
SetClip(CLIP_DISABLE);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
state = DATA_DRAW_INIT;
goto chrt_data_draw_init;
}
else
{
state = VALUE_LABEL_DRAW_INIT;
}
chrt_value_label_draw_init:
case VALUE_LABEL_DRAW_INIT:
ctr = 0;
// x is used here to represent change in value grid labels
// Note that we add a multiplier of 10 to compute for round off errors.
// It will not be perfect but approximations is better unless you work with
// figures divisible by 5.
if(GetState(pCh, CH_PERCENT))
{
// Scaling of the labels is included here
x = ChGetPercentRange(pCh) * 100 / (CH_YGRIDCOUNT - 1);
}
else
{
x = (ChGetValueRange(pCh) * 100) / (CH_YGRIDCOUNT - 1);
}
// compute for round off error, the adjustment for the factor 100 is done in
// conversion of the integer to string below.
if((x % 10) < 5)
x += 10;
state = VALUE_LABEL_DRAW_SET;
/*========================================================================*/
// Draw the Value Grid labels
/*========================================================================*/
chrt_value_label_draw_set:
case VALUE_LABEL_DRAW_SET:
// note that the adjustment of the 100 factor is done here.
if(GetState(pCh, CH_PERCENT))
{
// add the percent sign on the label
tempStr[STR_CHAR_CNT - 1] = '%';
// we have a plus 1 here since we add '%' sign already
j = 1 + word2xchar((ctr * x / 100) + ChGetPercentMin(pCh), tempStr, STR_CHAR_CNT - 1);
}
else
{
j = word2xchar((ctr * x / 100) + ChGetValueMin(pCh), tempStr, STR_CHAR_CNT);
}
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
temp = GetTextWidth((&tempStr[STR_CHAR_CNT - j]), pCh->prm.pGridLabelsFont);
MoveTo(xStart + ((valDelta * ctr) - (temp >> 1)), yStart);
}
else
{
temp = GetTextHeight(pCh->prm.pGridLabelsFont);
MoveTo
(
xStart - GetTextWidth((&tempStr[STR_CHAR_CNT - j]), pCh->prm.pGridLabelsFont),
yStart - ((valDelta * ctr) + (temp >> 1))
);
}
SetFont(pCh->prm.pGridLabelsFont);
state = VALUE_LABEL_DRAW_RUN;
case VALUE_LABEL_DRAW_RUN:
// draw the y axis grid numbers
SetColor(pCh->hdr.pGolScheme->TextColor0);
if(!OutText(&tempStr[STR_CHAR_CNT - j]))
return (0);
ctr++;
if(ctr >= CH_YGRIDCOUNT)
{
state = XAXIS_LABEL_DRAW_SET;
}
else
{
goto chrt_value_label_draw_set;
}
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
state = XAXIS_LABEL_DRAW_SET;
goto chrt_xaxis_label_draw_set;
}
else
{
state = YAXIS_LABEL_DRAW_SET;
}
/*========================================================================*/
// Draw the Y - Axis labels
/*========================================================================*/
chrt_yaxis_label_draw_set:
case YAXIS_LABEL_DRAW_SET:
// find the location of the label
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
pXcharTemp = pCh->prm.pSmplLabel;
uLocator = splDelta * (ChGetSampleRange(pCh) + 1);
}
else
{
pXcharTemp = pCh->prm.pValLabel;
uLocator = valDelta * CH_YGRIDCOUNT;
}
temp = GetTextWidth(pXcharTemp, pCh->prm.pAxisLabelsFont);
if(temp > uLocator)
temp = (pCh->hdr.bottom + pCh->hdr.top + temp) >> 1;
else
{
temp = yStart - ((uLocator - temp) >> 1);
}
MoveTo
(
xStart - GetTextWidth((&tempStr[STR_CHAR_CNT - j]), pCh->prm.pGridLabelsFont) - (GetTextWidth(temp2Xchar, pCh->prm.pGridLabelsFont) >> 1) - GetTextHeight(pCh->prm.pAxisLabelsFont),
temp
);
state = YAXIS_LABEL_DRAW_RUN;
case YAXIS_LABEL_DRAW_RUN:
SetFont(pCh->prm.pAxisLabelsFont);
// enable clipping and set region
SetClip(CLIP_ENABLE);
SetClipRgn(pCh->hdr.left, pCh->hdr.top, pCh->hdr.right, pCh->hdr.bottom);
// set the orientation of text to vertical
SetFontOrientation(ORIENT_VER);
SetColor(pCh->hdr.pGolScheme->TextColor1);
if(!OutText(pXcharTemp))
return (0);
SetClip(CLIP_DISABLE);
// place back the orientation of text to horizontal
SetFontOrientation(ORIENT_HOR);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
state = VALUE_LABEL_DRAW_INIT;
goto chrt_value_label_draw_init;
}
else
{
state = DATA_DRAW_INIT;
}
/*========================================================================*/
// Draw the bars representing the data/samples
/*========================================================================*/
chrt_data_draw_init:
case DATA_DRAW_INIT:
/* pSmple - points to the sample data of the variables
ctr - the sample counter
varCtr - variable counter
temp - the width of the bars
dTemp - the height of the bars
x or y - the location of the first bar per sample. For single variables
there will be only one bar per sample. x for vertical bars and y for horizontal bars.
*/
ctr = 0;
temp = splDelta / (2 + ChGetShowSeriesCount(pCh)); // <---- note this! this can be used to calculate the minimum size limit of the chart
state = DATA_DRAW_SET;
chrt_data_draw_set:
case DATA_DRAW_SET:
varCtr = 0;
pVar = ChGetNextShowData(pCh->pChData);
// set the position to start bar drawing
// x and y here are used in horizontal drawing and vertical drawing as the variable
// that refers to the position of the bar being drawn.
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
#ifdef USE_HORZ_ASCENDING_ORDER
y = yStart - temp - ((ChGetSampleRange(pCh) - ctr) * splDelta);
#else
y = yStart - (ctr * splDelta) - temp;
#endif
}
else
{
x = xStart + (ctr * splDelta) + temp;
}
state = BAR_DATA_DRAW;
chrt_data_draw:
case BAR_DATA_DRAW:
// get sample data from current variable
pSmple = (&(*pVar->pData) + (ctr + ChGetSampleStart(pCh) - 1));
// calculate the total value of the samples to compute the percentages
if(GetState(pCh, CH_PERCENT))
{
j = ChGetSampleStart(pCh);
samplesMax = 0;
while(j <= (ChGetSampleRange(pCh) + ChGetSampleStart(pCh)))
{
samplesMax += (*(&(*pVar->pData) + (j - 1)));
j++;
}
// Get the percentage of the sample
dTemp = ((DWORD) (*pSmple) * 100) / samplesMax;
// check if scaling is needed
if(ChGetPercentMax(pCh) <= dTemp)
dTemp = ChGetPercentRange(pCh);
else
{
if(dTemp < ChGetPercentMin(pCh))
dTemp = 0;
else
dTemp = dTemp - ChGetPercentMin(pCh);
}
dTemp = ((DWORD) (dTemp) * (valDelta * (CH_YGRIDCOUNT - 1))) / (ChGetPercentRange(pCh));
}
else
{
// get the height of the current bar to draw
// this should be adjusted to the min and max set values
if(ChGetValueMax(pCh) <= (*pSmple))
{
dTemp = ChGetValueRange(pCh);
}
else
{
if((*pSmple) < ChGetValueMin(pCh))
dTemp = 0;
else
dTemp = (*pSmple) - ChGetValueMin(pCh);
}
dTemp = ((DWORD) (dTemp) * (valDelta * (CH_YGRIDCOUNT - 1)) / ChGetValueRange(pCh));
}
// draw the front side of the bar
if(ChGetShowSeriesCount(pCh) > 1)
{
SetColor(*(&(*pCh->prm.pColor) + varCtr));
}
else
{
SetColor(*(&(*pCh->prm.pColor) + ctr));
}
if(GetState(pCh, CH_3D_ENABLE))
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Bar(xStart, y - (temp * (varCtr + 1)), xStart + dTemp, y - (temp * varCtr)))
return (0);
}
else
{
if(!Bar(x + 1 + 1 + (temp * varCtr), yStart - dTemp, x + (temp * (varCtr + 1)), yStart))
return (0);
}
state = BAR_DATA_DRAW_3D_PREP;
}
else
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Bar(xStart, y - (temp * (varCtr + 1)), xStart + dTemp, y - 1 - (temp * varCtr)))
return (0);
}
else
{
if(!Bar(x + 1 + (temp * varCtr), yStart - dTemp, x + (temp * (varCtr + 1)), yStart))
return (0);
}
if((GetState(pCh, CH_VALUE)) || (GetState(pCh, CH_PERCENT)))
{
state = BAR_DATA_DRAW_VALUE;
goto chrt_bar_data_draw_value;
}
else
{
state = BAR_DATA_DRAW_CHECK;
goto chrt_bar_data_draw_check;
}
}
case BAR_DATA_DRAW_3D_PREP:
// draw the 3-D component
// draw the 45 degree lines
// we will use y here as the variable to move the line drawn
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
z = y;
x = xStart + dTemp;
}
else
{
z = x + 1;
y = yStart - dTemp;
}
state = BAR_DATA_DRAW_3D_LOOP_1;
chrt_bar_data_draw_3d_loop_1:
case BAR_DATA_DRAW_3D_LOOP_1:
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(x <= xStart + dTemp + barDepth)
{
if((x == (xStart + dTemp)) || (x == (xStart + dTemp + barDepth)))
{
SetColor(BLACK);
}
else
{
if(ChGetShowSeriesCount(pCh) > 1)
{
SetColor(GetColorShade(*(&(*pCh->prm.pColor) + varCtr), 2));
}
else
{
SetColor(GetColorShade(*(&(*pCh->prm.pColor) + ctr), 2));
}
}
if(!Bar(x, z - (temp * (varCtr + 1)), x, z - (temp * varCtr)))
return (0);
state = BAR_DATA_DRAW_3D_LOOP_2;
}
else
{
state = BAR_DATA_DRAW_3D_OUTLINE1;
goto chrt_bar_data_draw_3d_outline_1;
}
}
else
{
if(y >= yStart - dTemp - barDepth)
{
if((y == (yStart - dTemp)) || (y == (yStart - dTemp - barDepth)))
{
SetColor(BLACK);
}
else
{
if(ChGetShowSeriesCount(pCh) > 1)
{
SetColor(GetColorShade(*(&(*pCh->prm.pColor) + varCtr), 2));
}
else
{
SetColor(GetColorShade(*(&(*pCh->prm.pColor) + ctr), 2));
}
}
if(!Bar(z + 1 + (temp * varCtr), y, z - 1 + (temp * (varCtr + 1)), y))
return (0);
state = BAR_DATA_DRAW_3D_LOOP_2;
}
else
{
state = BAR_DATA_DRAW_3D_OUTLINE1;
goto chrt_bar_data_draw_3d_outline_1;
}
}
case BAR_DATA_DRAW_3D_LOOP_2:
// check if we are going to draw the outline or the shade
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if((x == (xStart + dTemp)) || (x == (xStart + dTemp + barDepth)))
{
SetColor(BLACK);
}
else
{
if(ChGetShowSeriesCount(pCh) > 1)
{
SetColor(GetColorShade(*(&(*pCh->prm.pColor) + varCtr), 1));
}
else
{
SetColor(GetColorShade(*(&(*pCh->prm.pColor) + ctr), 1));
}
}
}
else
{
if((y == (yStart - dTemp)) || (y == (yStart - dTemp - barDepth)))
{
SetColor(BLACK);
}
else
{
if(ChGetShowSeriesCount(pCh) > 1)
{
SetColor(GetColorShade(*(&(*pCh->prm.pColor) + varCtr), 1));
}
else
{
SetColor(GetColorShade(*(&(*pCh->prm.pColor) + ctr), 1));
}
}
}
// draw the outline or shade
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Bar(x - dTemp, z - (temp * (varCtr + 1)), x, z - (temp * (varCtr + 1))))
return (0);
}
else
{
if(!Bar(z + (temp * (varCtr + 1)), y, z + (temp * (varCtr + 1)), y + dTemp))
return (0);
}
// update the loop variables
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
x++;
z--;
}
else
{
y--;
z++;
}
state = BAR_DATA_DRAW_3D_LOOP_1;
goto chrt_bar_data_draw_3d_loop_1;
chrt_bar_data_draw_3d_outline_1:
case BAR_DATA_DRAW_3D_OUTLINE1:
SetColor(BLACK);
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Line(x - 1, y - (temp * varCtr) - barDepth, x - 1 - barDepth, y - (temp * varCtr)))
return (0);
}
else
{
if
(
!Line
(
x + 1 + (temp * varCtr),
yStart - dTemp,
x + 1 + (temp * varCtr) + barDepth,
yStart - dTemp - barDepth
)
) return (0);
}
state = BAR_DATA_DRAW_3D_OUTLINE2;
case BAR_DATA_DRAW_3D_OUTLINE2:
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if(!Line(x - 1, y - (temp * (varCtr + 1)) - barDepth, x - 1 - barDepth, y - (temp * (varCtr + 1))))
return (0);
}
else
{
if
(
!Line
(
x + 1 + (temp * (varCtr + 1)),
(yStart - dTemp),
x + 1 + (temp * (varCtr + 1)) + barDepth,
(yStart - dTemp) - barDepth
)
) return (0);
}
state = BAR_DATA_DRAW_3D_OUTLINE3;
case BAR_DATA_DRAW_3D_OUTLINE3:
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if
(
!Line
(
x - dTemp - 1,
y - (temp * (varCtr + 1)) - barDepth,
x - dTemp - barDepth - 1,
y - (temp * (varCtr + 1))
)
) return (0);
}
else
{
if
(
!Line
(
x + 1 + (temp * (varCtr + 1)),
yStart,
x + 1 + (temp * (varCtr + 1)) + barDepth,
yStart - barDepth
)
) return (0);
}
state = BAR_DATA_DRAW_3D_OUTLINE4;
case BAR_DATA_DRAW_3D_OUTLINE4:
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if
(
!Bar
(
x - dTemp - barDepth - 1,
y - (temp * (varCtr + 1)),
x - dTemp - barDepth - 1,
y - (temp * (varCtr + 1)) + temp
)
) return (0);
}
else
{
if(!Bar(x + 1 + (temp * varCtr), yStart - dTemp, x + 1 + (temp * varCtr), yStart))
return (0);
}
state = BAR_DATA_DRAW_3D_OUTLINE5;
case BAR_DATA_DRAW_3D_OUTLINE5:
// draw the horizontal lines
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
if
(
!Bar
(
x - dTemp - barDepth - 1,
y - (temp * (varCtr + 1)) + temp,
x - barDepth - 1,
y - (temp * (varCtr + 1)) + temp
)
) return (0);
}
else
{
if(!Bar(x + 1 + (temp * varCtr), yStart, x + 1 + (temp * (varCtr + 1)), yStart))
return (0);
}
if((GetState(pCh, CH_VALUE)) || (GetState(pCh, CH_PERCENT)))
state = BAR_DATA_DRAW_VALUE;
else
{
state = BAR_DATA_DRAW_CHECK;
goto chrt_bar_data_draw_check;
}
chrt_bar_data_draw_value:
case BAR_DATA_DRAW_VALUE:
if(GetState(pCh, CH_VALUE))
{
j = word2xchar(*pSmple, tempStr, STR_CHAR_CNT);
}
else
{
// add the percent sign on the label
tempStr[STR_CHAR_CNT - 1] = '%';
// compute for the percentage
if(ChGetValueMax(pCh) <= (*pSmple))
dPercent = ChGetValueRange(pCh);
else
{
if((*pSmple) < ChGetValueMin(pCh))
dPercent = 0;
else
dPercent = (*pSmple) - ChGetValueMin(pCh);
}
dPercent = ((DWORD) (dPercent * 1000)) / samplesMax;
// check if we need to round up or not
if((dPercent % 10) < 5)
dPercent = (dPercent / 10); // do not round up to next number
else
dPercent = (dPercent / 10) + 1; // round up the value
// we have a plus 1 here since we add '%' sign already
j = 1 + word2xchar(dPercent, tempStr, STR_CHAR_CNT - 1);
}
if(GetState(pCh, CH_3D_ENABLE))
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
MoveTo
(
xStart + dTemp + barDepth + 3,
y - (temp * varCtr) - ((temp + GetTextHeight(pCh->hdr.pGolScheme->pFont)) >> 1) - barDepth
);
}
else
{
MoveTo
(
x + barDepth + (temp * varCtr) + (temp >> 1) - (GetTextWidth(&tempStr[STR_CHAR_CNT - j], pCh->hdr.pGolScheme->pFont) >> 1),
(yStart - 1) - dTemp - GetTextHeight(pCh->hdr.pGolScheme->pFont) - (barDepth)
);
}
}
else
{
if(GetState(pCh, CH_BAR_HOR) == CH_BAR_HOR)
{
MoveTo
(
xStart + dTemp + 3,
y - (temp * varCtr) - (temp >> 1) - (GetTextHeight(pCh->hdr.pGolScheme->pFont) >> 1)
);
}
else
{
MoveTo
(
x +
((temp >> 1) - (GetTextWidth(&tempStr[STR_CHAR_CNT - j], pCh->hdr.pGolScheme->pFont) >> 1)) +
(temp * varCtr),
(yStart - 1) - dTemp - (GetTextHeight(pCh->hdr.pGolScheme->pFont))
);
}
}
state = BAR_DATA_DRAW_VALUE_RUN;
case BAR_DATA_DRAW_VALUE_RUN:
// draw the values on top of the bars
// NOTE: we use the emboss light color here to draw the values.
SetColor(pCh->hdr.pGolScheme->EmbossLtColor);
SetFont(pCh->hdr.pGolScheme->pFont);
if(!OutText(&tempStr[STR_CHAR_CNT - j]))
return (0);
state = BAR_DATA_DRAW_CHECK;
chrt_bar_data_draw_check:
case BAR_DATA_DRAW_CHECK:
// find the next data series that will be shown
pVar = ChGetNextShowData(pVar);
if(pVar == NULL)
{
state = REMOVE;
return (1);
}
// increment the variable counter
varCtr++;
if(varCtr < ChGetShowSeriesCount(pCh))
{
pVar = (DATASERIES *)pVar->pNextData;
state = BAR_DATA_DRAW;
goto chrt_data_draw;
}
// increment the sample counter
ctr++;
if(ctr < ChGetSampleRange(pCh) + 1)
{
x += (splDelta + 1);
state = DATA_DRAW_SET;
goto chrt_data_draw_set;
}
state = REMOVE;
return (1);
/**************************************************************************/
// PIE CHART states
/**************************************************************************/
/*========================================================================*/
// Draw the pie
/*========================================================================*/
chrt_pie_prep:
case PIE_PREP:
/* If more than two data series have their SHOW_DATA flag set
the pie chart is drawn to represent the values of each variable
at Start sample point . End sample point is ignored in this case.
If only one data series is set to be shown the pie chart is drawn
from the sample start point and sample end point (inclusive).
Pie chart is drawn as a percentage of the data samples.
Therefore: percentage is computed depending on the sample
points used.
Only 1 data series is set to be shown
total = summation of the variable sample points from
sample start point to sample end point inclusive.
More than 1 data series is set to be shown
total = summation of the sample points for each variable
using the sample start point.
*/
/* For PIE chart variables used are the following
ctr = x position of the center of the pie chart
ctry = y position of the center of the pie chart
z = radius of the pie chart
j = start angle
k = end angle
*/
// calculate the needed variables
// radius z is affected by the following: CH_LEGEND, CH_VALUE and CH_PERCENT
temp = CH_MARGIN << 1;
// check the largest/longest sample
// use x here as a counter
varCtr = ChGetShowSeriesCount(pCh);
if(varCtr == 1)
{
varCtr = ChGetSampleRange(pCh) + 1;
}
pVar = ChGetNextShowData(pCh->pChData);
// y and z is used here as a temporary variable
y = 0;
// find the sample value that is largest (place in y)
// also while doing this get the total of the selected data (put in dTemp)
dTemp = 0;
while(varCtr >= 0)
{
if(ChGetShowSeriesCount(pCh) > 1)
{
z = *(&(*pVar->pData) + ChGetSampleStart(pCh) - 1);
}
else
{
z = *(&(*pVar->pData) + ChGetSampleEnd(pCh) - varCtr);
}
dTemp += z;
varCtr--;
// check if we get a larger value
if(z > y)
y = z;
if(varCtr == 0)
break;
if(ChGetShowSeriesCount(pCh) > 1)
{
pVar = ChGetNextShowData((DATASERIES *)pVar->pNextData);
}
// this is insurance (in case the link list is corrupted)
if(pVar == NULL)
{
break;
}
}
// initialize pVarFont to use pGridLabelsFont
pVarFont = pCh->prm.pGridLabelsFont;
// get the space occupied by the value
if(GetState(pCh, CH_VALUE))
{
z = word2xchar(y, tempStr, STR_CHAR_CNT);
x = (GetTextWidth(&tempStr[STR_CHAR_CNT - z], pVarFont));
}
else
x = 0;
// get the space occupied by percent value
if(GetState(pCh, CH_PERCENT))
{
// 7 is derived from "X:100%," - seven possible characters, 1 is added for buffer
y = (8 * GetTextWidth((XCHAR *)tempXchar, pVarFont));
}
else
y = 0;
// get the space occupied by the legend
if((GetState(pCh, CH_LEGEND)) && (ChGetShowSeriesCount(pCh) > 1))
temp += ((GetLongestNameLength(pCh) + (GetTextHeight(pCh->hdr.pGolScheme->pFont) >> 1)));
// calculate the center of the pie chart
ctr = (pCh->hdr.left + pCh->hdr.right - temp) >> 1;
ctry = ((pCh->hdr.bottom + (pCh->hdr.top + GetTextHeight(pVarFont))) >> 1) + CH_MARGIN;
// radius size is checked against the x and y area
if
(
((pCh->hdr.right - pCh->hdr.left) - temp - ((x + y) << 1)) <
(
(pCh->hdr.bottom - pCh->hdr.top - GetTextHeight(pCh->prm.pTitleFont)) - temp -
(GetTextHeight(pVarFont) << 1)
)
)
{
// use dimension in the x axis
if(x + y)
{
z = ctr - (pCh->hdr.left + (x + y) + CH_MARGIN);
}
else
{
z = ctr - pCh->hdr.left + CH_MARGIN;
}
}
else
{
// use dimension in the y axis
if(x + y)
z = ctry -
(
pCh->hdr.top + (CH_MARGIN << 1) + GetTextHeight(pCh->prm.pTitleFont) +
(GetTextHeight(pVarFont) << 1)
);
else
z = ctry - (pCh->hdr.top + (CH_MARGIN << 1) + (GetTextHeight(pCh->prm.pTitleFont) << 1));
}
state = PIE_DRAW_OUTLINE1;
case PIE_DRAW_OUTLINE1:
// Required items before the pie chart can be drawn
SetColor(LIGHTGRAY);
// Draw pie-chart outline
if(!Circle(ctr, ctry, z))
return (0);
state = PIE_DRAW_SECTOR;
/*========================================================================*/
// Draw the sectors of the pie
/*========================================================================*/
case PIE_DRAW_SECTOR:
// now we are ready to draw the sectors
// calculate the sector that a value will need
k = 0;
// check if more than one data series set to be shown , draw the pie chart of
// the data series that are set to be shown.
pVar = ChGetNextShowData(pCh->pChData);
y = dTemp;
varCtr = ChGetShowSeriesCount(pCh);
if(varCtr == 1)
{
varCtr = ChGetSampleRange(pCh) + 1;
}
else if(varCtr == 0)
{
pVar = NULL;
y = 0;
}
// we start at 0 degree.
j = 0;
// initialize the variables that position the pie sector labels.
// this is used to minimize the occurrence of overlapped labels.
pieLabelYPos = ctry;
pieLabelYPos2 = pCh->hdr.bottom - (GOL_EMBOSS_SIZE + 1) - GetTextHeight(pVarFont);
pieLabelYPos3 = pCh->hdr.top + GOL_EMBOSS_SIZE + CH_MARGIN + GetTextHeight(pCh->prm.pTitleFont) + GetTextHeight(pVarFont);
state = PIE_DRAW_SECTOR_LOOP;
chrt_pie_draw_sector_loop:
case PIE_DRAW_SECTOR_LOOP:
if(varCtr >= 0)
{
// get the value to be computed
if(ChGetShowSeriesCount(pCh) > 1)
{
temp = *(&(*pVar->pData) + ChGetSampleStart(pCh) - 1);
}
else
{
temp = *(&(*pVar->pData) + ChGetSampleEnd(pCh) - varCtr);
}
// calculate the sector that the value will occupy
dTemp = ((DWORD) (temp) * (3600)) / y;
// check if we need to round up or not
if((dTemp % 10) < 5)
dTemp = (dTemp / 10); // do not round up to next number
else
dTemp = (dTemp / 10) + 1; // round up the value
// set the color to the color of the variable
SetColor(*(&(*pCh->prm.pColor) + k));
// check if the sector has zero angle if it is zero just draw the
// line.
if(dTemp == 0)
{
state = PIE_DRAW_SECTOR_LOOP_CONTINUE;
goto chrt_pie_draw_sector_loop_continue;
}
// go to the state that draws only. Doing this separates the setup of static variables
// and rendering. So in cases when the rendering cannot continue, the variables
// are still set to correct values.
state = PIE_DRAW_SECTOR_ACTUAL;
goto pie_draw_sector_actual;
}
else
{
if(GetState(pCh, CH_DONUT) == CH_DONUT)
{
state = PIE_DONUT_HOLE_DRAW;
goto chrt_pie_donut_hole_draw;
}
else
{
state = REMOVE;
return (1);
}
}
case PIE_DRAW_SECTOR_ACTUAL:
pie_draw_sector_actual :
// check if it is the last sector to be drawn
if((varCtr == 1) || ((j + dTemp) >= 358))
{
if(!DrawSector(ctr, ctry, z, j, 360, LIGHTGRAY))
return (0);
}
else
{
if(!DrawSector(ctr, ctry, z, j, (j + dTemp), LIGHTGRAY))
return (0);
}
state = PIE_DRAW_SECTOR_LOOP_CREATE_STRINGS;
case PIE_DRAW_SECTOR_LOOP_CREATE_STRINGS:
// create the strings of the values if needed
if(GetState(pCh, CH_VALUE) || GetState(pCh, CH_PERCENT))
{
h = 0;
GetCirclePoint(z, (j + (dTemp >> 1)), &pieX, &pieY);
pieX += ctr;
pieY += ctry;
// do we need to show the values? create the strings here
if(GetState(pCh, CH_VALUE))
{
h = word2xchar(temp, tempStr, STR_CHAR_CNT);
}
// do we need to show the percentage? create the strings here
if(GetState(pCh, CH_PERCENT))
{
// add the % sign
// check if we need to add comma
if(GetState(pCh, CH_VALUE))
{
h += 1; // adjust h
tempStr[STR_CHAR_CNT - h] = ',';
}
h += 1; // adjust h
tempStr[STR_CHAR_CNT - h] = '%';
// now add the percentage
dPercent = (DWORD) (dTemp * 1000) / 360;
// check if we need to round up or not
if((dPercent % 10) < 5)
dPercent = (dPercent / 10); // do not round up to next number
else
dPercent = (dPercent / 10) + 1; // round up the value
i = word2xchar((WORD) dPercent, tempStr, STR_CHAR_CNT - h);
// adjust the h position
h += i;
}
#ifdef USE_PIE_ENABLE_LABEL
// add the labels
h += 1; // adjust h
tempStr[STR_CHAR_CNT - h] = ':';
h += 1; // adjust h
tempStr[STR_CHAR_CNT - h] = 'A' + (ChGetSampleStart(pCh) - 1 + k);
#endif
SetColor(BLACK);
m = j;
// we have to relocate the text depending on the position
if((m + (dTemp >> 1) >= 0) && (m + (dTemp >> 1) <= 90))
{
// check if we need to draw a line
if((dTemp < GetTextHeight(pVarFont)) || (pieLabelYPos > pieY))
{
pieLabelXPos = ctr + z + GetTextHeight(pVarFont);
pieSectorXPos = pieX + 3;
if((m + (dTemp >> 1)) < 45)
pieSectorYPos = pieY + 1;
else
pieSectorYPos = pieY + 3;
// The label will now exceed the chart dimension, so force the value to be printed near the sector
pieLabelXPos = ctr + z + GetTextHeight(pVarFont);
if((pieLabelYPos + GetTextHeight(pVarFont)) > (pCh->hdr.bottom - (GOL_EMBOSS_SIZE + 1)))
{
MoveTo(pieX, pieY + 1);
}
else
{
// draw the line
if
(
!Line
(
pieSectorXPos,
pieSectorYPos,
pieLabelXPos,
pieLabelYPos + (GetTextHeight(pVarFont) >> 1)
)
) return (0);
MoveTo(pieLabelXPos, pieLabelYPos);
pieLabelYPos += GetTextHeight(pVarFont);
}
}
else
{
MoveTo(pieX + GetTextWidth(tempXchar, pVarFont), pieY);
pieLabelYPos = pieY + GetTextHeight(pVarFont);
}
}
else if((m + (dTemp >> 1) > 90) && (m + (dTemp >> 1) <= 180))
{
// check if we need to draw a line
if((dTemp < GetTextHeight(pVarFont)) || (pieLabelYPos2 < pieY))
{
pieLabelXPos = ctr - z - 3;
pieSectorXPos = pieX - 3;
if((m + (dTemp >> 1)) < 135)
pieSectorYPos = pieY + 3;
else
pieSectorYPos = pieY;
// check if slope of line is greater than -1.
// if it is we must adjust position of text to avoid the line
// intersecting the circumference of the pie chart
if((m + (dTemp >> 1)) < 180)
{
// make the slope equal to 1, this will make sure it does not intersect the circumference
if((abs(pieY - pieLabelYPos2) / abs(pieX - pieLabelXPos)) >= 1)
{
pieLabelXPos = pieX - abs(pieY - pieLabelYPos2);
if
(
(SHORT) (pieLabelXPos - GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont)) <=
(pCh->hdr.left + CH_MARGIN)
)
{
pieLabelXPos =
(
pCh->hdr.left +
CH_MARGIN +
GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont)
);
}
}
}
// draw the line
if
(
!Line
(
pieSectorXPos,
pieSectorYPos,
pieLabelXPos,
pieLabelYPos2 + (GetTextHeight(pVarFont) >> 1)
)
) return (0);
MoveTo(pieLabelXPos - GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont), pieLabelYPos2);
pieLabelYPos2 -= GetTextHeight(pVarFont);
}
else
{
MoveTo
(
pieX - GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont) - GetTextWidth
(
tempXchar,
pVarFont
),
pieY
);
pieLabelYPos2 = pieY - GetTextHeight(pVarFont);
}
}
else if((m + (dTemp >> 1) > 180) && (m + (dTemp >> 1) <= 270))
{
// check if we need to draw a line
if((dTemp < GetTextHeight(pVarFont)) || (pieLabelYPos2 < pieY))
{
pieLabelXPos = ctr - z - 5;
if((m + (dTemp >> 1)) < 225)
{
pieSectorXPos = pieX - 3;
pieSectorYPos = pieY;
}
else
{
pieSectorXPos = pieX;
pieSectorYPos = pieY - 3;
}
// check if slope of line is greater than -1.
// if it is we must adjust position of text to avoid the line
// intersecting the circumference of the pie chart
if((m + (dTemp >> 1)) < 270)
{
// make the slope equal to 1, this will make sure it does not intersect the circumference
if((abs(pieY - pieLabelYPos2) / abs(pieX - pieLabelXPos)) >= 1)
{
pieLabelXPos = ctr - (z + (z >> 1)) - 5;
if
(
(SHORT) (pieLabelXPos - GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont)) <=
(pCh->hdr.left + CH_MARGIN)
)
{
pieLabelXPos =
(
pCh->hdr.left +
CH_MARGIN +
GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont)
);
}
}
}
// The label will now exceed the chart dimension, so force the value to be printed aligned in
// y position with the previous value printed
if
(
(pieLabelYPos2) <
(pCh->hdr.top + GOL_EMBOSS_SIZE + CH_MARGIN + GetTextHeight(pCh->prm.pTitleFont))
)
{
if
(
!Line
(
pieSectorXPos,
pieSectorYPos,
pieLabelXPos + (GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont) >> 1),
pieLabelYPos2 + ((GetTextHeight(pVarFont) >> 1) * 3)
)
) return (0);
MoveTo
(
pieLabelXPos + (GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont) >> 1),
pieLabelYPos2 + GetTextHeight(pVarFont)
);
// adjust the next marker
pieLabelYPos3 += GetTextHeight(pVarFont);
}
else
{
// draw the line
if
(
!Line
(
pieSectorXPos,
pieSectorYPos,
pieLabelXPos,
pieLabelYPos2 + (GetTextHeight(pVarFont) >> 1)
)
) return (0);
MoveTo(pieLabelXPos - GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont), pieLabelYPos2);
pieLabelYPos2 -= GetTextHeight(pVarFont);
}
}
else
{
MoveTo
(
pieX - GetTextWidth(&tempStr[STR_CHAR_CNT - h], pVarFont) - GetTextWidth
(
tempXchar,
pVarFont
),
pieY - GetTextHeight(pVarFont)
);
pieLabelYPos2 = pieY - (GetTextHeight(pVarFont) << 1);
}
}
else if((m + (dTemp >> 1) > 270) && (m + (dTemp >> 1) <= 360))
{
// check if we need to draw a line
if((dTemp < GetTextHeight(pVarFont)) || (pieLabelYPos3 > pieY))
{
pieLabelXPos = ctr + z + GetTextHeight(pVarFont);
pieSectorXPos = pieX + 3;
pieSectorYPos = pieY - 2;
// draw the line
if
(
!Line
(
pieSectorXPos,
pieSectorYPos,
pieLabelXPos,
pieLabelYPos3 - (GetTextHeight(pVarFont) >> 1)
)
) return (0);
MoveTo(pieLabelXPos, pieLabelYPos3 - GetTextHeight(pVarFont));
pieLabelYPos3 += GetTextHeight(pVarFont);
}
else
{
MoveTo(pieX + 5, pieY - GetTextHeight(pVarFont));
pieLabelYPos3 = pieY + GetTextHeight(pVarFont);
}
}
state = PIE_DRAW_SECTOR_LOOP_STRINGS_RUN;
}
else
{
state = PIE_DRAW_SECTOR_LOOP_CONTINUE;
goto chrt_pie_draw_sector_loop_continue;
}
case PIE_DRAW_SECTOR_LOOP_STRINGS_RUN:
// now draw the strings of the values and/or percentages
SetColor(BLACK);
SetFont(pVarFont);
if(!OutText(&tempStr[STR_CHAR_CNT - h]))
return (0);
state = PIE_DRAW_SECTOR_LOOP_CONTINUE;
chrt_pie_draw_sector_loop_continue:
case PIE_DRAW_SECTOR_LOOP_CONTINUE:
j += dTemp;
varCtr--;
if(varCtr == 0)
{
if(GetState(pCh, CH_DONUT) == CH_DONUT)
{
state = PIE_DONUT_HOLE_DRAW;
goto chrt_pie_donut_hole_draw;
}
else
{
state = REMOVE;
return (1);
}
}
// check if more than one data series to be shown
if(ChGetShowSeriesCount(pCh) > 1)
{
pVar = ChGetNextShowData((DATASERIES *)pVar->pNextData);
if(pVar == NULL)
{
break;
}
}
k++;
state = PIE_DRAW_SECTOR_LOOP;
goto chrt_pie_draw_sector_loop;
chrt_pie_donut_hole_draw:
case PIE_DONUT_HOLE_DRAW:
SetColor(LIGHTGRAY);
if(!Circle(ctr, ctry, (z >> 1) - (z >> 3)))
return (0);
SetColor(pCh->hdr.pGolScheme->CommonBkColor);
if(!FillCircle(ctr, ctry, ((z >> 1) - (z >> 3)) - 1))
return (0);
state = REMOVE;
return (1);
}
return (1);
}
/*********************************************************************
* Function: ChAddDataSeries(CHART *pCh, WORD nSamples, WORD *pData, XCHAR *pName)
*
*
* Notes: Adds a new variable data structure in the linked list
* of variable datas. Number of samples is given with the
* array of the samples. If there is only one data
* nSamples is set to 1 with the address of the variable data.
*
*
********************************************************************/
DATASERIES *ChAddDataSeries(CHART *pCh, WORD nSamples, WORD *pData, XCHAR *pName)
{
DATASERIES *pVar = NULL, *pListVar;
pVar = (DATASERIES *)GFX_malloc(sizeof(DATASERIES));
if(pVar == NULL)
return (NULL);
// add the other parameters of the variable data
pVar->pSData = (XCHAR *)pName;
pVar->samples = nSamples;
pVar->pData = (WORD *)pData;
pVar->show = SHOW_DATA;
pVar->pNextData = NULL;
pListVar = pCh->pChData;
if(pCh->pChData == NULL)
pCh->pChData = pVar;
else
{
// search the end of the list and append the new data
while(pListVar->pNextData != NULL)
pListVar = pListVar->pNextData;
pListVar->pNextData = pVar;
}
// update the variable count before exiting
pCh->prm.seriesCount++;
return (DATASERIES *)pVar;
}
/*********************************************************************
* Function: ChRemoveDataSeries(CHART *pCh, WORD number)
*
*
* Notes: Removes a data series structure in the linked list
* of data series.
*
********************************************************************/
void ChRemoveDataSeries(CHART *pCh, WORD number)
{
DATASERIES *pVar = NULL, *pPrevVar = NULL;
WORD ctr = 1;
pVar = pCh->pChData;
// check if the list is empty
if(pVar == NULL)
return;
// check if there is only one entry
if(pVar->pNextData == NULL)
{
GFX_free(pVar);
pCh->pChData = NULL;
return;
}
if(number == 0)
{
// remove all
while(pVar != NULL)
{
pPrevVar = pVar;
pVar = pVar->pNextData;
// free the memory used by the item
GFX_free(pPrevVar);
}
return;
}
// there are more than one entry, remove the entry specified
while(ctr < number)
{
pPrevVar = pVar;
pVar = pVar->pNextData;
ctr++;
}
// remove the item from the list
pPrevVar->pNextData = pVar->pNextData;
// free the memory used by the item
GFX_free(pVar);
return;
}
/*********************************************************************
* Function: ChSetDataSeries(CHART *pCh, WORD seriesNum, BYTE status)
*
*
* Notes: Sets the specified data series number show flag to be set to
* SHOW_DATA or HIDE_DATA depending on the status.
* If the seriesNum is 0, it sets all the data series
* entries in the data series linked list. Returns the same passed
* number if successful otherwise -1 if unsuccesful.
*
********************************************************************/
SHORT ChSetDataSeries(CHART *pCh, WORD seriesNum, BYTE status)
{
DATASERIES *pListSer;
WORD ctr = 1;
pListSer = pCh->pChData;
// check if the list is empty
if(pListSer == NULL)
return (-1);
while(pListSer != NULL)
{
// check if we need to show all
if(seriesNum == 0)
pListSer->show = status;
else if(seriesNum == ctr)
{
pListSer->show = status;
break;
}
ctr++;
pListSer = pListSer->pNextData;
}
if(seriesNum == ctr)
return (seriesNum);
else
return (-1);
}
/*********************************************************************
* Function: ChSetSampleRange(CHART *pCh, WORD start, WORD end)
*
*
* Notes: Sets the sampling start and end points when drawing the chart.
* Depending on the number of data series with SHOW_DATA flag
* set and the values of end and start samples a single
* data series is drawn or multiple data series are drawn.
*
********************************************************************/
void ChSetSampleRange(CHART *pCh, WORD start, WORD end)
{
pCh->prm.smplStart = start;
if(end < start)
pCh->prm.smplEnd = start;
else
pCh->prm.smplEnd = end;
}
/*********************************************************************
* Function: ChSetValueRange(CHART *pCh, WORD min, WORD max)
*
*
* Notes: Sets the sampling start and end points when drawing the chart.
* Depending on the number of data series with SHOW_DATA flag
* set and the values of end and start samples a single
* data series is drawn or multiple data series are drawn.
*
********************************************************************/
void ChSetValueRange(CHART *pCh, WORD min, WORD max)
{
pCh->prm.valMin = min;
if(max < min)
pCh->prm.valMax = min;
else
pCh->prm.valMax = max;
}
/*********************************************************************
* Function: ChSetPercentRange(CHART *pCh, WORD min, WORD max)
*
*
* Notes: Sets the percentage range when drawing the chart. This affects
* bar charts only and CH_PERCENTAGE bit state is set.
*
********************************************************************/
void ChSetPercentRange(CHART *pCh, WORD min, WORD max)
{
pCh->prm.perMin = min;
if(max < min)
pCh->prm.perMax = min;
else
pCh->prm.perMax = max;
}
///////////////////// SIN and COS Tables from 0 to 45 deg /////////////////////
const WORD sinTable[] __attribute__((aligned(2))) =
{
0,
1143,
2287,
3429,
4571,
5711,
6850,
7986,
9120,
10251,
11380,
12504,
13625,
14742,
15854,
16961,
18063,
19160,
20251,
21336,
22414,
23485,
24549,
25606,
26655,
27696,
28728,
29752,
30766,
31771,
32767,
33753,
34728,
35692,
36646,
37589,
38520,
39439,
40347,
41242,
42125,
42994,
43851,
44694,
45524,
46340
};
const WORD cosTable[] __attribute__((aligned(2))) =
{
65535,
65525,
65495,
65445,
65375,
65285,
65175,
65046,
64897,
64728,
64539,
64330,
64102,
63855,
63588,
63301,
62996,
62671,
62327,
61964,
61582,
61182,
60762,
60325,
59869,
59394,
58902,
58392,
57863,
57318,
56754,
56174,
55576,
54962,
54330,
53683,
53018,
52338,
51642,
50930,
50202,
49459,
48701,
47929,
47141,
46340
};
/* */
void FillSector(SHORT x, SHORT y, WORD outLineColor)
{
WORD pixel;
SHORT left, right;
SHORT top, bottom;
SHORT xc, yc;
// scan down
top = bottom = yc = y;
left = right = xc = x;
while(1)
{
pixel = GetPixel(xc, yc);
if(pixel == outLineColor)
{
for(xc = left + 1; xc < right; xc++)
{
pixel = GetPixel(xc, yc);
if(pixel != outLineColor)
{
break;
}
}
if(xc == right)
break;
}
// left scan
left = xc;
do
{
left--;
pixel = GetPixel(left, yc);
} while(pixel != outLineColor);
while(!Line(xc, yc, left + 1, yc));
// right scan
right = xc;
pixel = GetPixel(right, yc);
do
{
right++;
pixel = GetPixel(right, yc);
} while(pixel != outLineColor);
while(!Line(xc, yc, right - 1, yc));
xc = (left + right) >> 1;
yc++;
}
// scan up
yc = y;
xc = x;
while(1)
{
pixel = GetPixel(xc, yc);
if(pixel == outLineColor)
{
for(xc = left + 1; xc < right; xc++)
{
pixel = GetPixel(xc, yc);
if(pixel != outLineColor)
{
break;
}
}
if(xc == right)
break;
}
// left scan
left = xc;
do
{
left--;
pixel = GetPixel(left, yc);
} while(pixel != outLineColor);
while(!Line(xc, yc, left + 1, yc));
// right scan
right = xc;
pixel = GetPixel(right, yc);
do
{
right++;
pixel = GetPixel(right, yc);
} while(pixel != outLineColor);
while(!Line(xc, yc, right - 1, yc));
xc = (left + right) >> 1;
yc--;
}
}
/* */
void GetCirclePoint(SHORT radius, SHORT angle, SHORT *x, SHORT *y)
{
DWORD rad;
//DWORD_VAL ss;
SHORT ang;
SHORT temp;
ang = angle % 45;
if((angle / 45) & 0x01)
ang = 45 - ang;
rad = radius;
rad *= cosTable[ang];
*x = ((DWORD_VAL) rad).w[1];
//ss.Val = rad;
//*x = ss.w[1];
rad = radius;
rad *= sinTable[ang];
*y = ((DWORD_VAL) rad).w[1];
//ss.Val = rad;
//*y = ss.w[1];
if(((angle > 45) && (angle < 135)) || ((angle > 225) && (angle < 315)))
{
temp = *x;
*x = *y;
*y = temp;
}
if((angle > 90) && (angle < 270))
{
*x = -*x;
}
if((angle > 180) && (angle < 360))
{
*y = -*y;
}
}
/* */
WORD DrawSector(SHORT cx, SHORT cy, SHORT outRadius, SHORT angleFrom, SHORT angleTo, WORD outLineColor)
{
typedef enum
{
SEC_DRAW_IDLE,
SEC_DRAW_EDGE1,
SEC_DRAW_EDGE2,
SEC_DRAW_EDGE3,
SEC_DRAW_EDGE4,
SEC_DRAW_EDGE5,
SEC_DRAW_FILLINIT,
SEC_DRAW_FILL,
} DRAW_SECTOR_STATES;
static SHORT x1, y1, x2, y2, x3, y3;
static SHORT angleMid;
static WORD tempColor;
LONG temp;
static DRAW_SECTOR_STATES sectorState = SEC_DRAW_IDLE;
switch(sectorState)
{
case SEC_DRAW_IDLE:
// calculate points
angleMid = (angleTo + angleFrom) >> 1;
GetCirclePoint(outRadius, angleFrom, &x1, &y1);
GetCirclePoint(outRadius, angleTo, &x2, &y2);
x1 += cx;
y1 += cy;
x2 += cx;
y2 += cy;
// grab the current color value for later use
tempColor = GetColor();
// check if we need to draw the edges
// special case for single data shown on pie chart
// we remove the line drawn at angle 0
if(!((angleFrom == 0) && (angleTo == 360)))
{
sectorState = SEC_DRAW_EDGE1;
// special case for small angles
GetCirclePoint(outRadius - 1, angleFrom + 1, &x3, &y3);
x3 += cx;
y3 += cy;
goto sec_draw_edge1;
}
else
{
sectorState = SEC_DRAW_FILLINIT;
goto sec_draw_fillinit;
}
case SEC_DRAW_EDGE1:
sec_draw_edge1 : if(!Line(x3, y3, cx, cy)) return (0);
GetCirclePoint(outRadius - 1, angleTo - 1, &x3, &y3);
x3 += cx;
y3 += cy;
sectorState = SEC_DRAW_EDGE2;
case SEC_DRAW_EDGE2:
if(!Line(x3, y3, cx, cy))
return (0);
GetCirclePoint(outRadius - 1, angleMid, &x3, &y3);
x3 += cx;
y3 += cy;
sectorState = SEC_DRAW_EDGE3;
case SEC_DRAW_EDGE3:
if(!Line(x3, y3, cx, cy))
return (0);
SetColor(outLineColor);
sectorState = SEC_DRAW_EDGE4;
case SEC_DRAW_EDGE4:
if(!Line(x1, y1, cx, cy))
return (0);
sectorState = SEC_DRAW_EDGE5;
case SEC_DRAW_EDGE5:
if(!Line(x2, y2, cx, cy))
return (0);
sectorState = SEC_DRAW_FILLINIT;
case SEC_DRAW_FILLINIT:
sec_draw_fillinit : SetColor(tempColor);
temp = ((x1 - x2) * (x1 - x2));
temp += ((y1 - y2) * (y1 - y2));
if(((DWORD) temp <= (DWORD) 16) && ((angleTo - angleFrom) < 90))
{
sectorState = SEC_DRAW_IDLE;
return (1);
}
GetCirclePoint(outRadius - 2, angleMid, &x3, &y3);
x3 += cx;
y3 += cy;
sectorState = SEC_DRAW_FILL;
case SEC_DRAW_FILL:
// FillSector() is a blocking call
// making it non-blocking will further add delay to the rendering
FillSector(x3, y3, outLineColor);
sectorState = SEC_DRAW_IDLE;
break;
} // end of switch()
return (1);
}
#endif // USE_CHART
|