You cannot select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
374 lines
12 KiB
C++
374 lines
12 KiB
C++
// XTPChartTextPainter.cpp
|
|
//
|
|
// This file is a part of the XTREME TOOLKIT PRO MFC class library.
|
|
// (c)1998-2012 Codejock Software, All Rights Reserved.
|
|
//
|
|
// THIS SOURCE FILE IS THE PROPERTY OF CODEJOCK SOFTWARE AND IS NOT TO BE
|
|
// RE-DISTRIBUTED BY ANY MEANS WHATSOEVER WITHOUT THE EXPRESSED WRITTEN
|
|
// CONSENT OF CODEJOCK SOFTWARE.
|
|
//
|
|
// THIS SOURCE CODE CAN ONLY BE USED UNDER THE TERMS AND CONDITIONS OUTLINED
|
|
// IN THE XTREME TOOLKIT PRO LICENSE AGREEMENT. CODEJOCK SOFTWARE GRANTS TO
|
|
// YOU (ONE SOFTWARE DEVELOPER) THE LIMITED RIGHT TO USE THIS SOFTWARE ON A
|
|
// SINGLE COMPUTER.
|
|
//
|
|
// CONTACT INFORMATION:
|
|
// support@codejock.com
|
|
// http://www.codejock.com
|
|
//
|
|
/////////////////////////////////////////////////////////////////////////////
|
|
|
|
#include "stdafx.h"
|
|
#include <float.h>
|
|
#include <math.h>
|
|
|
|
#include "Common/XTPMarkupRender.h"
|
|
|
|
#include "../Types/XTPChartTypes.h"
|
|
#include "Common/Base/Types/XTPPoint3.h"
|
|
|
|
#include "../XTPChartDefines.h"
|
|
#include "../XTPChartElement.h"
|
|
#include <Chart/XTPChartLegendItem.h>
|
|
#include "../XTPChartContent.h"
|
|
|
|
#include "../Drawing/XTPChartDeviceContext.h"
|
|
#include "../Drawing/XTPChartDeviceCommand.h"
|
|
#include "../Drawing/XTPChartTextDeviceCommand.h"
|
|
#include "../Drawing/XTPChartTransformationDeviceCommand.h"
|
|
|
|
#include "XTPChartTextPainter.h"
|
|
#include <Common/XTPMathUtils.h>
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CXTPChartTextPainterBase
|
|
|
|
CXTPChartTextPainterBase::CXTPChartTextPainterBase(CXTPChartDeviceContext* pDC, const CXTPChartString& text, CXTPChartTextElement* pTextProvider)
|
|
{
|
|
m_strText = text;
|
|
m_pTextProvider = pTextProvider;
|
|
|
|
m_rcRoundedBounds.SetRectEmpty();
|
|
|
|
m_pMarkupUIElement = NULL;
|
|
|
|
CXTPMarkupContext* pMarkupContext = pTextProvider->GetContent()->GetMarkupContext();
|
|
|
|
if (pMarkupContext)
|
|
{
|
|
m_pMarkupUIElement = XTPMarkupParseText(pMarkupContext, text);
|
|
}
|
|
|
|
if (m_pMarkupUIElement)
|
|
{
|
|
m_szTextSize = CXTPChartMarkupElementDeviceCommand::MeasureElement(pDC, m_pMarkupUIElement, pTextProvider->GetFont());
|
|
}
|
|
else
|
|
{
|
|
m_szTextSize = pDC->MeasureString(&text, pTextProvider->GetFont());
|
|
}
|
|
|
|
m_nHeight = (int)ceil(m_szTextSize.Height);
|
|
m_nWidth = (int)ceil(m_szTextSize.Width);
|
|
}
|
|
|
|
CXTPChartTextPainterBase::~CXTPChartTextPainterBase()
|
|
{
|
|
XTPMarkupReleaseElement(m_pMarkupUIElement);
|
|
}
|
|
|
|
CRect CXTPChartTextPainterBase::GetRoundedBounds()
|
|
{
|
|
if (m_rcRoundedBounds.IsRectNull())
|
|
{
|
|
m_rcBounds = CalculateBounds();
|
|
m_rcRoundedBounds.SetRect((int)m_rcBounds.X, (int)m_rcBounds.Y, (int)m_rcBounds.GetRight(), (int)m_rcBounds.GetBottom());
|
|
}
|
|
return m_rcRoundedBounds;
|
|
}
|
|
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CXTPChartTextPainter
|
|
|
|
CXTPChartTextPainter::CXTPChartTextPainter(CXTPChartDeviceContext* pDC, const CXTPChartString& text, CXTPChartTextElement* pTextProvider)
|
|
: CXTPChartTextPainterBase(pDC, text, pTextProvider)
|
|
{
|
|
m_ptLocation = CXTPChartPointF(0, 0);
|
|
}
|
|
|
|
void CXTPChartTextPainter::SetLocation(const CXTPChartPointF& location)
|
|
{
|
|
m_ptLocation = location;
|
|
}
|
|
|
|
CXTPChartDeviceCommand* CXTPChartTextPainter::CreateDeviceCommand(CXTPChartDeviceContext* pDC, const CXTPChartColor& color)
|
|
{
|
|
UNREFERENCED_PARAMETER(pDC);
|
|
|
|
CXTPChartRectF rect(m_ptLocation.X, m_ptLocation.Y, m_szTextSize.Width, m_szTextSize.Height);
|
|
|
|
CXTPChartSaveStateDeviceCommand* pStateGraphicsCommand = new CXTPChartSaveStateDeviceCommand();
|
|
|
|
CXTPChartDeviceCommand* pTextContainer = pStateGraphicsCommand->AddChildCommand(new CXTPChartTextAntialiasingDeviceCommand(m_pTextProvider->GetAntialiasing()));
|
|
|
|
if (m_pMarkupUIElement)
|
|
pTextContainer->AddChildCommand(new CXTPChartMarkupElementDeviceCommand(m_pMarkupUIElement, m_pTextProvider->GetFont(), color, rect));
|
|
else
|
|
pTextContainer->AddChildCommand(new CXTPChartBoundedTextDeviceCommand(m_strText, m_pTextProvider->GetFont(), color, rect));
|
|
|
|
return pStateGraphicsCommand;
|
|
}
|
|
|
|
CXTPChartRectF CXTPChartTextPainter::CalculateBounds()
|
|
{
|
|
return CXTPChartRectF(m_ptLocation.X, m_ptLocation.Y, m_szTextSize.Width, m_szTextSize.Height);
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CXTPChartRotatedTextPainterBase
|
|
|
|
CXTPChartRotatedTextPainterBase::CXTPChartRotatedTextPainterBase(CXTPChartDeviceContext* pDC, const CXTPChartString& text, CXTPChartTextElement* pTextProvider, CPoint ptBase)
|
|
: CXTPChartTextPainterBase(pDC, text, pTextProvider)
|
|
{
|
|
m_ptBasePoint = ptBase;
|
|
m_fAngle = 0;
|
|
}
|
|
|
|
void CXTPChartRotatedTextPainterBase::SetBasePoint(CPoint pt)
|
|
{
|
|
m_ptBasePoint = pt;
|
|
}
|
|
|
|
CXTPChartRectF CXTPChartRotatedTextPainterBase::CalculateBounds()
|
|
{
|
|
float minX = FLT_MAX, minY = FLT_MAX, maxX = FLT_MIN, maxY = FLT_MIN;
|
|
|
|
float points[4][2];
|
|
CalculatePoints(CalculateRotation(), GetInitialTextRect(), m_fAngle, points);
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
minX = min(minX, points[i][0]);
|
|
maxX = max(maxX, points[i][0]);
|
|
|
|
minY = min(minY, points[i][1]);
|
|
maxY = max(maxY, points[i][1]);
|
|
}
|
|
|
|
return CXTPChartRectF(minX, minY, maxX - minX, maxY - minY);
|
|
}
|
|
|
|
CRect CXTPChartRotatedTextPainterBase::GetInitialTextRect()
|
|
{
|
|
return CRect(CalculateLeftTopPoint(), CSize(m_nWidth, m_nHeight));
|
|
}
|
|
|
|
void CXTPChartRotatedTextPainterBase::CalculatePoints(XTPChartTextRotation rotation, CRect rect, float fDegree, float points[4][2])
|
|
{
|
|
double fPI = CXTPMathUtils::m_dPI;
|
|
double fRadial = fDegree * fPI / 180.0;
|
|
|
|
float width = rect.Width() / 2.0f;
|
|
float height = rect.Height() / 2.0f;
|
|
float fsin = (float)sin(fRadial);
|
|
float fcos = (float)cos(fRadial);
|
|
|
|
|
|
points[0][0] = rect.left + height * fsin + width * (1 - fcos), points[0][1] = rect.top + height * (1 - fcos) - width * fsin;
|
|
points[1][0] = rect.right + height * fsin - width * (1 - fcos), points[1][1] = rect.top + height * (1 - fcos) + width * fsin;
|
|
points[2][0] = rect.right - height * fsin - width * (1 - fcos), points[2][1] = rect.bottom - height * (1 - fcos) + width * fsin;
|
|
points[3][0] = rect.left - height * fsin + width * (1 - fcos), points[3][1] = rect.bottom - height * (1 - fcos) - width * fsin;
|
|
|
|
float offset[2];
|
|
switch (rotation)
|
|
{
|
|
case xtpChartTextLeftTop:
|
|
offset[0] = -width * (1 - fcos) - height * fsin;
|
|
offset[1] = width * fsin - height * (1 - fcos);
|
|
break;
|
|
case xtpChartTextCenterTop:
|
|
offset[0] = -height * fsin, -height * (1 - fcos);
|
|
break;
|
|
case xtpChartTextRightTop:
|
|
offset[0] = width * (1 - fcos) - height * fsin, offset[1] = -width * fsin - height * (1 - fcos);
|
|
break;
|
|
case xtpChartTextLeftCenter:
|
|
offset[0] = -width * (1 - fcos), offset[1] = width * fsin;
|
|
break;
|
|
case xtpChartTextRightCenter:
|
|
offset[0] = width * (1 - fcos), offset[1] = -width * fsin;
|
|
break;
|
|
case xtpChartTextLeftBottom:
|
|
offset[0] = -width * (1 - fcos) + height * fsin, offset[1] = width * fsin + height * (1 - fcos);
|
|
break;
|
|
case xtpChartTextCenterBottom:
|
|
offset[0] = height * fsin, offset[1] = height * (1 - fcos);
|
|
break;
|
|
case xtpChartTextRightBottom:
|
|
offset[0] = width * (1 - fcos) + height * fsin, offset[1] = -width * fsin + height * (1 - fcos);
|
|
break;
|
|
default:
|
|
return;
|
|
}
|
|
|
|
for (int i = 0; i < 4; i++)
|
|
{
|
|
points[i][0] += offset[0];
|
|
points[i][0] += offset[1];
|
|
}
|
|
|
|
}
|
|
|
|
CXTPChartDeviceCommand* CXTPChartRotatedTextPainterBase::CreateDeviceCommand(CXTPChartDeviceContext* pDC, const CXTPChartColor& color)
|
|
{
|
|
UNREFERENCED_PARAMETER(pDC);
|
|
|
|
CRect rect = GetInitialTextRect();
|
|
CPoint offset;
|
|
|
|
CPoint center(rect.left + rect.Width() / 2, rect.top + rect.Height() / 2);
|
|
|
|
switch (CalculateRotation())
|
|
{
|
|
case xtpChartTextLeftTop:
|
|
offset = CPoint(rect.left, rect.top);
|
|
break;
|
|
case xtpChartTextCenterTop:
|
|
offset = CPoint(center.x, rect.top);
|
|
break;
|
|
case xtpChartTextRightTop:
|
|
offset = CPoint(rect.right, rect.top);
|
|
break;
|
|
case xtpChartTextLeftCenter:
|
|
offset = CPoint(rect.left, center.y);
|
|
break;
|
|
case xtpChartTextCenterCenter:
|
|
offset = CPoint(center.x, center.y);
|
|
break;
|
|
case xtpChartTextRightCenter:
|
|
offset = CPoint(rect.right, center.y);
|
|
break;
|
|
case xtpChartTextLeftBottom:
|
|
offset = CPoint(rect.left, rect.bottom);
|
|
break;
|
|
case xtpChartTextCenterBottom:
|
|
offset = CPoint(center.x, rect.bottom);
|
|
break;
|
|
case xtpChartTextRightBottom:
|
|
offset = CPoint(rect.right, rect.bottom);
|
|
break;
|
|
default:
|
|
offset = CPoint(0, 0);
|
|
break;
|
|
}
|
|
|
|
|
|
rect.OffsetRect(-offset.x, -offset.y);
|
|
|
|
CXTPChartSaveStateDeviceCommand* pStateGraphicsCommand = new CXTPChartSaveStateDeviceCommand();
|
|
|
|
CXTPChartDeviceCommand* pCommand = pStateGraphicsCommand;
|
|
|
|
pCommand = pCommand->AddChildCommand(new CXTPChartTranslateDeviceCommand((float)offset.x, (float)offset.y, 0));
|
|
pCommand = pCommand->AddChildCommand(new CXTPChartRotateDeviceCommand(m_fAngle));
|
|
|
|
CXTPChartDeviceCommand* pTextContainer = pCommand->AddChildCommand(new CXTPChartTextAntialiasingDeviceCommand(m_pTextProvider->GetAntialiasing()));
|
|
|
|
if (m_pMarkupUIElement)
|
|
pTextContainer->AddChildCommand(new CXTPChartMarkupElementDeviceCommand(m_pMarkupUIElement, m_pTextProvider->GetFont(), color, CXTPChartRectF(rect)));
|
|
else
|
|
pTextContainer->AddChildCommand(new CXTPChartBoundedTextDeviceCommand(m_strText, m_pTextProvider->GetFont(), color, CXTPChartRectF(rect)));
|
|
|
|
return pStateGraphicsCommand;
|
|
}
|
|
|
|
|
|
//////////////////////////////////////////////////////////////////////////
|
|
// CXTPChartRotatedTextPainterNearLine
|
|
|
|
CXTPChartRotatedTextPainterNearLine::CXTPChartRotatedTextPainterNearLine(CXTPChartDeviceContext* pDC, const CXTPChartString& text, CXTPChartTextElement* pTextProvider, CPoint ptBase, XTPChartNearTextPosition position, float fAngle)
|
|
: CXTPChartRotatedTextPainterBase(pDC, text, pTextProvider, ptBase)
|
|
{
|
|
m_nNearPosition = position;
|
|
m_fAngle = fAngle;
|
|
}
|
|
|
|
|
|
CPoint CXTPChartRotatedTextPainterNearLine::CalculateLeftTopPoint()
|
|
{
|
|
double fPI = acos(-1.0);
|
|
double fRadial = m_fAngle * fPI / 180.0;
|
|
int halfHeight = m_nHeight / 2;
|
|
|
|
switch (m_nNearPosition)
|
|
{
|
|
case xtpChartTextNearBottom:
|
|
if (m_fAngle == 0 || m_fAngle == 180)
|
|
return CPoint(m_ptBasePoint.x - m_nWidth / 2, m_ptBasePoint.y);
|
|
|
|
return m_fAngle < 180 ?
|
|
CPoint(m_ptBasePoint.x, m_ptBasePoint.y - (int)(m_nHeight / 2 * sin(fRadial))) :
|
|
CPoint(m_ptBasePoint.x - m_nWidth, m_ptBasePoint.y - (int)(m_nHeight / 2 * sin(fRadial - fPI)));
|
|
|
|
|
|
case xtpChartTextNearLeft:
|
|
if (m_fAngle == 90 || m_fAngle == 270)
|
|
return CPoint(m_ptBasePoint.x - m_nWidth / 2 - halfHeight, m_ptBasePoint.y - halfHeight);
|
|
return (m_fAngle < 90 || m_fAngle > 270) ?
|
|
CPoint(m_ptBasePoint.x - (int)(m_nWidth + fabs(halfHeight * sin(fRadial))), m_ptBasePoint.y - halfHeight) :
|
|
CPoint(m_ptBasePoint.x - (int)(fabs(halfHeight * cos(fRadial - fPI / 2))), m_ptBasePoint.y - halfHeight);
|
|
|
|
case xtpChartTextNearRight:
|
|
if (m_fAngle == 90 || m_fAngle == 270)
|
|
return CPoint(m_ptBasePoint.x - m_nWidth / 2 + halfHeight, m_ptBasePoint.y - halfHeight);
|
|
return (m_fAngle < 90 || m_fAngle > 270) ?
|
|
CPoint(m_ptBasePoint.x + (int)(fabs(halfHeight * sin(fRadial))), m_ptBasePoint.y - halfHeight) :
|
|
CPoint(m_ptBasePoint.x - m_nWidth + (int)(fabs(halfHeight * cos(fRadial - fPI / 2))), m_ptBasePoint.y - halfHeight);
|
|
|
|
|
|
case xtpChartTextNearTop:
|
|
if (m_fAngle == 0 || m_fAngle == 180)
|
|
return CPoint(m_ptBasePoint.x - m_nWidth / 2, m_ptBasePoint.y - m_nHeight);
|
|
return m_fAngle < 180 ?
|
|
CPoint(m_ptBasePoint.x - m_nWidth, m_ptBasePoint.y - (int)(halfHeight + fabs(halfHeight * cos(fRadial)))) :
|
|
CPoint(m_ptBasePoint.x, m_ptBasePoint.y - (int)(halfHeight + fabs(halfHeight * cos(fRadial - fPI))));
|
|
|
|
}
|
|
|
|
return CPoint(0, 0);
|
|
|
|
}
|
|
|
|
XTPChartTextRotation CXTPChartRotatedTextPainterNearLine::CalculateRotation()
|
|
{
|
|
switch (m_nNearPosition)
|
|
{
|
|
case xtpChartTextNearBottom:
|
|
if (m_fAngle == 0 || m_fAngle == 180)
|
|
return xtpChartTextCenterCenter;
|
|
return m_fAngle < 180 ? xtpChartTextLeftCenter : xtpChartTextRightCenter;
|
|
|
|
case xtpChartTextNearLeft:
|
|
if (m_fAngle == 90 || m_fAngle == 270)
|
|
return xtpChartTextCenterCenter;
|
|
return (m_fAngle < 90 || m_fAngle > 270) ? xtpChartTextRightCenter : xtpChartTextLeftCenter;
|
|
|
|
case xtpChartTextNearRight:
|
|
if (m_fAngle == 90 || m_fAngle == 270)
|
|
return xtpChartTextCenterCenter;
|
|
return (m_fAngle < 90 || m_fAngle > 270) ? xtpChartTextLeftCenter : xtpChartTextRightCenter;
|
|
|
|
case xtpChartTextNearTop:
|
|
if (m_fAngle == 0 || m_fAngle == 180)
|
|
return xtpChartTextCenterCenter;
|
|
return m_fAngle < 180 ? xtpChartTextRightCenter : xtpChartTextLeftCenter;
|
|
|
|
default:
|
|
return xtpChartTextCenterCenter;
|
|
}
|
|
}
|