#include "stdafx.h"
#include "ReportDict.h"

#include <math.h>

#define APL_ENABLE_PRO_RATA		0x001
#define APL_ENABLE_SHOW_LINES	0x002
#define APL_ENABLE_SHOW_NAMES	0x004
#define APL_ENABLE_SHOW_PERCENTS 0x008
#define APL_ENABLE_ANGLE_TEXTS  0x010
#define APL_ENABLE_HIDE_VALUES	0x020
#define PI 3.1415926535897932

CCoordinateLine::CCoordinateLine(CaplReportMgr* ReportMgr)
{
	m_ReportMgr = ReportMgr;

	m_inst = NULL;
	m_font_inst = NULL;
	m_name = _T("X");
	m_pParam = NULL;
	
	m_line_width = AfxGetApp()->GetProfileInt(_T("DefaultSettings"),_T("LineWidth"),1);
	m_line_color = 0; //черный
	m_font_color = 0; //черный
	m_lines_color = RGB(200, 200, 200);//светло серый

	m_TextVector = aplText_Horz;
	
	m_pDSColumn = NULL;

	memset(&m_font, 0, sizeof(LOGFONT));

	m_font.lfOutPrecision = OUT_DEFAULT_PRECIS;
	m_font.lfClipPrecision = CLIP_DEFAULT_PRECIS;
	m_font.lfQuality = DEFAULT_QUALITY;
	m_font.lfPitchAndFamily = DEFAULT_PITCH;
	m_font.lfItalic = 255;
	m_font.lfHeight = 80;
	m_font.lfWeight = FW_NORMAL;
	m_font.lfCharSet = RUSSIAN_CHARSET;
	
	_strcpy(m_font.lfFaceName, _T("Arial"));

	m_skip_elements = 1;
	m_NumRowsInGroup = 10;
	m_bShowLines = true;
	m_bProRata = true;
	m_bHideValues = false;
	m_bShowNames = true;
	m_bShowPercents = true;

	m_bCalcFromZero = false;
	
	m_pGistogram = NULL;
}

CCoordinateLine::~CCoordinateLine()
{
}

void CCoordinateLine::Update(bool set)
{
	if(m_ReportMgr==NULL) return;

	int Flags;
	if(set) //storing
	{
		if(m_inst==NULL)
			m_inst = m_ReportMgr->m_data->CreateInstance(m_ReportMgr->e_apl_coordinate_line);
		if(m_font_inst==NULL)
			m_font_inst = m_ReportMgr->m_data->CreateInstance(m_ReportMgr->e_font);
		if(m_pParam)
		{
			if(m_pParam->m_inst==NULL)
				m_pParam->Update(set);
			m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_parametr, m_pParam->m_inst);
		}
		else
			m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_parametr, (CaplInstance*)NULL);
		if(m_pDSColumn)
		{
			if(m_pDSColumn->m_inst==NULL)
			{
				m_ReportMgr->m_base_query->Update(true);
				m_ReportMgr->m_header->Update(true);
			}
			m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_ds_column, m_pDSColumn->m_inst);
		}
		else
			m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_ds_column, (CaplInstance*)NULL);

		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_font, m_font_inst);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_name, m_name);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_line_color, (int)m_line_color);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_line_width, m_line_width);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_font_color, (int)m_font_color);

		Flags = 0;
		if(m_bProRata)
			Flags|=APL_ENABLE_PRO_RATA;
		if(m_bShowLines)
			Flags|=APL_ENABLE_SHOW_LINES;
		if(m_bHideValues)
			Flags|=APL_ENABLE_HIDE_VALUES;
		if(m_bShowNames)
			Flags|=APL_ENABLE_SHOW_NAMES;
		if(m_bShowPercents)
			Flags|=APL_ENABLE_SHOW_PERCENTS;
		if(m_TextVector==aplText_Angle)
			Flags|=APL_ENABLE_ANGLE_TEXTS;
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_lines_color, (int)m_lines_color);
		m_ReportMgr->m_data->PutAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_flags, Flags);

		m_ReportMgr->SetFontValue(m_font_inst, m_font);
	}
	else if(m_inst!=NULL)
	{

		CaplInstance *Inst;
		int tmpInt;

		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_name, m_name);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_line_color, tmpInt);
		m_line_color = (COLORREF)tmpInt;
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_line_width, m_line_width);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_font_color, tmpInt);
		m_font_color = (COLORREF)tmpInt;
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_font, m_font_inst);
		m_ReportMgr->GetFontValue(m_font_inst, m_font);

		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_ds_column, Inst);
		m_pDSColumn = m_ReportMgr->FindColumnByInst(Inst);
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_parametr, Inst);
		if (Inst!=NULL)
		{
			tmpInt = m_ReportMgr->m_data_source.m_params.Find(Inst);
			if(tmpInt>-1)
				m_pParam = m_ReportMgr->m_data_source.m_params[tmpInt];
		}
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_lines_color, tmpInt);
		m_lines_color = (COLORREF)tmpInt;
		m_ReportMgr->m_data->GetAttr(m_inst, m_ReportMgr->a_apl_coordinate_line_flags, Flags);
		m_bProRata = (Flags&APL_ENABLE_PRO_RATA)!=0?true:false;
		m_bHideValues = (Flags&APL_ENABLE_HIDE_VALUES)!=0?true:false;
		m_bShowLines = (Flags&APL_ENABLE_SHOW_LINES)!=0?true:false;
		m_bShowNames = (Flags&APL_ENABLE_SHOW_NAMES)!=0?true:false;
		m_bShowPercents = (Flags&APL_ENABLE_SHOW_PERCENTS)!=0?true:false;
		m_TextVector = (Flags&APL_ENABLE_ANGLE_TEXTS)!=0?aplText_Angle:aplText_Horz;
	}
}

bool CCoordinateLine::LoadDataFromMem(BYTE *pByte, int &ind)
{
	int* pInt = NULL;
	LOGFONT	*pLF = NULL;
	COLORREF *pCR = NULL;
	int int_size = sizeof(int);
	int cnt;
	TCHAR* pCh = NULL;
	
	//имя
	pInt = (int*)&pByte[ind];
	cnt = *pInt;
	ind+=int_size;
	m_name.Empty();
	for(int i=0; i<cnt;i++)
	{
		pCh = (TCHAR*)&pByte[ind];
		m_name+=*pCh;
		ind+=sizeof(TCHAR);
	}
	//шрифт
	pLF = (LOGFONT*)&pByte[ind];
	m_font = *pLF;
	ind+=sizeof(LOGFONT);
	//цвета
	pCR = (COLORREF*)&pByte[ind];
	m_font_color = *pCR;
	ind+=sizeof(COLORREF);
	
	pCR = (COLORREF*)&pByte[ind];
	m_line_color = *pCR;
	ind+=sizeof(COLORREF);
	
	pCR = (COLORREF*)&pByte[ind];
	m_lines_color = *pCR;
	ind+=sizeof(COLORREF);
	
	//толщина линии
	pInt = (int*)&pByte[ind];
	m_line_width = *pInt;
	ind+=int_size;
	
	//отображать разлиновку
	pInt = (int*)&pByte[ind];
	m_bShowLines = (*pInt==1);
	ind+=int_size;

	//распределять значения пропорционально
	pInt = (int*)&pByte[ind];
	m_bProRata = (*pInt==1);
	ind+=int_size;

	//отображать значения
	pInt = (int*)&pByte[ind];
	m_bShowNames = (*pInt==1);
	ind+=int_size;

	//отображать процентные значения
	pInt = (int*)&pByte[ind];
	m_bShowPercents = (*pInt==1);
	ind+=int_size;

	//отображать текст под углом
	pInt = (int*)&pByte[ind];
	m_TextVector = (*pInt==1)?aplText_Angle:aplText_Horz;
	ind+=int_size;

	//отображать текст
	pInt = (int*)&pByte[ind];
	m_bHideValues = (*pInt==1);
	ind+=int_size;

	return true;
}

void CCoordinateLine::CopyToMem(BYTE* pByte, int &ind)
{
	int* pInt = NULL;
	LOGFONT	*pLF = NULL;
	COLORREF *pCR = NULL;
	int int_size = sizeof(int);
	TCHAR* pCh = NULL;

	//имя
	pInt = (int*)&pByte[ind];
	*pInt = m_name.GetLength();
	ind+=int_size;
	for(int i=0; i<m_name.GetLength();i++)
	{
		pCh = (TCHAR*)&pByte[ind];
		*pCh = m_name[i];
		ind+=sizeof(TCHAR);
	}
	//шрифт
	pLF = (LOGFONT*)&pByte[ind];
	*pLF = m_font;
	ind+=sizeof(LOGFONT);
	//цвета
	pCR = (COLORREF*)&pByte[ind];
	*pCR = m_font_color;
	ind+=sizeof(COLORREF);

	pCR = (COLORREF*)&pByte[ind];
	*pCR = m_line_color;
	ind+=sizeof(COLORREF);
	
	pCR = (COLORREF*)&pByte[ind];
	*pCR = m_lines_color;
	ind+=sizeof(COLORREF);

	//толщина линии
	pInt = (int*)&pByte[ind];
	*pInt = m_line_width;
	ind+=int_size;

	//отображать разлиновку
	pInt = (int*)&pByte[ind];
	*pInt = m_bShowLines?1:0;
	ind+=int_size;

	//распределять значения пропорционально
	pInt = (int*)&pByte[ind];
	*pInt = m_bProRata?1:0;
	ind+=int_size;

	//отображать значения
	pInt = (int*)&pByte[ind];
	*pInt = m_bShowNames?1:0;
	ind+=int_size;

	//отображать процентные значения
	pInt = (int*)&pByte[ind];
	*pInt = m_bShowPercents?1:0;
	ind+=int_size;

	//отображать текст под углом
	pInt = (int*)&pByte[ind];
	*pInt = (m_TextVector==aplText_Angle)?1:0;
	ind+=int_size;

	//отображать текст
	pInt = (int*)&pByte[ind];
	*pInt = m_bHideValues?1:0;
	ind+=int_size;
}

int CCoordinateLine::GetMemSize()
{
	//наименование
	int size = sizeof(int);
	size+=m_name.GetLength()*sizeof(TCHAR);
	size+=sizeof(LOGFONT);//шрифт
	size+=sizeof(COLORREF);//цвет текста
	size+=sizeof(COLORREF);//цвет линии
	size+=sizeof(COLORREF);//цвет разлиновки
	size+=sizeof(int);//толщина линии
	size+=sizeof(int);//отображать разлиновку
	size+=sizeof(int);//распределять значения пропорционально
	size+=sizeof(int);//отображать значения
	size+=sizeof(int);//отображать процентные значения
	size+=sizeof(int);//отображать текст под углом
	size+=sizeof(int);//отображать текст

	return size;
}

void CCoordinateLine::Delete()
{
	if(m_ReportMgr==NULL) return;
	if(m_ReportMgr->m_data==NULL) return;
	
	if(m_inst==NULL) return;

	m_ReportMgr->m_data->DeleteInstance(m_inst);
	if(m_font_inst)
		m_ReportMgr->m_data->DeleteInstance(m_font_inst);
}

CCoordinateLine& CCoordinateLine::operator=(const CCoordinateLine& CLine)
{
	m_inst = CLine.m_inst;
	m_font_inst = CLine.m_font_inst;
	m_name = CLine.m_name;
	m_pParam = CLine.m_pParam;
	
	m_line_width = CLine.m_line_width;
	m_line_color = CLine.m_line_color;
	m_font_color = CLine.m_font_color;
	
	m_pDSColumn = CLine.m_pDSColumn;
	m_font = CLine.m_font;

	return *this;
}

void CCoordinateLine::OffsetDrawRect(CDC *pDC, CRect &rect, double scale, CDataSource* pDS, double dMin, double &dRange)
{
	if(pDC == NULL) return;
	
	LOGFONT		lf_small = m_font;
	LOGFONT		lf_font = m_font;
	CFont font, small_font, *old_font;

	lf_small.lfHeight-=40;
	lf_small.lfHeight = (int)(((double)lf_small.lfHeight)*scale);
	lf_font.lfHeight = (int)(((double)lf_font.lfHeight)*scale);

	// шрифт названия осей
	font.CreatePointFontIndirect(&lf_font, pDC);
	// шрифт значений на осях
	small_font.CreatePointFontIndirect(&lf_small, pDC);
	
	old_font = pDC->SelectObject(&small_font);
	
	if(m_pGistogram && m_pGistogram->m_ViewType==APL_GISTOGRAM_VIEW_TYPE_CYCLE) // круговая гистограмма (таблетка)
	{
		if(m_pDSColumn) // ось X
		{
			rect = CalcCaptionCycle(pDC, rect, scale, pDS, dMin, dRange);
		}
		else // ось Y
		{
		}
	}
	else // график и линейная гистограмма (столбики)
	{
		CString sName = m_pParam?m_pParam->GetText():m_name;
		if(m_pDSColumn) // ось X
		{
			int iDxBottom = CalcCaptionRows(pDC, rect, scale, pDS, dMin, dRange);

			rect.bottom+=iDxBottom+5;
			pDC->SelectObject(&font);
			if(!sName.IsEmpty())
				rect.bottom+=pDC->GetTextExtent(sName).cy+5;
			int right_move = pDC->GetTextExtent(sName).cx/2+30;
			if(m_pGistogram && m_pGistogram->m_ViewType==APL_GISTOGRAM_VIEW_TYPE_TIMELINE) // временная гистограмма
			{
				CString sVal;
				CArray<Timeline_values_struct> values;
				int rows = pDS->GetRows();
				int ind = pDS->GetHeader()->Find(m_pDSColumn->GetName());
				int indX = pDS->GetHeader()->Find(m_pGistogram->m_Elements[0]->GetColumnName());
				CDataSource* pUseDS = pDS;
				bool bAngle = m_TextVector==aplText_Angle ? true : false; // под углом
				if(m_bHideValues == true) // не отображаются подписи
				{
					// поищем, может на странице существует другая гистограмма с отображающимися подписями?
					if(m_pGistogram->m_owner_sheet)
					{
						for(int i=0;i<m_pGistogram->m_owner_sheet->m_els.GetSize();i++)
						{
							CReportElement* elem = m_pGistogram->m_owner_sheet->m_els.GetAt(i);
							if(elem==0) continue;
							CGistogramElement* g_elem = dynamic_cast<CGistogramElement*>(elem);
							if(g_elem==0) continue;
							if(g_elem->m_ViewType!=APL_GISTOGRAM_VIEW_TYPE_TIMELINE) continue;
							if(g_elem->m_XLine.m_bHideValues == true) continue;
							if(g_elem->m_Elements.Size == 0) continue;
							CRect r_cur, r_base;
							m_pGistogram->GetRect(r_cur, scale, 1);
							g_elem->GetRect(r_base, scale, 1);
							if(r_cur.left != r_base.left || r_cur.right != r_base.right) continue;
							pUseDS = &g_elem->m_DataSource;
							rows = pUseDS->GetRows();
							ind = pUseDS->GetHeader()->Find(g_elem->m_XLine.m_pDSColumn->GetName());
							indX = pUseDS->GetHeader()->Find(g_elem->m_Elements[0]->GetColumnName());
							bAngle = g_elem->m_XLine.m_TextVector==aplText_Angle ? true : false;
							break;
						}
					}
				}
				if(indX>=0)
				{
					// прочитаем переданные значения
					for(int i=0; i<rows; i++)
					{
						pUseDS->GetAt(i, indX, sVal);
						CStringArray sArr, AdditionalCoordinateArray;
						bool bAdditionalLine;
						Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
						Timeline_values_struct new_val;
						Report_ConvertAggr2TimeLine(sArr, new_val);
						values.Add(new_val);
					}
					Report_SortTimeLine(values, true); // отсортируем интервалы по времени их окончания
				}
				// узнаем длину последней подписи по оси X
				if(values.GetSize())
				{
					pDC->SelectObject(&small_font);
					pUseDS->GetAt(values.GetSize()-1, ind, sVal);
					COleDateTime dt(values[values.GetSize()-1].end);
					sVal = dt.Format(sVal);
					int len = pDC->GetTextExtent(sVal).cx;
					if(bAngle) // под углом
						len = (int)sqrt((double)len*len/2);
					if(right_move<len)
						right_move = len;
				}
			}
			rect.right-=right_move;
		}
		else // ось Y
		{
			if(m_pGistogram && m_pGistogram->m_ViewType==APL_GISTOGRAM_VIEW_TYPE_TIMELINE) // временная гистограмма
			{
				// определим, есть ли значения, отличные от 1
				CString sVal;
				bool bUseValues = false;
				int indX = pDS->GetHeader()->Find(m_pGistogram->m_Elements[0]->GetColumnName());
				if(indX>=0)
				{
					// прочитаем переданные значения
					int rows = pDS->GetRows();
					for(int i=0; i<rows; i++)
					{
						pDS->GetAt(i, indX, sVal);
						CStringArray sArr, AdditionalCoordinateArray;
						bool bAdditionalLine;
						Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
						Timeline_values_struct new_val;
						Report_ConvertAggr2TimeLine(sArr, new_val);
						if(new_val.is_value_begin == true || new_val.is_value_end == true)
						{
							bUseValues = true;
							break;
						}
					}
				}

				if(bUseValues)
					rect.left+=pDC->GetTextExtent(_T("0.000")).cx+5;
				else
					rect.left+=5;
			}
			else
				rect.left+=pDC->GetTextExtent(_T("0.000")).cx+5;
			pDC->SelectObject(&font);
			if(!sName.IsEmpty())
				rect.top-=pDC->GetTextExtent(sName).cy+15;
		}
	}

	pDC->SelectObject(old_font);
	font.DeleteObject();
	small_font.DeleteObject();
}

void CCoordinateLine::Draw(CDC *pDC, CRect rect, double scale, CDataSource* pDS, double dMin, double &dRange, Gdiplus::Graphics* graphics /*= NULL*/)
{
	if(pDC == NULL) return;
	if(pDS == NULL) return;
	if(dRange < 0.00000001&&dRange > -0.00000001)
		dRange = 1;

	if(m_pGistogram && m_pGistogram->m_ViewType==APL_GISTOGRAM_VIEW_TYPE_CYCLE) // круговая гистограмма (таблетка)
	{
		DrawCycleGistogram(pDC, rect, scale, pDS, dMin, dRange, graphics);
		return;
	}
	if(m_pGistogram && m_pGistogram->m_ViewType==APL_GISTOGRAM_VIEW_TYPE_TIMELINE) // временная гистограмма
	{
		DrawTimeLineGistogram(pDC, rect, scale, pDS, dMin, dRange);
		return;
	}

	CPen pen, *old_pen;
	CPen lines_pen;
	CFont font, small_font, *old_font;
	LOGBRUSH lb;
	LOGFONT		lf_small = m_font;
	LOGFONT		lf_font = m_font;
	int rows, cols;
	int i;
	int x, y;
	int ind;
	int OldBkMode;
	int elem;
	double iMax, iMin;
	int tmp;
	CString sVal;
	double dVal;
	COLORREF old_color;

	lf_small.lfHeight-=40;
	lf_small.lfHeight = (int)(((double)lf_small.lfHeight)*scale);
	lf_font.lfHeight = (int)(((double)lf_font.lfHeight)*scale);

	lb.lbHatch = 0;
	lb.lbStyle = BS_SOLID;
	lb.lbColor = m_line_color;

	pDS->GetSize(rows, cols);

	pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_SQUARE, (int)(m_line_width*scale), &lb);
	// шрифт названия осей
	font.CreatePointFontIndirect(&lf_font, pDC);
	// шрифт значений на осях
	small_font.CreatePointFontIndirect(&lf_small, pDC);

	old_color = pDC->SetTextColor(m_font_color);
	OldBkMode = pDC->SetBkMode(TRANSPARENT);
	old_pen = pDC->SelectObject(&pen);
	old_font = pDC->SelectObject(&small_font);

	CString sName = m_pParam?m_pParam->GetText():m_name;

	if(m_pDSColumn) // ось X
	{
		if(m_pGistogram==NULL) //график
		{
			pDC->MoveTo(rect.left, rect.bottom);
			pDC->LineTo(rect.right, rect.bottom);
		}
		else //гистограмма
		{
			double angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
			int dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));
			pDC->MoveTo(rect.right, rect.bottom+m_pGistogram->m_Volume);
			pDC->LineTo(rect.left+dx, rect.bottom+m_pGistogram->m_Volume);
			pDC->LineTo(rect.left, rect.bottom);
			pDC->LineTo(rect.right-dx, rect.bottom);
			pDC->LineTo(rect.right, rect.bottom+m_pGistogram->m_Volume);
		}

		pDC->SelectObject(old_pen);
		pen.DeleteObject();
		pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_FLAT, 1, &lb);
		lb.lbColor = m_lines_color;
		lines_pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_FLAT, 1, &lb);
		old_pen = pDC->SelectObject(&pen);

		int iDxBottom = 1;
		ind = pDS->GetHeader()->Find(m_pDSColumn->GetName());
		if(ind>-1)
		{
			if(m_pGistogram==NULL) //график
			{
				elem = -1;
				double dPrevVal;
				for(i=0; i<rows; i++)
				{
					pDS->GetAt(i, ind, sVal);
					dVal = __atof(sVal);
					if(i>0 && dPrevVal==dVal) continue;
					dPrevVal = dVal;
					if(m_bProRata)
						x = (int)(i*(double)(rect.Width())/(rows-1));
					else
						x = (int)((dVal-dMin)*(double)(rect.Width())/dRange);
					pDC->MoveTo(rect.left + x, rect.bottom-10);
					pDC->LineTo(rect.left + x, rect.bottom+10);
					elem++;
					if(elem==m_skip_elements || (i==rows-1 && elem<=(int)(m_skip_elements*0.5)) || elem==0)
					{
						if(m_bShowLines)
						{
							pDC->SelectObject(&lines_pen);
							pDC->MoveTo(rect.left + x, rect.bottom-10);
							pDC->LineTo(rect.left + x, rect.top+10);
							pDC->SelectObject(&pen);
							pDC->MoveTo(rect.left + x, rect.bottom-10);
							pDC->LineTo(rect.left + x, rect.bottom+10);
						}
						elem = 0;
					}
				}

				iDxBottom = CalcCaptionRows(pDC, rect, scale, pDS, dMin, dRange, true);
			}
			else //гистограмма
			{
				double angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
				int dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));
				iDxBottom = CalcCaptionRows(pDC, rect, scale, pDS, dMin, dRange, true);
				rect.left+=dx;
				rect.bottom+=m_pGistogram->m_Volume;

				double width = (double)rect.Width()/rows;
				for(i=0; i<rows+1; i+=m_skip_elements)
				{
					x = (int)(i*width);
					if(m_bShowLines)
					{
						pDC->SelectObject(&lines_pen);
						pDC->MoveTo(rect.left + x, rect.bottom);
						pDC->LineTo(rect.left + x, rect.top+10);
						//засечки по оси X
						pDC->MoveTo(rect.left + x, rect.bottom);
						pDC->LineTo(rect.left + x-dx, rect.bottom-m_pGistogram->m_Volume);
					}
				}
			}
		}
		y = iDxBottom+5;
		pDC->SelectObject(&font);
		if(!sName.IsEmpty())
		{
			x = rect.right+30-pDC->GetTextExtent(sName).cx/2;
			if(m_pGistogram==NULL) //график
				pDC->TextOut(x, rect.bottom-y, sName);
			else //гистограмма
			{
				double angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
				int dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));
				pDC->TextOut(x-dx, rect.bottom-y-m_pGistogram->m_Volume, sName);
			}
		}
	}
	else // ось Y
	{
		double angel = 0;
		int dx = 0;
		if(m_pGistogram==NULL) //график
		{
			pDC->MoveTo(rect.left, rect.bottom-10);
			pDC->LineTo(rect.left, rect.top+10);
		}
		else //гистограмма
		{
			angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
			dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));

			pDC->MoveTo(rect.left+dx, rect.top);
			pDC->LineTo(rect.left+dx, rect.bottom+m_pGistogram->m_Volume);
			pDC->LineTo(rect.left, rect.bottom);
			pDC->LineTo(rect.left, rect.top-m_pGistogram->m_Volume);
			pDC->LineTo(rect.left+dx, rect.top);

			rect.left+=dx;
			rect.bottom+=m_pGistogram->m_Volume;
		}

		pDC->SelectObject(old_pen);
		pen.DeleteObject();
		pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_FLAT, 1, &lb);
		lb.lbColor = m_lines_color;
		lines_pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_FLAT, 1, &lb);
		old_pen = pDC->SelectObject(&pen);

		//определяем максимальное
		iMax = dMin+dRange;
		//определяем минимальное
		if(m_bCalcFromZero)
			iMin = dMin<0?(int)(dMin+0.999):0;
		else
			iMin = (int)(dMin+0.999);

		y=0;
		sVal.Format(_T("%d"), iMin);
		x=pDC->GetTextExtent(sVal).cx;
		y+=pDC->GetTextExtent(sVal).cy/2;
		if(m_pGistogram==NULL) //график
			pDC->TextOut(rect.left-15-x, rect.bottom+y, sVal);
		else //гистограмма
			pDC->TextOut(rect.left-15-x-dx, rect.bottom+y-m_pGistogram->m_Volume, sVal);

		int iMultiCnt = 0;
		double dTmpRange(dRange);
		while(iMax==iMin || ((int)(dTmpRange))==0)
		{
			dTmpRange *=10;
			iMultiCnt ++;
			iMax = iMin+(int)(dTmpRange+0.9999);
		}

		if(iMultiCnt>0)
		{
			dRange = iMax;
			for(int l_i(0); l_i<iMultiCnt; l_i++)
				dRange/=10;
		}

		if(iMax!=iMin)
		{
			y = -(int)((iMax)*(double)(rect.Height())/iMax);
			if(m_bShowLines)
			{
				pDC->SelectObject(&lines_pen);
				if(m_pGistogram==NULL) //график
				{
					pDC->MoveTo(rect.left-10, rect.bottom+y);
					pDC->LineTo(rect.right+10, rect.bottom+y);
					pDC->SelectObject(&pen);
					pDC->MoveTo(rect.left-10, rect.bottom+y);
					pDC->LineTo(rect.left+10, rect.bottom+y);
				}
				else //гистограмма
				{
					pDC->MoveTo(rect.left-dx, rect.bottom+y-m_pGistogram->m_Volume);
					pDC->LineTo(rect.left, rect.bottom+y);
					pDC->LineTo(rect.right+10, rect.bottom+y);
				}
			}
			else if(m_pGistogram==NULL) //график
			{
				pDC->MoveTo(rect.left-10, rect.bottom+y);
				pDC->LineTo(rect.left+10, rect.bottom+y);
			}
			if(iMultiCnt==0)
			{
				sVal.Format(_T("%g"), iMax);
			}
			else
			{
				int i_loc;
				double dTmpVal_l = iMax;
				for(i_loc=0; i_loc<iMultiCnt; i_loc++)
					dTmpVal_l/=10;
				sVal.Format(_T("%g"), dTmpVal_l);
			}
			x=pDC->GetTextExtent(sVal).cx;
			y+=pDC->GetTextExtent(sVal).cy/2;
			if(m_pGistogram==NULL) //график
				pDC->TextOut(rect.left-15-x, rect.bottom+y, sVal);
			else //гистограмма
				pDC->TextOut(rect.left-dx-15-x, rect.bottom+y-m_pGistogram->m_Volume, sVal);
		}
		//теперь разлиновываем по целым, если линий будет более 10, то по 10
		m_NumRowsInGroup = 1;
		int lines = 1;
		elem=-1;
		tmp = (int)(iMax-iMin);
		while(tmp>10)
		{
			m_NumRowsInGroup *=10;
			tmp/=10;
		}
		m_NumRowsInGroup = m_NumRowsInGroup/2>0?m_NumRowsInGroup/2:(m_NumRowsInGroup>1?m_NumRowsInGroup:1);
		lines = m_NumRowsInGroup>=10?m_NumRowsInGroup/10:1;

		for(i=(int)iMin; i<(int)iMax; i+=lines)
		{
			if((iMax-iMin)>m_NumRowsInGroup)
			{
				if((double)((int)(i)/lines)!=(double)(i)/lines) continue;

				if(m_pGistogram==NULL) //график
				{
					y = -(int)((i)*(double)(rect.Height())/(iMax));
					pDC->MoveTo(rect.left-7, rect.bottom+y);
					pDC->LineTo(rect.left+7, rect.bottom+y);
				}

				if((double)((int)(i)/m_NumRowsInGroup)!=(double)(i)/m_NumRowsInGroup) continue;
			}
			y = -(int)((i)*(double)(rect.Height())/(iMax));
			if(m_pGistogram==NULL) //график
			{
				pDC->MoveTo(rect.left-10, rect.bottom+y);
				pDC->LineTo(rect.left+10, rect.bottom+y);
			}
			elem++;
			if(elem==m_skip_elements || (i==iMax && elem<=(int)(m_skip_elements*0.5)) || elem==0)
			{
				if(m_bShowLines && y!=0)
				{
					pDC->SelectObject(&lines_pen);
					if(m_pGistogram==NULL) //график
					{
						pDC->MoveTo(rect.left-10, rect.bottom+y);
						pDC->LineTo(rect.right+10, rect.bottom+y);
						pDC->SelectObject(&pen);
						pDC->MoveTo(rect.left-10, rect.bottom+y);
						pDC->LineTo(rect.left+10, rect.bottom+y);
					}
					else //гистограмма
					{
						pDC->MoveTo(rect.left-dx, rect.bottom+y-m_pGistogram->m_Volume);
						pDC->LineTo(rect.left, rect.bottom+y);
						pDC->LineTo(rect.right+10, rect.bottom+y);
					}
				}
				if(iMultiCnt==0)
					sVal.Format(_T("%d"), i);
				else
				{
					int i_loc;
					double dTmpVal_l = i;
					for(i_loc=0; i_loc<iMultiCnt; i_loc++)
						dTmpVal_l/=10;
					sVal.Format(_T("%g"), dTmpVal_l);
				}
				x=pDC->GetTextExtent(sVal).cx;
				y+=pDC->GetTextExtent(sVal).cy/2;
				if(m_pGistogram==NULL) //график
					pDC->TextOut(rect.left-15-x, rect.bottom+y, sVal);
				else //гистограмма
					pDC->TextOut(rect.left-15-x-dx, rect.bottom+y-m_pGistogram->m_Volume, sVal);
				elem=0;
			}
		}

		if(!sName.IsEmpty())
		{
			x = rect.left-pDC->GetTextExtent(sName).cx/2;
			if(x<rect.left-pDC->GetTextExtent(_T("00.000")).cx) x = rect.left-pDC->GetTextExtent(_T("00.000")).cx;
			pDC->SelectObject(&font);
			pDC->TextOut(x, rect.top+15+pDC->GetTextExtent(sName).cy, sName);
		}
	}
	pDC->SetTextColor(old_color);
	pDC->SetBkMode(OldBkMode);
	pDC->SelectObject(old_font);
	pDC->SelectObject(old_pen);
	pen.DeleteObject();
	lines_pen.DeleteObject();
	font.DeleteObject();
	small_font.DeleteObject();
}

void CCoordinateLine::DrawTimeLineGistogram(CDC *pDC, CRect rect, double scale, CDataSource* pDS, double dMin, double &dRange)
{
	if(dRange < 0.00000001&&dRange > -0.00000001)
		dRange = 1;
	if(pDC == NULL) return;
	if(pDS == NULL) return;

	CPen pen, *old_pen;
	CPen lines_pen;
	CFont font, small_font, *old_font;
	LOGBRUSH lb;
	LOGFONT		lf_small = m_font;
	LOGFONT		lf_font = m_font;
	int rows, cols;
	int i;
	int x, y;

	lf_small.lfHeight-=40;
	lf_small.lfHeight = (int)(((double)lf_small.lfHeight)*scale);
	lf_font.lfHeight = (int)(((double)lf_font.lfHeight)*scale);

	lb.lbHatch = 0;
	lb.lbStyle = BS_SOLID;
	lb.lbColor = m_line_color;

	pDS->GetSize(rows, cols);

	pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_SQUARE, (int)(m_line_width*scale), &lb);
	// шрифт названия осей
	font.CreatePointFontIndirect(&lf_font, pDC);
	// шрифт значений на осях
	small_font.CreatePointFontIndirect(&lf_small, pDC);

	COLORREF old_color = pDC->SetTextColor(m_font_color);
	int OldBkMode = pDC->SetBkMode(TRANSPARENT);
	old_pen = pDC->SelectObject(&pen);
	old_font = pDC->SelectObject(&small_font);

	CString sName = m_pParam?m_pParam->GetText():m_name;

	if(m_pDSColumn) // ось X
	{
		double angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
		int dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));

		pDC->MoveTo(rect.right, rect.bottom+m_pGistogram->m_Volume);
		pDC->LineTo(rect.left+dx, rect.bottom+m_pGistogram->m_Volume);
		pDC->LineTo(rect.left, rect.bottom);
		pDC->LineTo(rect.right-dx, rect.bottom);
		pDC->LineTo(rect.right, rect.bottom+m_pGistogram->m_Volume);

		pDC->SelectObject(old_pen);
		pen.DeleteObject();
		pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_FLAT, 1, &lb);
		lb.lbColor = m_lines_color;
		lines_pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_FLAT, 1, &lb);
		old_pen = pDC->SelectObject(&pen);

		int iDxBottom = 1;
		int ind = pDS->GetHeader()->Find(m_pDSColumn->GetName());
		if(ind>-1)
		{
			double angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
			int dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));
			iDxBottom = CalcCaptionRows(pDC, rect, scale, pDS, dMin, dRange, true);
			rect.left+=dx;
			rect.bottom+=m_pGistogram->m_Volume;

			double width = (double)rect.Width()/rows;
			for(i=0; i<rows+1; i+=m_skip_elements)
			{
				x = (int)(i*width);
				if(m_bShowLines)
				{
					pDC->SelectObject(&lines_pen);
					pDC->MoveTo(rect.left + x, rect.bottom);
					pDC->LineTo(rect.left + x, rect.top+10);
					//засечки по оси X
					pDC->MoveTo(rect.left + x, rect.bottom);
					pDC->LineTo(rect.left + x-dx, rect.bottom-m_pGistogram->m_Volume);
				}
			}
		}
		y = iDxBottom+5;
		pDC->SelectObject(&font);
		if(!sName.IsEmpty())
		{
			x = rect.right+30-pDC->GetTextExtent(sName).cx/2;
			pDC->TextOut(x-dx, rect.bottom-y-m_pGistogram->m_Volume, sName);
		}
	}
	else // ось Y
	{
		double angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
		int dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));

		pDC->MoveTo(rect.left+dx, rect.top);
		pDC->LineTo(rect.left+dx, rect.bottom+m_pGistogram->m_Volume);
		pDC->LineTo(rect.left, rect.bottom);
		pDC->LineTo(rect.left, rect.top-m_pGistogram->m_Volume);
		pDC->LineTo(rect.left+dx, rect.top);

		rect.left+=dx;
		rect.bottom+=m_pGistogram->m_Volume;

		pDC->SelectObject(old_pen);
		pen.DeleteObject();
		pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_FLAT, 1, &lb);
		lb.lbColor = m_lines_color;
		lines_pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_FLAT, 1, &lb);
		old_pen = pDC->SelectObject(&pen);

		//определяем максимальное целое
		int iMax, iMin;
		iMax = (int)(dMin+dRange);
		//определяем минимальное целое
		if(m_bCalcFromZero)
			iMin = dMin<0?(int)(dMin+0.999):0;
		else
			iMin = (int)(dMin+0.999);

		// определим, есть ли значения, отличные от 1
		CString sVal;
		bool bUseValues = false;
		int indX = pDS->GetHeader()->Find(m_pGistogram->m_Elements[0]->GetColumnName());
		if(indX>=0)
		{
			// прочитаем переданные значения
			for(i=0; i<rows; i++)
			{
				pDS->GetAt(i, indX, sVal);
				CStringArray sArr, AdditionalCoordinateArray;
				bool bAdditionalLine;
				Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
				Timeline_values_struct new_val;
				Report_ConvertAggr2TimeLine(sArr, new_val);
				if(new_val.is_value_begin == true || new_val.is_value_end == true)
				{
					bUseValues = true;
					break;
				}
			}
		}

		if(bUseValues)
		{
			y = 0;
			sVal.Format(_T("%d"), iMin);
			x=pDC->GetTextExtent(sVal).cx;
			y+=pDC->GetTextExtent(sVal).cy/2;
			pDC->TextOut(rect.left-15-x-dx, rect.bottom+y-m_pGistogram->m_Volume, sVal);
		}

		int iMultiCnt = 0;
		double dTmpRange(dRange);
		while(iMax==iMin || ((int)(dTmpRange))==0)
		{
			dTmpRange *=10;
			iMultiCnt ++;
			iMax = iMin+(int)(dTmpRange+0.9999);
		}

		if(iMultiCnt>0)
		{
			dRange = iMax;
			for(int l_i(0); l_i<iMultiCnt; l_i++)
				dRange/=10;
		}

		if(iMax!=iMin)
		{
			y = -(int)((iMax)*(double)(rect.Height())/iMax);
			if(m_bShowLines)
			{
				pDC->SelectObject(&lines_pen);
				pDC->MoveTo(rect.left-dx, rect.bottom+y-m_pGistogram->m_Volume);
				pDC->LineTo(rect.left, rect.bottom+y);
				pDC->LineTo(rect.right+10, rect.bottom+y);
			}
			if(bUseValues)
			{
				if(iMultiCnt==0)
					sVal.Format(_T("%d"), iMax);
				else
				{
					int i_loc;
					double dTmpVal_l = iMax;
					for(i_loc=0; i_loc<iMultiCnt; i_loc++)
						dTmpVal_l/=10;
					sVal.Format(_T("%g"), dTmpVal_l);
				}
				x=pDC->GetTextExtent(sVal).cx;
				y+=pDC->GetTextExtent(sVal).cy/2;
				pDC->TextOut(rect.left-dx-15-x, rect.bottom+y-m_pGistogram->m_Volume, sVal);
			}
		}
		//теперь разлиновываем по целым, если линий будет более 10, то по 10
		m_NumRowsInGroup = 1;
		int lines = 1;
		int elem=-1;
		int tmp = iMax-iMin;
		while(tmp>10)
		{
			m_NumRowsInGroup *=10;
			tmp/=10;
		}
		m_NumRowsInGroup = m_NumRowsInGroup/2>0?m_NumRowsInGroup/2:(m_NumRowsInGroup>1?m_NumRowsInGroup:1);
		lines = m_NumRowsInGroup>=10?m_NumRowsInGroup/10:1;

		for(i=iMin; i<iMax; i+=lines)
		{
			if((iMax-iMin)>m_NumRowsInGroup)
			{
				if((double)((int)(i)/lines)!=(double)(i)/lines) continue;
				if((double)((int)(i)/m_NumRowsInGroup)!=(double)(i)/m_NumRowsInGroup) continue;
			}
			y = -(int)((i)*(double)(rect.Height())/(iMax));
			elem++;
			if(elem==m_skip_elements || (i==iMax && elem<=(int)(m_skip_elements*0.5)) || elem==0)
			{
				if(m_bShowLines && y!=0)
				{
					pDC->SelectObject(&lines_pen);
					pDC->MoveTo(rect.left-dx, rect.bottom+y-m_pGistogram->m_Volume);
					pDC->LineTo(rect.left, rect.bottom+y);
					pDC->LineTo(rect.right+10, rect.bottom+y);
				}

				if(bUseValues)
				{
					if(iMultiCnt==0)
						sVal.Format(_T("%d"), i);
					else
					{
						int i_loc;
						double dTmpVal_l = i;
						for(i_loc=0; i_loc<iMultiCnt; i_loc++)
							dTmpVal_l/=10;
						sVal.Format(_T("%g"), dTmpVal_l);
					}
					x=pDC->GetTextExtent(sVal).cx;
					y+=pDC->GetTextExtent(sVal).cy/2;
					pDC->TextOut(rect.left-15-x-dx, rect.bottom+y-m_pGistogram->m_Volume, sVal);
				}

				elem=0;
			}
		}

		x = rect.left;
		pDC->SelectObject(&font);
		if(!sName.IsEmpty())
			pDC->TextOut(x, rect.top+15+pDC->GetTextExtent(sName).cy, sName);
	}
	pDC->SetTextColor(old_color);
	pDC->SetBkMode(OldBkMode);
	pDC->SelectObject(old_font);
	pDC->SelectObject(old_pen);
	pen.DeleteObject();
	lines_pen.DeleteObject();
	font.DeleteObject();
	small_font.DeleteObject();
}

void CCoordinateLine::DrawCycleGistogram(CDC *pDC, CRect rect, double scale, CDataSource* pDS, double dMin, double &dRange, Gdiplus::Graphics* graphics /*= NULL*/)
{
	CPen pen, *old_pen;
	CFont font, small_font, *old_font;
	LOGBRUSH lb;
	LOGFONT		lf_small = m_font;
	LOGFONT		lf_font = m_font;
	int rows, cols;
	int ind;
	int OldBkMode;
	COLORREF old_color;

	lf_small.lfHeight-=40;
	lf_small.lfHeight = (int)(((double)lf_small.lfHeight)*scale);
	lf_font.lfHeight = (int)(((double)lf_font.lfHeight)*scale);

	lb.lbHatch = 0;
	lb.lbStyle = BS_SOLID;
	lb.lbColor = m_line_color;

	pDS->GetSize(rows, cols);

	pen.CreatePen(PS_SOLID|PS_GEOMETRIC|PS_ENDCAP_SQUARE, (int)(m_line_width*scale), &lb);
	// шрифт названия осей
	font.CreatePointFontIndirect(&lf_font, pDC);
	// шрифт значений на осях
	small_font.CreatePointFontIndirect(&lf_small, pDC);

	old_color = pDC->SetTextColor(m_font_color);
	OldBkMode = pDC->SetBkMode(TRANSPARENT);
	old_pen = pDC->SelectObject(&pen);
	old_font = pDC->SelectObject(&small_font);

	if(m_pDSColumn) // ось X
	{
		ind = pDS->GetHeader()->Find(m_pDSColumn->GetName());
		if(ind>-1)
		{
			CalcCaptionCycle(pDC, rect, scale, pDS, dMin, dRange, true, graphics);
		}
	}
	else // ось Y
	{
		for(int iDI=0; iDI<m_pGistogram->m_Elements.GetSize(); iDI++)
		{
			ind = pDS->GetHeader()->Find(m_pGistogram->m_Elements[iDI]->GetColumnName());
			if(ind<0) continue;

			CalcCaptionCycle(pDC, rect, scale, pDS, dMin, dRange, true, graphics);
		}
	}
	pDC->SetTextColor(old_color);
	pDC->SetBkMode(OldBkMode);
	pDC->SelectObject(old_font);
	pDC->SelectObject(old_pen);
	pen.DeleteObject();
	font.DeleteObject();
	small_font.DeleteObject();
}

// отрисовка подписей на оси X
int CCoordinateLine::CalcCaptionRows(CDC *pDC, CRect rect, double scale, CDataSource* pDS, double dMin, double &dRange, bool bDraw)
{
	// отсекаем ось Y
	if(m_pDSColumn==NULL) return 1;
	if(m_pGistogram && m_pGistogram->m_ViewType==APL_GISTOGRAM_VIEW_TYPE_TIMELINE) // временная гистограмма
	{
		if(m_bHideValues == true) return 1;
	}

	int i, ind;
	int rows, cols;
	CString sVal;
	int v_cnt(0);

	CFont font, small_font, add_small_font;
	LOGFONT lf_font = m_font;

	LOGFONT lf_small = m_font;
	lf_small.lfHeight-=40;
	lf_small.lfHeight = (int)(((double)lf_small.lfHeight)*scale);
	if(m_TextVector==aplText_Angle)
		lf_small.lfEscapement = 450;
	lf_font.lfHeight = (int)(((double)lf_font.lfHeight)*scale);

	LOGFONT lf_add_small = lf_small;
	lf_add_small.lfWeight = FW_BOLD;

	// шрифт названия осей
	font.CreatePointFontIndirect(&lf_font, pDC);
	// шрифт значений на осях
	small_font.CreatePointFontIndirect(&lf_small, pDC);
	// шрифт дополнительных значений на осях
	add_small_font.CreatePointFontIndirect(&lf_add_small, pDC);

	CFont *old_font = pDC->SelectObject(&small_font);

	pDS->GetSize(rows, cols);

	if(rows==0) return 1;
	
	double *dXVals = new double[rows*5]; // массив смещений начала надписей
	double *dWidthVals = new double[rows*5]; // массив ширины надписей
	CString *sValues = new CString[rows*5]; // массив текстов надписей
	bool *bBold = new bool[rows*5]; // массив признаков, что надпись нужно вывести шрифтом, отличным от обычного

	ind = pDS->GetHeader()->Find(m_pDSColumn->GetName());

	memset(dXVals, 0, sizeof(dXVals));
	memset(dWidthVals, 0, sizeof(dWidthVals));
	memset(bBold, 0, sizeof(bBold));

	// заполнение массивов: ширина надписи, смещение начала надписи, текст надписи
	if(m_pGistogram==NULL) //график
	{
		double dVal, x, dPrev;
		int elem = -1;
		dPrev = 0;
		for(i=0; i<rows; i++)
		{
			pDS->GetAt(i, ind, sVal);

			dVal = __atof(sVal);
			if(i>0 && dVal==dPrev) continue;
			dPrev = dVal;
			if(m_bProRata)
				x = (int)(i*(double)(rect.Width())/(rows-1));
			else
				x = (int)((dVal-dMin)*(double)(rect.Width())/dRange);
			elem++;
			if(elem==m_skip_elements || (i==rows-1 && elem<=(int)(m_skip_elements*0.5)) || elem==0)
			{
				dWidthVals[v_cnt] = pDC->GetTextExtent(sVal).cx;
				dXVals[v_cnt] = x-dWidthVals[v_cnt]/2;
				sValues[v_cnt] = sVal;
				bBold[v_cnt] = false;
				v_cnt++;
				elem=0;
			}
		}
	}
	else //гистограмма
	{
		if(m_pGistogram->m_ViewType==APL_GISTOGRAM_VIEW_TYPE_TIMELINE) // временная гистограмма
		{
			double angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
			int dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));
			double width = (double)(rect.Width()-dx);
			COleDateTimeSpan ts(0, 1, 0, 0);

			int indX = pDS->GetHeader()->Find(m_pGistogram->m_Elements[0]->GetColumnName());
			if(indX>=0)
			{
				// прочитаем переданные значения
				CArray<Timeline_values_struct> values;
				for(i=0; i<rows; i++)
				{
					pDS->GetAt(i, indX, sVal);
					CStringArray sArr, AdditionalCoordinateArray;
					bool bAdditionalLine;
					Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
					Timeline_values_struct new_val;
					Report_ConvertAggr2TimeLine(sArr, new_val);
					values.Add(new_val);
				}
				Report_SortTimeLine(values);

				// найдем минимальное и максимальное значение времени
				double min_time = 0.0;
				double max_time = 0.0;
				Report_GetMinMaxTime(values, min_time, max_time);

				if(max_time-min_time>0.0)
				{
					// определим масштаб (годы, месяцы, дни)
					UCHAR mstb = _T('y');
					for(i=0; i<rows; i++)
					{
						pDS->GetAt(i, ind, sVal);
						if(sVal.Find(_T("%d"))!=-1 || sVal.Find(_T("%j"))!=-1)
						{
							mstb = _T('d');
							break;
						}
						if(sVal.Find(_T("%b"))!=-1 || sVal.Find(_T("%B"))!=-1 || sVal.Find(_T("%m"))!=-1)
							mstb = _T('m');
					}
					if(rows==0)
						mstb = _T('d');

					CStringArray FormatArr;
					if(mstb == _T('y') || mstb == _T('m')) // если масштаб годы или месяцы, выведем только годы или название месяцев в тех местах, где они начинаются
					{
						values.RemoveAll();
						i = 0;

						CString buf_max, buf_min;
						COleDateTime dt;
						dt.m_dt = max_time;
						aplDate2String(dt, buf_max);
						dt.m_dt = min_time;
						aplDate2String(dt, buf_min);
						while(buf_min < buf_max)
						{
							Timeline_values_struct new_val;
							new_val.begin = dt.m_dt;
							if(mstb == _T('y'))
								dt.SetDate(dt.GetYear()+1, 1, 1);
							else
							{
								int year = dt.GetYear() + (dt.GetMonth()+1)/12;
								int month = (dt.GetMonth()+1)%12;
								if (month==0) { year--; month = 12; }
								dt.SetDate(year, month, 1);
							}
							aplDate2String(dt, buf_min);
							if(buf_min > buf_max)
								dt.m_dt = max_time;
							new_val.end = dt.m_dt;
							new_val.type = 0;
							values.Add(new_val);

							pDS->GetAt(i, ind, sVal);
							FormatArr.Add(sVal);
							i++;
							if(i >= rows)
								i--;
						}

						if(values.GetSize() > rows)
						{
							delete []dXVals;
							delete []dWidthVals;
							delete []sValues;
							delete []bBold;
							dXVals = new double[values.GetSize()*2];
							dWidthVals = new double[values.GetSize()*2];
							sValues = new CString[values.GetSize()*2];
							bBold = new bool[values.GetSize()*2];
						}
					}
					else // если масштаб дни, заполним формат вывода данных
					{
						for(i=0; i<rows; i++)
						{
							pDS->GetAt(i, ind, sVal);
							FormatArr.Add(sVal);
						}
					}

					// вычислим начальные позиции отрисовки надписей
					for(i=0; i<values.GetSize(); i++)
					{
						if(values[i].begin==values[i].end) continue;
						if(values[i].end-values[i].begin < ts.m_span) continue; // меньше одного часа

						// проверим, может следующий интервал не состыкован с текущим
						bool bDrawEnd = false;
						if(i==values.GetSize()-1)
							bDrawEnd = true;
						else
						{
							int x1 = (int)(width*(values[i].end-min_time)/(max_time-min_time));
							int x2 = (int)(width*(values[i+1].begin-min_time)/(max_time-min_time));
							if(x2-x1>1)
								bDrawEnd = true;
						}

						//
						COleDateTime dt(values[i].begin);
						sVal = dt.Format(FormatArr[i]);

						dWidthVals[v_cnt] = pDC->GetTextExtent(sVal).cx;
						sValues[v_cnt] = sVal;
						bBold[v_cnt] = false;
						if(m_TextVector==aplText_Angle) // под углом
						{
							dXVals[v_cnt] = width*(values[i].begin-min_time)/(max_time-min_time) + pDC->GetTextExtent(sVal).cy/2;

							if(v_cnt && sValues[v_cnt-1] == sValues[v_cnt]) {} // одинаковые надписи пропускаем (не отрисовываем)
							else
							{
								double dWidthText = pDC->GetTextExtent(_T("Ag")).cy;
								if(v_cnt && dXVals[v_cnt]-dXVals[v_cnt-1] < dWidthText) // слишком узко для отображения надписи
								{
									// проверим, может можно подвинуть
									double dNextXVal = 0;
									if(bDrawEnd) // следующий интервал не состыкован с текущим
										dNextXVal = width*(values[i].end-min_time)/(max_time-min_time) + dWidthText/2;
									else // следующий интервал стыкуется с текущим
										dNextXVal = width*(values[i+1].begin-min_time)/(max_time-min_time) + dWidthText/2;

									if((dNextXVal-dXVals[v_cnt])+(dXVals[v_cnt]-dXVals[v_cnt-1]) >= 2*dWidthText
										&& dXVals[v_cnt] > dXVals[v_cnt-1]) // можно подвинуть вправо
									{
										dXVals[v_cnt] = dXVals[v_cnt-1] + dWidthText;
										v_cnt++;
									}
									else // нельзя подвинуть вправо, проверим, может можно подвинуть влево предыдущую надпись
									{
										if(v_cnt>1 &&
											(dXVals[v_cnt]-dXVals[v_cnt-1])+(dXVals[v_cnt-1]-dXVals[v_cnt-2]) >= 2*dWidthText) // можно подвинуть влево
										{
											dXVals[v_cnt-1] = dXVals[v_cnt] - dWidthText;
											v_cnt++;
										}
										else // нельзя подвинуть, просто пропускаем (не отрисовываем) надпись
										{
										}
									}
								}
								else
									v_cnt++;
							}
						}
						else if(m_TextVector==aplText_Horz) // горизонтально
						{
							dXVals[v_cnt] = width*(values[i].begin-min_time)/(max_time-min_time);
							if(v_cnt && sValues[v_cnt-1] == sValues[v_cnt]) {} // одинаковые надписи пропускаем (не отрисовываем)
							else
							{
								if(v_cnt && dXVals[v_cnt]-dXVals[v_cnt-1]<pDC->GetTextExtent(sValues[v_cnt-1]).cx) // слишком узко для отображения надписи
								{
									// проверим, может можно подвинуть влево предыдущую надпись
									if(v_cnt>1 &&
										dXVals[v_cnt-1]-pDC->GetTextExtent(sValues[v_cnt-1]).cx > dXVals[v_cnt-2]+pDC->GetTextExtent(sValues[v_cnt-2]).cx+10) // можно подвинуть влево
									{
										dXVals[v_cnt-1] = dXVals[v_cnt-1] - pDC->GetTextExtent(sValues[v_cnt-1]).cx;
									}
								}
								v_cnt++;
							}
						}

						// если следующий интервал не состыкован с текущим, выведем время конца интервала
						if(bDrawEnd)
						{
							COleDateTime dt(values[i].end);
							sVal = dt.Format(FormatArr[i]);

							dWidthVals[v_cnt] = pDC->GetTextExtent(sVal).cx;
							sValues[v_cnt] = sVal;
							bBold[v_cnt] = false;
							if(m_TextVector==aplText_Angle) // под углом
							{
								dXVals[v_cnt] = width*(values[i].end-min_time)/(max_time-min_time) + pDC->GetTextExtent(sVal).cy/2;
								if(v_cnt && sValues[v_cnt-1] == sValues[v_cnt]) {} // одинаковые надписи пропускаем (не отрисовываем)
								else
								{
									double dWidthText = pDC->GetTextExtent(_T("Ag")).cy;
									if(v_cnt && dXVals[v_cnt]-dXVals[v_cnt-1] < dWidthText) // слишком узко для отображения надписи
									{
										// проверим, может можно повинуть
										if(i==values.GetSize()-1) // если последняя надпись, просто пропускаем (не отрисовываем) надпись
										{
										}
										else
										{
											double dNextXVal = width*(values[i+1].begin-min_time)/(max_time-min_time) + dWidthText/2;

											if((dNextXVal-dXVals[v_cnt])+(dXVals[v_cnt]-dXVals[v_cnt-1]) >= 2*dWidthText
												&& dXVals[v_cnt] > dXVals[v_cnt-1]) // можно подвинуть вправо
											{
												dXVals[v_cnt] = dXVals[v_cnt-1] + dWidthText;
												v_cnt++;
											}
											else // нельзя подвинуть вправо, проверим, может можно подвинуть влево предыдущую надпись
											{
												if(v_cnt>1 &&
													(dXVals[v_cnt]-dXVals[v_cnt-1])+(dXVals[v_cnt-1]-dXVals[v_cnt-2]) >= 2*dWidthText) // можно подвинуть влево
												{
													dXVals[v_cnt-1] = dXVals[v_cnt] - dWidthText;
													v_cnt++;
												}
												else // нельзя подвинуть, просто пропускаем (не отрисовываем) надпись
												{
												}
											}
										}
									}
									else
										v_cnt++;
								}
							}
							else if(m_TextVector==aplText_Horz) // горизонтально
							{
								dXVals[v_cnt] = width*(values[i].end-min_time)/(max_time-min_time);
								if(v_cnt && sValues[v_cnt-1] == sValues[v_cnt]) {} // одинаковые надписи пропускаем (не отрисовываем)
								else
								{
									if(v_cnt && dXVals[v_cnt]-dXVals[v_cnt-1]<pDC->GetTextExtent(sValues[v_cnt-1]).cx) // слишком узко для отображения надписи
									{
										// проверим, может можно подвинуть влево предыдущую надпись
										if(v_cnt>1 &&
											dXVals[v_cnt-1]-pDC->GetTextExtent(sValues[v_cnt-1]).cx > dXVals[v_cnt-2]+pDC->GetTextExtent(sValues[v_cnt-2]).cx+10) // можно подвинуть влево
										{
											dXVals[v_cnt-1] = dXVals[v_cnt-1] - pDC->GetTextExtent(sValues[v_cnt-1]).cx;
										}
									}
									v_cnt++;
								}
							}
						}
					}
				}
			}
		}
		else // столбики
		{
			double angel = ((double)m_pGistogram->m_Angel)/180.0*PI;
			int dx = (int)(m_pGistogram->m_Volume*sin(angel)/cos(angel));
			double width = (double)(rect.Width()-dx)/rows;
			if(m_TextVector==aplText_Angle) // под углом
			{
				while(width>12 && width<pDC->GetTextExtent(_T("Ag")).cy) // слишком узко для отображения подписи
				{
					lf_small.lfHeight--;
					small_font.DeleteObject();
					small_font.CreatePointFontIndirect(&lf_small, pDC);
					pDC->SelectObject(&small_font);

					lf_add_small.lfHeight--;
					add_small_font.DeleteObject();
					add_small_font.CreatePointFontIndirect(&lf_add_small, pDC);

					if(lf_small.lfHeight <= 0 || lf_add_small.lfHeight <= 0)
						break;
				}
			}
			for(i=0; i<rows; i+=m_skip_elements)
			{
				pDS->GetAt(i, ind, sVal);
				if(sVal.Find(_T(';'))!=-1) // если в подписи столбика есть дополнительная строка для вывода
				{
					CStringArray sArr, AdditionalCoordinateArray;
					bool bAdditionalLine;
					Report_ConvertStrAggr2Array(sVal, sArr, AdditionalCoordinateArray, bAdditionalLine);
					if(sArr.GetSize()>0)
						sVal = sArr[0];
					else
						sVal = _T("");
					//дополнительная строка подписи
					if(AdditionalCoordinateArray.GetSize()>0)
					{
						pDC->SelectObject(&add_small_font);
						for(int j = 0; j < AdditionalCoordinateArray.GetSize(); j++)
						{
							dWidthVals[v_cnt] = pDC->GetTextExtent(AdditionalCoordinateArray[j]).cx;
							if(m_TextVector==aplText_Angle) // под углом
							{
								dXVals[v_cnt] = (i+1)*width - width/2;
							}
							else if(m_TextVector==aplText_Horz) // горизонтально
							{
								if(dWidthVals[v_cnt]>width)
									dXVals[v_cnt] = i*width;
								else
									dXVals[v_cnt] = (i+1)*width - (width + dWidthVals[v_cnt])/2;
							}
							sValues[v_cnt] = AdditionalCoordinateArray[j];
							bBold[v_cnt] = true;
							v_cnt++;
						}
						pDC->SelectObject(&small_font);
					}
				}
				dWidthVals[v_cnt] = pDC->GetTextExtent(sVal).cx;
				if(m_TextVector==aplText_Angle) // под углом
				{
					dXVals[v_cnt] = (i+1)*width - width/2;
				}
				else if(m_TextVector==aplText_Horz) // горизонтально
				{
					if(dWidthVals[v_cnt]>width)
						dXVals[v_cnt] = i*width;
					else
						dXVals[v_cnt] = (i+1)*width - (width + dWidthVals[v_cnt])/2;
				}
				sValues[v_cnt] = sVal;
				bBold[v_cnt] = false;
				v_cnt++;
			}
		}
	}

	int rows_cnt(1);

	// собственно отрисовка, если функция вызвана с целью отрисовки;
	// или определение количества строк, необходимых для отрисовки подписей без наложения друг на друга,
	// если функция вызвана с целью вычислений области отрисовки
	int *iRowOut = new int[v_cnt];
	for(i=0; i<v_cnt; i++)
	{
		int rows_ind = 1;
		if(dWidthVals[i]==0)
		{
			iRowOut[i] = rows_ind;
			continue;
		}

		if(m_TextVector==aplText_Horz) // горизонтально
		{
			for(int k=1; k<=rows_cnt; k++)
			{
				int j;
				for(j=i-1; j>=0; j--)
				{
					if(iRowOut[j]==rows_ind)
					{
						if(dXVals[j]+dWidthVals[j]>=dXVals[i])
						{
							rows_ind++;
							break;
						}
					}
				}
				if(j==-1)
					break;
			}
		}
		iRowOut[i] = rows_ind;
		if(rows_ind>rows_cnt)
			rows_cnt = rows_ind;
		if(rows_cnt>2 && m_TextVector==aplText_Horz) // для временной гистограммы максимально может быть две горизонтальные строки
		{
			if(m_pGistogram && m_pGistogram->m_ViewType==APL_GISTOGRAM_VIEW_TYPE_TIMELINE) // временная гистограмма
			{
				rows_cnt = 2;
				continue;
			}
		}
		if(bDraw) // отрисовка
		{
			int text_h = (int)(pDC->GetTextExtent(_T("Ag")).cy*1.1);
			int y = 15;
			if(m_TextVector==aplText_Angle) // под углом
				y = 5;
			if(bBold[i]) // вывод текста шрифтом, отличным от обычного
			{
				COLORREF color = pDC->GetTextColor();
				pDC->SelectObject(&add_small_font);
				pDC->SetTextColor(RGB(255,0,0));
				pDC->TextOut((int)(rect.left+dXVals[i]), rect.bottom-y-(rows_ind-1)*text_h, sValues[i]);
				pDC->SelectObject(&small_font);
				pDC->SetTextColor(color);
			}
			else // обычный вывод текста
			{
				pDC->TextOut((int)(rect.left+dXVals[i]), rect.bottom-y-(rows_ind-1)*text_h, sValues[i]);
			}
		}
	}
	delete []iRowOut;

	int ret = 0;
	if(m_TextVector==aplText_Angle) // под углом
	{
		for(i=0; i<v_cnt; i++)
		{
			int x = (int)((dWidthVals[i]+pDC->GetTextExtent(_T("Ag")).cy)*sin(45.0/180.0*PI));
			if(ret<x)
				ret = x;
		}
		if(bDraw)
			ret += 5;
	}
	else if(m_TextVector==aplText_Horz) // горизонтально
	{
		ret = (int)(pDC->GetTextExtent(_T("Ag")).cy*1.1*rows_cnt);
		if(bDraw)
			ret += 15;
	}

	delete []dXVals;
	delete []dWidthVals;
	delete []sValues;
	delete []bBold;

	pDC->SelectObject(old_font);
	font.DeleteObject();
	small_font.DeleteObject();

	return ret;
}

int CCoordinateLine::CalcCycleYText(int y, int width, int height,
									int* iBorderOldAngleX, int* iBorderOldAngleY, int iBorderOldAngleNum,
									int* iBorderPieAngleX, int* iBorderPieAngleY, int iBorderPieAngleNum,
									int* iBorderEllipseX, int* iBorderEllipseY, int iBorderEllipseNum,
									int& out_width, int& out_x)
{
	out_width = 0;
	out_x = 0;

	int xOldTop = 0, xPieTop = 0, xEllipse1Top = 0, xEllipse2Top = 0;
	int xOldBottom = 0, xPieBottom = 0, xEllipse1Bottom = 0, xEllipse2Bottom = 0;
	for(int j = 0; j < iBorderOldAngleNum; j++)
	{
		if( (iBorderOldAngleY[j]==y)
			||
			(j &&
				(iBorderOldAngleY[j-1]<y && iBorderOldAngleY[j]>y)
				||
				(iBorderOldAngleY[j-1]>y && iBorderOldAngleY[j]<y)
			)
		  )
		{
			xOldTop = iBorderOldAngleX[j];
			break;
		}
	}
	for(int j = 0; j < iBorderOldAngleNum; j++)
	{
		if( (iBorderOldAngleY[j]==y-height)
			||
			(j &&
				(iBorderOldAngleY[j-1]<y-height && iBorderOldAngleY[j]>y-height)
				||
				(iBorderOldAngleY[j-1]>y-height && iBorderOldAngleY[j]<y-height)
			)
		  )
		{
			xOldBottom = iBorderOldAngleX[j];
			break;
		}
	}
	for(int j = 0; j < iBorderPieAngleNum; j++)
	{
		if( (iBorderPieAngleY[j]==y)
			||
			(j &&
				(iBorderPieAngleY[j-1]<y && iBorderPieAngleY[j]>y)
				||
				(iBorderPieAngleY[j-1]>y && iBorderPieAngleY[j]<y)
			)
		  )
		{
			xPieTop = iBorderPieAngleX[j];
			break;
		}
	}
	for(int j = 0; j < iBorderPieAngleNum; j++)
	{
		if( (iBorderPieAngleY[j]==y-height)
			||
			(j &&
				(iBorderPieAngleY[j-1]<y-height && iBorderPieAngleY[j]>y-height)
				||
				(iBorderPieAngleY[j-1]>y-height && iBorderPieAngleY[j]<y-height)
			)
		  )
		{
			xPieBottom = iBorderPieAngleX[j];
			break;
		}
	}
	for(int j = 0; j < iBorderEllipseNum; j++)
	{
		if( (iBorderEllipseY[j]==y)
			||
			(j &&
				(iBorderEllipseY[j-1]<y && iBorderEllipseY[j]>y)
				||
				(iBorderEllipseY[j-1]>y && iBorderEllipseY[j]<y)
			)
		  )
		{
			if(xEllipse1Top==0)
				xEllipse1Top = iBorderEllipseX[j];
			else
			{
				if(iBorderEllipseY[j-1]!=iBorderEllipseY[j])
				{
					xEllipse2Top = iBorderEllipseX[j];
					break;
				}
			}
		}
	}
	for(int j = 0; j < iBorderEllipseNum; j++)
	{
		if( (iBorderEllipseY[j]==y-height)
			||
			(j &&
				(iBorderEllipseY[j-1]<y-height && iBorderEllipseY[j]>y-height)
				||
				(iBorderEllipseY[j-1]>y-height && iBorderEllipseY[j]<y-height)
			)
		  )
		{
			if(xEllipse1Bottom==0)
				xEllipse1Bottom = iBorderEllipseX[j];
			else
			{
				if(iBorderEllipseY[j-1]!=iBorderEllipseY[j])
				{
					xEllipse2Bottom = iBorderEllipseX[j];
					break;
				}
			}
		}
	}

	bool bRet = false;
	int xOld = 0, xPie = 0, xEllipse1 = 0, xEllipse2 = 0;
	if(xOldTop && xPieTop && xOldBottom && xPieBottom)
	{
		if(xOldTop>xPieTop)
		{
			if(xOldTop>xOldBottom) xOld = xOldBottom;
			else xOld = xOldTop;
			if(xPieTop>xPieBottom) xPie = xPieTop;
			else xPie = xPieBottom;
			out_width = xOld-xPie;
			out_x = xPie+(xOld-xPie-width)/2;
			if(xOld-xPie>width)
				bRet = true;
		}
		else
		{
			if(xPieTop>xPieBottom) xPie = xPieBottom;
			else xPie = xPieTop;
			if(xOldTop>xOldBottom) xOld = xOldTop;
			else xOld = xOldBottom;
			out_width = xPie-xOld;
			out_x = xOld+(xPie-xOld-width)/2;
			if(xPie-xOld>width)
				bRet = true;
		}
	}
	else if(xOldTop && xEllipse1Top && xOldBottom && xEllipse1Bottom)
	{
		if(xOldTop>xEllipse1Top)
		{
			if(xOldTop>xOldBottom) xOld = xOldBottom;
			else xOld = xOldTop;
			if(xEllipse1Top>xEllipse1Bottom) xEllipse1 = xEllipse1Top;
			else xEllipse1 = xEllipse1Bottom;
			out_width = xOld-xEllipse1;
			out_x = xEllipse1+(xOld-xEllipse1-width)/2;
			if(xOld-xEllipse1>width)
				bRet = true;
		}
		else
		{
			if(xEllipse1Top>xEllipse1Bottom) xEllipse1 = xEllipse1Bottom;
			else xEllipse1 = xEllipse1Top;
			if(xOldTop>xOldBottom) xOld = xOldTop;
			else xOld = xOldBottom;
			out_width = xEllipse1-xOld;
			out_x = xOld+(xEllipse1-xOld-width)/2;
			if(xEllipse1-xOld>width)
				bRet = true;
		}
	}
	else if(xPieTop && xEllipse1Top && xPieBottom && xEllipse1Bottom)
	{
		if(xPieTop>xEllipse1Top)
		{
			if(xPieTop>xPieBottom) xPie = xPieBottom;
			else xPie = xPieTop;
			if(xEllipse1Top>xEllipse1Bottom) xEllipse1 = xEllipse1Top;
			else xEllipse1 = xEllipse1Bottom;
			out_width = xPie-xEllipse1;
			out_x = xEllipse1+(xPie-xEllipse1-width)/2;
			if(xPie-xEllipse1>width)
				bRet = true;
		}
		else
		{
			if(xEllipse1Top>xEllipse1Bottom) xEllipse1 = xEllipse1Bottom;
			else xEllipse1 = xEllipse1Top;
			if(xPieTop>xPieBottom) xPie = xPieTop;
			else xPie = xPieBottom;
			out_width = xEllipse1-xPie;
			out_x = xPie+(xEllipse1-xPie-width)/2;
			if(xEllipse1-xPie>width)
				bRet = true;
		}
	}
	else if(xEllipse1Top && xEllipse2Top && xEllipse1Bottom && xEllipse2Bottom)
	{
		if(xEllipse1Top>xEllipse2Top)
		{
			if(xEllipse1Top>xEllipse1Bottom) xEllipse1 = xEllipse1Bottom;
			else xEllipse1 = xEllipse1Top;
			if(xEllipse2Top>xEllipse2Bottom) xEllipse2 = xEllipse2Top;
			else xEllipse2 = xEllipse2Bottom;
			out_width = xEllipse1-xEllipse2;
			out_x = xEllipse2+(xEllipse1-xEllipse2-width)/2;
			if(xEllipse1-xEllipse2>width)
				bRet = true;
		}
		else
		{
			if(xEllipse2Top>xEllipse2Bottom) xEllipse2 = xEllipse2Bottom;
			else xEllipse2 = xEllipse2Top;
			if(xEllipse1Top>xEllipse1Bottom) xEllipse1 = xEllipse1Top;
			else xEllipse1 = xEllipse1Bottom;
			out_width = xEllipse2-xEllipse1;
			out_x = xEllipse1+(xEllipse2-xEllipse1-width)/2;
			if(xEllipse2-xEllipse1>width)
				bRet = true;
		}
	}
	if(bRet)
		return out_x;
	else
		return 0;
}

// отрисовка подписей на круговой гистограмме
CRect CCoordinateLine::CalcCaptionCycle(CDC *pDC, CRect rect, double scale, CDataSource* pDS, double dMin, double &dRange,
										bool bDraw /*= false*/, Gdiplus::Graphics* graphics /*= NULL*/)
{
	int i, indX, indY;
	int rows, cols;
	int v_cnt = 0;

	CFont font, small_font;
	LOGFONT		lf_small = m_font;
	LOGFONT		lf_font = m_font;

	lf_small.lfHeight-=40;
	lf_small.lfHeight = (int)(((double)lf_small.lfHeight)*scale);
	lf_font.lfHeight = (int)(((double)lf_font.lfHeight)*scale);

	// шрифт названия осей
	font.CreatePointFontIndirect(&lf_font, pDC);
	// шрифт значений на осях
	small_font.CreatePointFontIndirect(&lf_small, pDC);

	CFont *old_font = pDC->SelectObject(&small_font);

	pDS->GetSize(rows, cols);

	if(rows==0) return rect;

	if(m_pDSColumn==NULL) // ось Y
	{
		if(m_bShowNames==false && m_bShowPercents==false)
			return rect;
	}
	else // ось X
	{
		if(m_bShowNames==false)
			return rect;
	}

	double *dXVals = new double[rows]; // коодинаты X начала надписей
	double *dYVals = new double[rows]; // коодинаты Y начала надписей
	double *dWidthVals = new double[rows]; // ширина надписей
	CString *sValues = new CString[rows]; // значения надписей
	double *dXDisplFrom = new double[rows]; // коодинаты X начала выносных линий
	double *dYDisplFrom = new double[rows]; // коодинаты Y начала выносных линий
	double *dXDisplTo = new double[rows]; // коодинаты X конца выносных линий
	double *dYDisplTo = new double[rows]; // коодинаты Y конца выносных линий
	double *dXDisplToDop = new double[rows]; // коодинаты X конца дополнительных выносных линий
	double *dYDisplToDop = new double[rows]; // коодинаты Y конца дополнительных выносных линий
	COLORREF *cColors = new COLORREF[rows]; // цвета секторов диаграммы
	double *dOldAngles = new double[rows]; // угол начала сектора диаграммы
	double *dPieAngles = new double[rows]; // угол конца сектора диаграммы

	CArray<COLORREF> column_colors;
	ASSERT(m_pGistogram->m_XLine.m_pDSColumn);
	indX = pDS->GetHeader()->Find(m_pGistogram->m_XLine.m_pDSColumn->GetName());
	for(int iDI=0; iDI<m_pGistogram->m_Elements.GetSize(); iDI++)
	{
		column_colors.RemoveAll();
		bool ret_c = m_pGistogram->GetColor(m_pGistogram->m_Elements[iDI]->m_color, rows, column_colors);
		ASSERT(ret_c);

		indY = pDS->GetHeader()->Find(m_pGistogram->m_Elements[iDI]->GetColumnName());
	}

	memset(dXVals, 0, sizeof(dXVals));
	memset(dWidthVals, 0, sizeof(dWidthVals));

	// заполнение массивов: ширина надписи, смещение начала надписи, текст надписи
	int text_h = (int)(pDC->GetTextExtent(_T("Ag")).cy*1.1);

	double dVal;
	double dSumVal = 0.0;
	for(i=0; i<rows; i++)
	{
		pDS->GetAt(i, indY, dVal);
		dSumVal += dVal;
	}
	CRect TopEllipseRect(rect); // прямоугольная область для верхнего эллипса "таблетки"
	CRect BottomEllipseRect(rect); // прямоугольная область для нижнего эллипса "таблетки"
	TopEllipseRect.bottom -= (int)(cos(((double)m_pGistogram->m_Angel)/180.0*PI)*rect.Height());
	BottomEllipseRect.top += (int)(cos(((double)m_pGistogram->m_Angel)/180.0*PI)*rect.Height());
	if(m_pDSColumn==NULL) // ось Y
	{
		// сначала заполним массив текстов надписей, т.к. последний нужно подгонять под 100%
		// это может повлиять на расположение надписи внутри сегмента
		double dPieAngle = 0;
		CString sLastVal;
		double *dPercentVals = new double[rows];
		for(i=0; i<rows; i+=m_skip_elements)
		{
			pDS->GetAt(i, indY, dVal);
			if(dVal==0) continue;

			double dOldAngle = dPieAngle;
			dPieAngle += (dVal*360)/dSumVal;

			CString sVal;
			if(m_bShowNames==true)
			{
				pDS->GetAt(i, indY, sVal);
				sLastVal = sVal;
				if(m_bShowPercents==true)
				{
					CString buf;
					buf.Format(_T("%.2f"), (dPieAngle-dOldAngle)*100/360);
					buf.TrimRight(_T('0'));
					buf.TrimRight(_T('.'));
					dPercentVals[v_cnt] = __atof(buf);
					sVal += _T(" (");
					sVal += buf;
					sVal += _T("%)");
				}
			}
			else
			{
				if(m_bShowPercents==true)
				{
					sVal.Format(_T("%.2f"), (dPieAngle-dOldAngle)*100/360);
					sVal.TrimRight(_T('0'));
					sVal.TrimRight(_T('.'));
					dPercentVals[v_cnt] = __atof(sVal);
					sVal += _T("%");
				}
			}
			sValues[v_cnt] = sVal;
			dWidthVals[v_cnt] = pDC->GetTextExtent(sVal).cx;

			v_cnt++;
		}
		// проверим на сходимость процентов до 100
		if(m_bShowPercents==true)
		{
			double percents = 0;
			for(i=0; i<v_cnt-1; i++)
				percents += dPercentVals[i];
			CString buf;
			buf.Format(_T("%.2f"), 100.0-percents);
			buf.TrimRight(_T('0'));
			buf.TrimRight(_T('.'));
			if(m_bShowNames==true)
			{
				sValues[v_cnt-1] = sLastVal;
				sValues[v_cnt-1] += _T(" (");
				sValues[v_cnt-1] += buf;
				sValues[v_cnt-1] += _T("%)");
			}
			else
				sValues[v_cnt-1] = buf;
			dWidthVals[v_cnt-1] = pDC->GetTextExtent(sValues[v_cnt-1]).cx;
		}
		delete []dPercentVals;

		// заполним массивы расположения текстов надписей внутри сегментов
		v_cnt = 0;
		dPieAngle = 0;
		for(i=0; i<rows; i+=m_skip_elements)
		{
			pDS->GetAt(i, indY, dVal);
			if(dVal==0) continue;

			double dOldAngle = dPieAngle;
			dPieAngle += (dVal*360)/dSumVal;

			// заполним массив координат линии начальной границы сегмента
			int MaxNumPixels = (int)(abs(cos(dOldAngle/180*PI)*TopEllipseRect.Width()/2));
			if(MaxNumPixels<(int)(abs(sin(dOldAngle/180*PI)*TopEllipseRect.Height()/2)))
				MaxNumPixels = (int)(abs(sin(dOldAngle/180*PI)*TopEllipseRect.Height()/2));
			MaxNumPixels++;
			int *iBorderOldAngleX = new int[MaxNumPixels];
			int *iBorderOldAngleY = new int[MaxNumPixels];
			int iBorderOldAngleNum = 0;
			double dc = cos(dOldAngle/180*PI)*TopEllipseRect.Width()/2;
			double ds = -sin(dOldAngle/180*PI)*TopEllipseRect.Height()/2;
			for(int j=0; j<MaxNumPixels; j++)
			{
				int x = (int)(dc*j/MaxNumPixels+TopEllipseRect.CenterPoint().x);
				int y = (int)(ds*j/MaxNumPixels+TopEllipseRect.CenterPoint().y);
				if(!iBorderOldAngleNum || x!=iBorderOldAngleX[iBorderOldAngleNum-1] || y!=iBorderOldAngleY[iBorderOldAngleNum-1])
				{
					iBorderOldAngleX[iBorderOldAngleNum] = x;
					iBorderOldAngleY[iBorderOldAngleNum] = y;
					iBorderOldAngleNum++;
				}
			}

			// заполним массив координат линии конечной границы сегмента
			MaxNumPixels = (int)(abs(cos(dPieAngle/180*PI)*TopEllipseRect.Width()/2));
			if(MaxNumPixels<(int)(abs(sin(dPieAngle/180*PI)*TopEllipseRect.Height()/2)))
				MaxNumPixels = (int)(abs(sin(dPieAngle/180*PI)*TopEllipseRect.Height()/2));
			MaxNumPixels++;
			int *iBorderPieAngleX = new int[MaxNumPixels];
			int *iBorderPieAngleY = new int[MaxNumPixels];
			int iBorderPieAngleNum = 0;
			dc = cos(dPieAngle/180*PI)*TopEllipseRect.Width()/2;
			ds = -sin(dPieAngle/180*PI)*TopEllipseRect.Height()/2;
			for(int j=0; j<MaxNumPixels; j++)
			{
				int x = (int)(dc*j/MaxNumPixels+TopEllipseRect.CenterPoint().x);
				int y = (int)(ds*j/MaxNumPixels+TopEllipseRect.CenterPoint().y);
				if(!iBorderPieAngleNum || x!=iBorderPieAngleX[iBorderPieAngleNum-1] || y!=iBorderPieAngleY[iBorderPieAngleNum-1])
				{
					iBorderPieAngleX[iBorderPieAngleNum] = x;
					iBorderPieAngleY[iBorderPieAngleNum] = y;
					iBorderPieAngleNum++;
				}
			}

			// заполним массив координат дуги эллипса на сегменте
			MaxNumPixels = (int)((dPieAngle-dOldAngle)*100+1);
			int *iBorderEllipseX = new int[MaxNumPixels];
			int *iBorderEllipseY = new int[MaxNumPixels];
			int iBorderEllipseNum = 0;
			for(double j = dOldAngle; j < dPieAngle; j+=0.01)
			{
				int x = (int)(cos(j/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x);
				int y = (int)(-sin(j/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y);
				if(!iBorderEllipseNum || x!=iBorderEllipseX[iBorderEllipseNum-1] || y!=iBorderEllipseY[iBorderEllipseNum-1])
				{
					iBorderEllipseX[iBorderEllipseNum] = x;
					iBorderEllipseY[iBorderEllipseNum] = y;
					iBorderEllipseNum++;
				}
			}

			// подберем положение текста надписи, чтобы она не пересекала границ сегмента
			double dMiddleAngle = dOldAngle+(dPieAngle-dOldAngle)/2;
			double x = cos(dMiddleAngle/180*PI)*TopEllipseRect.Width()*0.7/2+TopEllipseRect.CenterPoint().x;
			x -= pDC->GetTextExtent(sValues[v_cnt]).cx/2;
			double y = -sin(dMiddleAngle/180*PI)*TopEllipseRect.Height()*0.7/2+TopEllipseRect.CenterPoint().y;
			y += pDC->GetTextExtent(sValues[v_cnt]).cy/2;
			bool bCross = false;
			for(int j = 0; j < iBorderOldAngleNum; j++)
			{
				if( iBorderOldAngleX[j]>(int)x && iBorderOldAngleX[j]<(int)(x+pDC->GetTextExtent(sValues[v_cnt]).cx)
					&&
					iBorderOldAngleY[j]<(int)y && iBorderOldAngleY[j]>(int)(y-pDC->GetTextExtent(sValues[v_cnt]).cy))
				{
					bCross = true;
					break;
				}
			}
			if(!bCross)
			{
				for(int j = 0; j < iBorderPieAngleNum; j++)
				{
					if( iBorderPieAngleX[j]>(int)x && iBorderPieAngleX[j]<(int)(x+pDC->GetTextExtent(sValues[v_cnt]).cx)
						&&
						iBorderPieAngleY[j]<(int)y && iBorderPieAngleY[j]>(int)(y-pDC->GetTextExtent(sValues[v_cnt]).cy))
					{
						bCross = true;
						break;
					}
				}
			}
			if(!bCross)
			{
				for(int j = 0; j < iBorderEllipseNum; j++)
				{
					if( iBorderEllipseX[j]>(int)x && iBorderEllipseX[j]<(int)(x+pDC->GetTextExtent(sValues[v_cnt]).cx)
						&&
						iBorderEllipseY[j]<(int)y && iBorderEllipseY[j]>(int)(y-pDC->GetTextExtent(sValues[v_cnt]).cy))
					{
						bCross = true;
						break;
					}
				}
			}
			int MaxSegmentWidth = 0, MaxSegmentWidthX = 0, MaxSegmentWidthY = 0;
			int dx = pDC->GetTextExtent(_T("A")).cx;
			int dy = pDC->GetTextExtent(_T("Ag")).cy;
			if(bCross) // есть пересечение текста надписи с границей сегмента
			{
				int out_width, out_x;
				// сначала попробуем подвигать влево-вправо
				int xx = CalcCycleYText((int)y, (int)(dWidthVals[v_cnt]+dx), dy,
										iBorderOldAngleX, iBorderOldAngleY, iBorderOldAngleNum,
										iBorderPieAngleX, iBorderPieAngleY, iBorderPieAngleNum,
										iBorderEllipseX, iBorderEllipseY, iBorderEllipseNum,
										out_width, out_x);
				if(xx)
				{
					x = xx+dx/2;
					bCross = false;
				}
				else // не получилось, тогда будем подбирать перебором по высоте
				{
					int max_y = 0, min_y = 0;
					for(int j = 0; j < iBorderOldAngleNum; j++)
					{
						if(iBorderOldAngleY[j]>max_y || !max_y)
							max_y = iBorderOldAngleY[j];
						if(iBorderOldAngleY[j]<min_y || !min_y)
							min_y = iBorderOldAngleY[j];
					}
					for(int j = 0; j < iBorderPieAngleNum; j++)
					{
						if(iBorderPieAngleY[j]>max_y || !max_y)
							max_y = iBorderPieAngleY[j];
						if(iBorderPieAngleY[j]<min_y || !min_y)
							min_y = iBorderPieAngleY[j];
					}
					for(int j = 0; j < iBorderEllipseNum; j++)
					{
						if(iBorderEllipseY[j]>max_y || !max_y)
							max_y = iBorderEllipseY[j];
						if(iBorderEllipseY[j]<min_y || !min_y)
							min_y = iBorderEllipseY[j];
					}
					if(min_y!=0 && max_y!=0)
					{
						if(abs(max_y-min_y)>dy)
						{
							if(dOldAngle>=0 && dOldAngle<=180)
							{
								max_y -= dy;
								for(int j = max_y; j > min_y; j--)
								{
									int xx = CalcCycleYText(j, (int)(dWidthVals[v_cnt]+dx), dy,
															iBorderOldAngleX, iBorderOldAngleY, iBorderOldAngleNum,
															iBorderPieAngleX, iBorderPieAngleY, iBorderPieAngleNum,
															iBorderEllipseX, iBorderEllipseY, iBorderEllipseNum,
															out_width, out_x);
									if(xx)
									{
										x = xx+dx/2;
										y = j;
										bCross = false;
										break;
									}
									if(out_width>MaxSegmentWidth)
									{
										MaxSegmentWidth = out_width;
										MaxSegmentWidthX = out_x;
										MaxSegmentWidthY = j;
									}
								}
							}
							else
							{
								for(int j = min_y; j < max_y; j++)
								{
									int xx = CalcCycleYText(j, (int)(dWidthVals[v_cnt]+dx), dy,
															iBorderOldAngleX, iBorderOldAngleY, iBorderOldAngleNum,
															iBorderPieAngleX, iBorderPieAngleY, iBorderPieAngleNum,
															iBorderEllipseX, iBorderEllipseY, iBorderEllipseNum,
															out_width, out_x);
									if(xx)
									{
										x = xx+dx/2;
										y = j;
										bCross = false;
										break;
									}
									if(out_width>MaxSegmentWidth)
									{
										MaxSegmentWidth = out_width;
										MaxSegmentWidthX = out_x;
										MaxSegmentWidthY = j;
									}
								}
							}
						}
						else
						{
							// это случай узкого горизонтального сегмента
							// надо реализовать в будущем
						}
					}
				}
			}
			if(bCross && MaxSegmentWidthX!=0 && MaxSegmentWidthY!=0) // не смогли вписать текст надписи в сегмент, запишем его в самое широкое место
			{
				x = MaxSegmentWidthX+dx/2;
				y = MaxSegmentWidthY;
				if( (dOldAngle>=0 && dOldAngle<45 && dPieAngle>0 && dPieAngle<45)
					||
					(dOldAngle>315 && dOldAngle<=360 && dPieAngle>315 && dPieAngle<=360)
				  )
				{
					if(dWidthVals[v_cnt]>MaxSegmentWidth)
						x = x+MaxSegmentWidth-dWidthVals[v_cnt]-1;
					y += dy/2;
				}
				if(dOldAngle>135 && dOldAngle<225 && dPieAngle>135 && dPieAngle<225)
					y += dy/2;

			}

			dXVals[v_cnt] = x;
			dYVals[v_cnt] = y;

			delete []iBorderOldAngleX;
			delete []iBorderOldAngleY;
			delete []iBorderPieAngleX;
			delete []iBorderPieAngleY;
			delete []iBorderEllipseX;
			delete []iBorderEllipseY;

			cColors[v_cnt] = column_colors[i];
			dOldAngles[v_cnt] = dOldAngle;
			dPieAngles[v_cnt] = dPieAngle;

			dXDisplFrom[v_cnt] = 0;
			dYDisplFrom[v_cnt] = 0;
			dXDisplTo[v_cnt] = 0;
			dYDisplTo[v_cnt] = 0;
			dXDisplToDop[v_cnt] = 0;
			dYDisplToDop[v_cnt] = 0;

			v_cnt++;
		}
	}
	else // ось X
	{
		double dPieAngle = 0;
		for(i=0; i<rows; i+=m_skip_elements)
		{
			pDS->GetAt(i, indY, dVal);
			if(dVal==0) continue;
			CString sVal;
			pDS->GetAt(i, indX, sVal);

			double dOldAngle = dPieAngle;
			dPieAngle += (dVal*360)/dSumVal;
			double dMiddleAngle = dOldAngle+(dPieAngle-dOldAngle)/2;
			double x = cos(dMiddleAngle/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x;
			if(dMiddleAngle > 90 && dMiddleAngle < 270)
				x -= pDC->GetTextExtent(sVal).cx;
			double y = 0;
			if(dMiddleAngle > 180)
				y = -sin(dMiddleAngle/180*PI)*BottomEllipseRect.Height()/2+BottomEllipseRect.CenterPoint().y;
			else
			{
				y = -sin(dMiddleAngle/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;
				y += text_h;
			}

			dXDisplFrom[v_cnt] = 0;
			dYDisplFrom[v_cnt] = 0;
			dXDisplToDop[v_cnt] = 0;
			dYDisplToDop[v_cnt] = 0;
			if(v_cnt)
			{
				// проверим, не наезжает ли надпись на предыдущую
				bool bSet = false;
				if( (x>dXVals[v_cnt-1] && x<dXVals[v_cnt-1]+dWidthVals[v_cnt-1])
					||
					(x+pDC->GetTextExtent(sVal).cx>dXVals[v_cnt-1] && x+pDC->GetTextExtent(sVal).cx<dXVals[v_cnt-1]+dWidthVals[v_cnt-1]) )
				{
					if( (y<dYVals[v_cnt-1] && y>dYVals[v_cnt-1]-text_h)
						||
						(y-text_h<dYVals[v_cnt-1] && y-text_h>dYVals[v_cnt-1]-text_h) )
					{
						dXDisplFrom[v_cnt] = cos(dMiddleAngle/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x;
						if(dMiddleAngle > 90 && dMiddleAngle <= 180)
							x = dXVals[v_cnt-1]-pDC->GetTextExtent(sVal).cx-25;
						else if(dMiddleAngle >= 270)
							x = dXVals[v_cnt-1]+dWidthVals[v_cnt-1]+25;
						if(dMiddleAngle > 90 && dMiddleAngle < 270)
							dXDisplTo[v_cnt] = x+pDC->GetTextExtent(sVal).cx;
						else
							dXDisplTo[v_cnt] = x;

						if(dMiddleAngle <= 90)
							y = dYVals[v_cnt-1]+text_h;
						else if(dMiddleAngle <= 180)
							y = dYVals[v_cnt-1];
						else if(dMiddleAngle < 270)
							y = dYVals[v_cnt-1]-text_h;
						else
							y = dYVals[v_cnt-1];
						if(dMiddleAngle > 180)
						{
							dYDisplFrom[v_cnt] = -sin(dMiddleAngle/180*PI)*BottomEllipseRect.Height()/2+BottomEllipseRect.CenterPoint().y;
							dYDisplTo[v_cnt] = y;
						}
						else
						{
							dYDisplFrom[v_cnt] = -sin(dMiddleAngle/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;
							dYDisplTo[v_cnt] = y-text_h;
						}
						if(dMiddleAngle > 90 && dMiddleAngle <= 180)
						{
							dXDisplToDop[v_cnt] = dXDisplTo[v_cnt];
							dYDisplToDop[v_cnt] = y;
							y += text_h;
						}
						else if(dMiddleAngle >= 270)
						{
							dXDisplToDop[v_cnt] = dXDisplTo[v_cnt];
							y -= text_h;
							dYDisplToDop[v_cnt] = y;
						}

						bSet = true;
					}
				}
				if( bSet==false
					&&
					dXDisplFrom[v_cnt-1]!=0.0 && dYDisplFrom[v_cnt-1]!=0.0 && dXDisplTo[v_cnt-1]!=0.0 && dYDisplTo[v_cnt-1]!=0.0)
				{
					// если налезает на линию, а не на надпись
					if( dXDisplTo[v_cnt-1]>x && dXDisplTo[v_cnt-1]<x+pDC->GetTextExtent(sVal).cx
						&&
						dYDisplTo[v_cnt-1]<y && dYDisplTo[v_cnt-1]>y-text_h )
					{
						dXDisplFrom[v_cnt] = cos(dMiddleAngle/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x;
						if(dMiddleAngle > 90 && dMiddleAngle <= 180)
							x = dXDisplTo[v_cnt-1]-pDC->GetTextExtent(sVal).cx-25;
						else if(dMiddleAngle >= 270)
							x = dXDisplTo[v_cnt-1]+25;
						if(dMiddleAngle > 90 && dMiddleAngle < 270)
							dXDisplTo[v_cnt] = x+pDC->GetTextExtent(sVal).cx;
						else
							dXDisplTo[v_cnt] = x;

						if(dMiddleAngle > 180)
						{
							dYDisplFrom[v_cnt] = -sin(dMiddleAngle/180*PI)*BottomEllipseRect.Height()/2+BottomEllipseRect.CenterPoint().y;
							dYDisplTo[v_cnt] = y;
						}
						else
						{
							dYDisplFrom[v_cnt] = -sin(dMiddleAngle/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;
							dYDisplTo[v_cnt] = y-text_h;
						}

						bSet = true;
					}
					if(bSet==false)
					{
						if( x+pDC->GetTextExtent(sVal).cx>dXVals[v_cnt-1] && dMiddleAngle <= 90
							&&
							dYDisplFrom[v_cnt-1]>y && dYDisplTo[v_cnt-1]<y)
						{
							dXDisplFrom[v_cnt] = dXDisplTo[v_cnt] = x;

							y = dYVals[v_cnt-1]+text_h;
							dYDisplFrom[v_cnt] = -sin(dMiddleAngle/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;
							dYDisplTo[v_cnt] = y-text_h;
						}
						else if(x<dXVals[v_cnt-1]+dWidthVals[v_cnt-1] && dMiddleAngle > 180 && dMiddleAngle < 270
								&&
								dYDisplFrom[v_cnt-1]<y && dYDisplTo[v_cnt-1]>y)
						{
							dXDisplFrom[v_cnt] = dXDisplTo[v_cnt] = x+pDC->GetTextExtent(sVal).cx;

							y = dYVals[v_cnt-1]-text_h;
							dYDisplFrom[v_cnt] = -sin(dMiddleAngle/180*PI)*BottomEllipseRect.Height()/2+BottomEllipseRect.CenterPoint().y;
							dYDisplTo[v_cnt] = y;
						}
					}
				}
			}

			dWidthVals[v_cnt] = pDC->GetTextExtent(sVal).cx;
			dXVals[v_cnt] = x;
			dYVals[v_cnt] = y;
			sValues[v_cnt] = sVal;
			cColors[v_cnt] = 0x00f0f0f0;
			dOldAngles[v_cnt] = 0;
			dPieAngles[v_cnt] = 0;
			v_cnt++;
		}
	}

	if(bDraw) // собственно отрисовка
	{
		for(i=0; i<v_cnt; i++)
		{
			if(dWidthVals[i]==0) continue;
			if(dXDisplFrom[i]!=0.0 && dYDisplFrom[i]!=0.0 && dXDisplTo[i]!=0.0 && dYDisplTo[i]!=0.0)
			{
				pDC->MoveTo((int)dXDisplFrom[i], (int)dYDisplFrom[i]);
				pDC->LineTo((int)dXDisplTo[i], (int)dYDisplTo[i]);
				if(dXDisplToDop[i]!=0.0 && dYDisplToDop[i]!=0.0)
					pDC->LineTo((int)dXDisplToDop[i], (int)dYDisplToDop[i]);
			}
			else if(m_pDSColumn==NULL) // ось Y
			{
				// нарисуем эллипс вокруг строки
				if(graphics && m_ReportMgr->m_data_source.GetUseGDIPlus())
				{
					double x1, y1, x2, y2;
					if(dOldAngles[i] == 0 || dOldAngles[i] == 360)
					{
						x1 = TopEllipseRect.right;
						y1 = TopEllipseRect.CenterPoint().y;
					}
					else
					{
						x1 = cos(dOldAngles[i]/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x;
						y1 = -sin(dOldAngles[i]/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;
					}
					if(dPieAngles[i] == 0 || dPieAngles[i] == 360)
					{
						x2 = TopEllipseRect.right;
						y2 = TopEllipseRect.CenterPoint().y;
					}
					else
					{
						x2 = cos(dPieAngles[i]/180*PI)*TopEllipseRect.Width()/2+TopEllipseRect.CenterPoint().x;
						y2 = -sin(dPieAngles[i]/180*PI)*TopEllipseRect.Height()/2+TopEllipseRect.CenterPoint().y;
					}
					double dStartAngle = atan2(y1-TopEllipseRect.CenterPoint().y, x1-TopEllipseRect.CenterPoint().x)*180.0/PI;
					if(dOldAngles[i] > 180) dStartAngle += 360.0;
					double dEndAngle = atan2(y2-TopEllipseRect.CenterPoint().y, x2-TopEllipseRect.CenterPoint().x)*180.0/PI;
					if(dPieAngles[i] > 180) dEndAngle += 360.0;

					// преобразование координат
					POINT pt[5];
					pt[0].x = (int)dXVals[i];
					pt[0].y = (int)dYVals[i];
					pt[1].x = pDC->GetTextExtent(sValues[i]).cx;
					pt[1].y = pDC->GetTextExtent(sValues[i]).cy;
					pt[2].x = pDC->GetTextExtent(_T("Wl")).cx;
					pt[2].y = pDC->GetTextExtent(_T(")")).cy;
					pt[3] = TopEllipseRect.TopLeft();
					pt[4] = TopEllipseRect.BottomRight();
					::DPtoLP(pDC->m_hDC, pt, 5);
					pt[0].y -= pt[1].y;

					Gdiplus::GraphicsPath path, pathPie;
					Gdiplus::RectF rect_f((float)pt[0].x-pt[2].x/4, (float)-pt[0].y+pt[2].y/4, (float)pt[1].x+pt[2].x/2, (float)-pt[1].y-pt[2].y/2);
					path.AddEllipse(rect_f);
					Gdiplus::RectF rect_l((float)pt[3].x, (float)-pt[4].y, (float)abs(pt[4].x-pt[3].x), (float)abs(pt[4].y-pt[3].y));
					pathPie.AddPie(rect_l, (float)dStartAngle, (float)(dEndAngle-dStartAngle));
					Gdiplus::Region region(rect_f);
					region.Intersect(&path);
					region.Intersect(&pathPie);

					Gdiplus::Color color_main, color_dark, color_light;
					m_pGistogram->GetGradientColors(cColors[i], color_main, color_dark, color_light);
					while((int)color_light.GetRed()+(int)color_light.GetGreen()+(int)color_light.GetBlue() < 120*3)
					{
						Gdiplus::Color cm, cd;
						m_pGistogram->GetGradientColors(RGB(color_light.GetRed(), color_light.GetGreen(), color_light.GetBlue()), cm, cd, color_light);
					}
					Gdiplus::Color colors_ellipse[3] = {color_main, color_light, color_light};
					float positions_ellipse[3] = {0.0f, 0.1f, 1.0f};
					Gdiplus::PathGradientBrush ellipseBrush(&path);
					ellipseBrush.SetCenterColor(color_light);
					ellipseBrush.SetInterpolationColors(colors_ellipse, positions_ellipse, 3);

					// по непонятным причинам FillRegion берет размер окна вывода в логических единицах
					// и делает пересчет координат функцией ::DPtoLP.
					// в результате, правая часть области заливки не заполняется цветом
					// пришлось выкручиваться, используя код ниже
//					graphics->FillRegion(&ellipseBrush, &region);
					Gdiplus::Matrix matrix;
					graphics->GetTransform(&matrix);
					int cnt_reg_scan = region.GetRegionScansCount(&matrix);
					Gdiplus::Rect* rects_scan = new Gdiplus::Rect[cnt_reg_scan];
					region.GetRegionScans(&matrix, rects_scan, &cnt_reg_scan);
					for(int j = 0; j < cnt_reg_scan; ++j)
						graphics->FillRectangle(&ellipseBrush, rects_scan[j]);
					delete []rects_scan;

					HDC gdiDC = graphics->GetHDC();
					graphics->ReleaseHDC(gdiDC);
				}
			}
			pDC->TextOut((int)dXVals[i], (int)dYVals[i], sValues[i]);
		}
	}
	else // вычисления области диаграммы
	{
		if(m_pDSColumn!=NULL) // ось X
		{
			int left = 0;
			for(i=0; i<v_cnt; i++)
			{
				if(dWidthVals[i]==0) continue;
				if(dXVals[i]<rect.left)
				{
					if(rect.left-dXVals[i] > left)
						left = rect.left-(int)dXVals[i];
				}
			}

			int right = 0;
			for(i=0; i<v_cnt; i++)
			{
				if(dWidthVals[i]==0) continue;
				if(dXVals[i]+dWidthVals[i]>rect.right)
				{
					if(dXVals[i]+dWidthVals[i]-rect.right > right)
						right = (int)dXVals[i]+(int)dWidthVals[i]-rect.right;
				}
			}

			int top = 0;
			for(i=0; i<v_cnt; i++)
			{
				if(dWidthVals[i]==0) continue;
				if(dYVals[i]>rect.top)
				{
					if(dYVals[i]-rect.top > top)
						top = (int)dYVals[i]-rect.top;
				}
			}

			int bottom = 0;
			for(i=0; i<v_cnt; i++)
			{
				if(dWidthVals[i]==0) continue;
				if(dYVals[i]-text_h<rect.bottom)
				{
					if(rect.bottom-dYVals[i]+text_h > bottom)
						bottom = rect.bottom-(int)dYVals[i]+text_h;
				}
			}

			rect.left += left;
			rect.right -= right;
			rect.top -= top;
			rect.bottom += bottom;
		}
	}

	delete []dXVals;
	delete []dYVals;
	delete []dWidthVals;
	delete []sValues;
	delete []cColors;
	delete []dOldAngles;
	delete []dPieAngles;
	delete []dXDisplFrom;
	delete []dYDisplFrom;
	delete []dXDisplTo;
	delete []dYDisplTo;
	delete []dXDisplToDop;
	delete []dYDisplToDop;

	pDC->SelectObject(old_font);
	font.DeleteObject();
	small_font.DeleteObject();

	return rect;
}
