1552 lines
42 KiB
C++
1552 lines
42 KiB
C++
/*
|
||
* (c) Copyright Ascensio System SIA 2010-2023
|
||
*
|
||
* This program is a free software product. You can redistribute it and/or
|
||
* modify it under the terms of the GNU Affero General Public License (AGPL)
|
||
* version 3 as published by the Free Software Foundation. In accordance with
|
||
* Section 7(a) of the GNU AGPL its Section 15 shall be amended to the effect
|
||
* that Ascensio System SIA expressly excludes the warranty of non-infringement
|
||
* of any third-party rights.
|
||
*
|
||
* This program is distributed WITHOUT ANY WARRANTY; without even the implied
|
||
* warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For
|
||
* details, see the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||
*
|
||
* You can contact Ascensio System SIA at 20A-6 Ernesta Birznieka-Upish
|
||
* street, Riga, Latvia, EU, LV-1050.
|
||
*
|
||
* The interactive user interfaces in modified source and object code versions
|
||
* of the Program must display Appropriate Legal Notices, as required under
|
||
* Section 5 of the GNU AGPL version 3.
|
||
*
|
||
* Pursuant to Section 7(b) of the License you must retain the original Product
|
||
* logo when distributing the program. Pursuant to Section 7(e) we decline to
|
||
* grant you any rights under trademark law for use of our trademarks.
|
||
*
|
||
* All the Product's GUI elements, including illustrations and icon sets, as
|
||
* well as technical writing content are licensed under the terms of the
|
||
* Creative Commons Attribution-ShareAlike 4.0 International. See the License
|
||
* terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||
*
|
||
*/
|
||
#include "GraphicsPath_private.h"
|
||
#include "agg_bounding_rect.h"
|
||
#include <algorithm>
|
||
|
||
namespace Aggplus
|
||
{
|
||
// GraphicsPath
|
||
CGraphicsPath::CGraphicsPath() : ISimpleGraphicsPath()
|
||
{
|
||
m_internal = new CGraphicsPath_private();
|
||
}
|
||
|
||
CGraphicsPath::CGraphicsPath(const CGraphicsPath& other) noexcept
|
||
{
|
||
*this = other;
|
||
}
|
||
|
||
CGraphicsPath::CGraphicsPath(CGraphicsPath&& other) noexcept
|
||
{
|
||
*this = other;
|
||
}
|
||
|
||
CGraphicsPath::CGraphicsPath(const std::vector<CGraphicsPath>& paths) noexcept : CGraphicsPath()
|
||
{
|
||
if (paths.size() == 1)
|
||
*this = paths[0];
|
||
else
|
||
{
|
||
StartFigure();
|
||
for (const auto& p : paths)
|
||
{
|
||
unsigned length = p.GetPointCount();
|
||
std::vector<PointD> points = p.GetPoints(0, length);
|
||
for (unsigned j = 0; j < length; j++)
|
||
{
|
||
if (p.IsMovePoint(j))
|
||
MoveTo(points[j].X, points[j].Y);
|
||
else if (p.IsLinePoint(j))
|
||
LineTo(points[j].X, points[j].Y);
|
||
else if (p.IsCurvePoint(j))
|
||
{
|
||
CurveTo(points[j].X, points[j].Y,
|
||
points[j + 1].X, points[j + 1].Y,
|
||
points[j + 2].X, points[j + 2].Y);
|
||
j += 2;
|
||
}
|
||
}
|
||
//if (p.Is_poly_closed()) CloseFigure();
|
||
}
|
||
}
|
||
}
|
||
|
||
CGraphicsPath::~CGraphicsPath()
|
||
{
|
||
RELEASEOBJECT(m_internal);
|
||
}
|
||
|
||
CGraphicsPath* CGraphicsPath::Clone()
|
||
{
|
||
CGraphicsPath* pNew = new CGraphicsPath();
|
||
pNew->m_internal->m_agg_ps = m_internal->m_agg_ps;
|
||
pNew->m_internal->m_bEvenOdd = m_internal->m_bEvenOdd;
|
||
pNew->m_internal->m_bIsMoveTo = m_internal->m_bIsMoveTo;
|
||
return pNew;
|
||
}
|
||
|
||
Status CGraphicsPath::Reset()
|
||
{
|
||
m_internal->m_agg_ps.remove_all();
|
||
m_internal->m_bIsMoveTo = false;
|
||
return Ok;
|
||
}
|
||
|
||
void CGraphicsPath::SetRuler(bool bEvenOdd)
|
||
{
|
||
m_internal->m_bEvenOdd = bEvenOdd;
|
||
}
|
||
|
||
Status CGraphicsPath::StartFigure()
|
||
{
|
||
m_internal->m_agg_ps.start_new_path();
|
||
return Ok;
|
||
}
|
||
|
||
Status CGraphicsPath::CloseFigure()
|
||
{
|
||
m_internal->m_agg_ps.close_polygon();
|
||
return Ok;
|
||
}
|
||
|
||
bool CGraphicsPath::Is_poly_closed() const
|
||
{
|
||
if (!m_internal->m_agg_ps.total_vertices())
|
||
return true;
|
||
|
||
double x, y;
|
||
unsigned int nTip = m_internal->m_agg_ps.last_vertex(&x, &y);
|
||
|
||
if (nTip & agg::path_flags_close)
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
|
||
Status CGraphicsPath::MoveTo(double x, double y)
|
||
{
|
||
m_internal->m_bIsMoveTo = true;
|
||
m_internal->m_agg_ps.move_to(x, y);
|
||
return Ok;
|
||
}
|
||
Status CGraphicsPath::LineTo(double x, double y)
|
||
{
|
||
m_internal->m_agg_ps.line_to(x, y);
|
||
return Ok;
|
||
}
|
||
Status CGraphicsPath::CurveTo(double x1, double y1, double x2, double y2, double x3, double y3)
|
||
{
|
||
m_internal->m_agg_ps.curve4(x1, y1, x2, y2, x3, y3);
|
||
return Ok;
|
||
}
|
||
|
||
Status CGraphicsPath::AddLine(double x1, double y1, double x2, double y2)
|
||
{
|
||
if (Is_poly_closed())
|
||
{
|
||
m_internal->m_agg_ps.move_to(x1, y1);
|
||
}
|
||
else
|
||
{
|
||
m_internal->m_agg_ps.line_to(x1, y1);
|
||
}
|
||
|
||
m_internal->m_agg_ps.line_to(x2, y2);
|
||
return Ok;
|
||
}
|
||
|
||
Status CGraphicsPath::AddLines(double* pPoints, int nCount)
|
||
{
|
||
if (4 > nCount)
|
||
{
|
||
return InvalidParameter;
|
||
}
|
||
int nRet = 0;
|
||
|
||
if (!m_internal->m_bIsMoveTo)
|
||
{
|
||
MoveTo(pPoints[0], pPoints[1]);
|
||
}
|
||
|
||
/*if (Is_poly_closed())
|
||
{
|
||
m_agg_ps.move_to((double)pPoints[0], (double)pPoints[1]);
|
||
}
|
||
else
|
||
{
|
||
m_agg_ps.line_to((double)pPoints[0], (double)pPoints[1]);
|
||
}*/
|
||
|
||
int n = (nCount / 2) - 1;
|
||
|
||
for (int i = 1; i <= n; ++i)
|
||
{
|
||
const double* points = &pPoints[i * 2];
|
||
m_internal->m_agg_ps.line_to(points[0], points[1]);
|
||
}
|
||
return Ok;
|
||
}
|
||
|
||
Status CGraphicsPath::AddBezier(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4)
|
||
{
|
||
if (Is_poly_closed())
|
||
m_internal->m_agg_ps.move_to(x1, y1);
|
||
else
|
||
m_internal->m_agg_ps.line_to(x1, y1);
|
||
|
||
m_internal->m_agg_ps.curve4(x2, y2, x3, y3, x4, y4);
|
||
return Ok;
|
||
}
|
||
Status CGraphicsPath::AddBeziers(double* pPoints, int nCount)
|
||
{
|
||
if (8 > nCount)
|
||
return InvalidParameter;
|
||
|
||
if (!m_internal->m_bIsMoveTo)
|
||
{
|
||
MoveTo(pPoints[0], pPoints[1]);
|
||
}
|
||
|
||
const double* points = pPoints;
|
||
|
||
agg::curve4 curve;
|
||
curve.approximation_method(agg::curve_inc);
|
||
curve.approximation_scale(25.0);
|
||
curve.init(points[0], points[1], points[2], points[3], points[4], points[5], points[6], points[7]);
|
||
|
||
if (Is_poly_closed())
|
||
{
|
||
m_internal->m_agg_ps.concat_path(curve, 0);
|
||
}
|
||
else
|
||
{
|
||
m_internal->m_agg_ps.join_path(curve, 0);
|
||
}
|
||
|
||
int nCountTo = (nCount - 8) / 6;
|
||
for (int i = 0; i < nCountTo; ++i)
|
||
{
|
||
points = pPoints + 8 + 6 * i;
|
||
CurveTo(points[0], points[1], points[2], points[3], points[4], points[5]);
|
||
}
|
||
|
||
return Ok;
|
||
}
|
||
|
||
Status CGraphicsPath::AddCurve(double* pPoints, int nCount)
|
||
{
|
||
// этим мы не пользуемся. Понадобится - реализую.
|
||
return AddBeziers(pPoints, nCount);
|
||
}
|
||
|
||
Status CGraphicsPath::AddEllipse(double x, double y, double width, double height)
|
||
{
|
||
agg::bezier_arc arc(x+width/2.0, y+height/2.0, width/2.0, height/2.0, 0.0, agg::pi2);
|
||
//2.3 m_agg_ps.add_path(arc, 0, true);
|
||
m_internal->m_agg_ps.join_path(arc, 0);
|
||
return Ok;
|
||
}
|
||
Status CGraphicsPath::AddRectangle(double x, double y, double width, double height)
|
||
{
|
||
m_internal->m_agg_ps.move_to(x, y);
|
||
m_internal->m_agg_ps.line_to(x + width, y);
|
||
m_internal->m_agg_ps.line_to(x + width, y + height);
|
||
m_internal->m_agg_ps.line_to(x, y + height);
|
||
m_internal->m_agg_ps.line_to(x, y);
|
||
m_internal->m_agg_ps.close_polygon();
|
||
return Ok;
|
||
}
|
||
Status CGraphicsPath::AddRoundRectangle(double x, double y, double width, double height, double cx, double cy)
|
||
{
|
||
m_internal->m_agg_ps.move_to(x + cx, y);
|
||
m_internal->m_agg_ps.line_to(x + width - cx, y);
|
||
agg::bezier_arc arc1(x + width - cx, y + cy, cx, cy, -agg::pi / 2.0, agg::pi / 2.0);
|
||
m_internal->m_agg_ps.join_path(arc1, 0);
|
||
m_internal->m_agg_ps.line_to(x + width, y + height - cy);
|
||
agg::bezier_arc arc2(x + width - cx, y + height - cy, cx, cy, 0.0, agg::pi / 2.0);
|
||
m_internal->m_agg_ps.join_path(arc2, 0);
|
||
m_internal->m_agg_ps.line_to(x + cx, y + height);
|
||
agg::bezier_arc arc3(x + cx, y + height - cy, cx, cy, agg::pi / 2.0, agg::pi / 2.0);
|
||
m_internal->m_agg_ps.join_path(arc3, 0);
|
||
m_internal->m_agg_ps.line_to(x, y + cy);
|
||
agg::bezier_arc arc4(x + cx, y + cy, cx, cy, agg::pi, agg::pi / 2.0);
|
||
m_internal->m_agg_ps.join_path(arc4, 0);
|
||
m_internal->m_agg_ps.close_polygon();
|
||
return Ok;
|
||
}
|
||
Status CGraphicsPath::AddPolygon(double* pPoints, int nCount)
|
||
{
|
||
if (2 > nCount)
|
||
{
|
||
return InvalidParameter;
|
||
}
|
||
int nRet = 0;
|
||
|
||
if (Is_poly_closed())
|
||
{
|
||
m_internal->m_agg_ps.move_to(pPoints[0], pPoints[1]);
|
||
}
|
||
else
|
||
{
|
||
m_internal->m_agg_ps.line_to(pPoints[0], pPoints[1]);
|
||
}
|
||
|
||
int n = (nCount / 2) - 1;
|
||
|
||
for (int i = 1; i < n; ++i)
|
||
{
|
||
double* points = &pPoints[i * 2];
|
||
m_internal->m_agg_ps.line_to(points[0], points[1]);
|
||
}
|
||
|
||
m_internal->m_agg_ps.close_polygon();
|
||
return Ok;
|
||
}
|
||
Status CGraphicsPath::AddPath(const CGraphicsPath& oPath)
|
||
{
|
||
typedef agg::conv_curve<agg::path_storage> conv_crv_type;
|
||
|
||
agg::path_storage p_copy(oPath.m_internal->m_agg_ps);
|
||
conv_crv_type p3(p_copy);
|
||
|
||
m_internal->m_agg_ps.join_path(p3, 0);
|
||
return Ok;
|
||
}
|
||
Status CGraphicsPath::AddArc(double x, double y, double width, double height, double startAngle, double sweepAngle)
|
||
{
|
||
if(sweepAngle >= 360.0)
|
||
{
|
||
sweepAngle = 360;
|
||
}
|
||
|
||
agg::bezier_arc arc(x+width/2.00, y+height/2.00, width/2.00, height/2.00, agg::deg2rad(startAngle), agg::deg2rad(sweepAngle));
|
||
|
||
//2.3 m_agg_ps.add_path(arc, 0, !z_is_poly_closed());
|
||
|
||
if (Is_poly_closed())
|
||
{
|
||
m_internal->m_agg_ps.concat_path(arc, 0);
|
||
}
|
||
else
|
||
{
|
||
m_internal->m_agg_ps.join_path(arc, 0);
|
||
}
|
||
|
||
return Ok;
|
||
}
|
||
|
||
ULONG CGraphicsPath::GetPointCount() const
|
||
{
|
||
ULONG nPointCount=0;
|
||
ULONG nTotal = m_internal->m_agg_ps.total_vertices();
|
||
|
||
double x, y;
|
||
|
||
for(ULONG i = 0; i < nTotal; ++i)
|
||
{
|
||
ULONG nTip = m_internal->m_agg_ps.vertex(i, &x, &y);
|
||
if(nTip)
|
||
{
|
||
if (!(nTip & agg::path_flags_close))
|
||
{
|
||
++nPointCount;
|
||
}
|
||
}
|
||
}
|
||
return nPointCount;
|
||
}
|
||
|
||
Status CGraphicsPath::GetPathPoints(PointF* points, int count) const
|
||
{
|
||
int nTotal = m_internal->m_agg_ps.total_vertices();
|
||
double x, y;
|
||
int i = 0, k = 0;
|
||
|
||
while (k < count && i < nTotal)
|
||
{
|
||
unsigned int nTip = m_internal->m_agg_ps.vertex(i, &x, &y);
|
||
if (nTip)
|
||
{
|
||
if(!(nTip & agg::path_flags_close))
|
||
{
|
||
points[k].X = REAL(x);
|
||
points[k].Y = REAL(y);
|
||
++k;
|
||
}
|
||
}
|
||
++i;
|
||
}
|
||
|
||
return Ok;
|
||
}
|
||
|
||
Status CGraphicsPath::GetLastPoint(double& x, double& y)
|
||
{
|
||
m_internal->m_agg_ps.last_vertex(&x, &y);
|
||
return Ok;
|
||
}
|
||
|
||
Status CGraphicsPath::GetPathPoints(double* points, int count) const
|
||
{
|
||
int nTotal = m_internal->m_agg_ps.total_vertices();
|
||
double x, y;
|
||
int i = 0, k = 0;
|
||
|
||
while (k < count && i < nTotal)
|
||
{
|
||
unsigned int nTip = m_internal->m_agg_ps.vertex(i, &x, &y);
|
||
if (nTip)
|
||
{
|
||
if(!(nTip & agg::path_flags_close))
|
||
{
|
||
points[2 * k] = x;
|
||
points[2 * k + 1] = y;
|
||
++k;
|
||
}
|
||
}
|
||
++i;
|
||
}
|
||
|
||
return Ok;
|
||
}
|
||
|
||
void CGraphicsPath::GetBounds(double& left, double& top, double& width, double& height) const
|
||
{
|
||
unsigned int nTotal = m_internal->m_agg_ps.total_vertices();
|
||
if (nTotal)
|
||
{
|
||
agg::rect_d bounds(1e100, 1e100, -1e100, -1e100);
|
||
double x, y;
|
||
for(unsigned int i = 0; i < nTotal; i++)
|
||
{
|
||
unsigned int nTip = m_internal->m_agg_ps.vertex(i, &x, &y);
|
||
if(agg::is_vertex(nTip))
|
||
{
|
||
if(x < bounds.x1) bounds.x1 = x;
|
||
if(y < bounds.y1) bounds.y1 = y;
|
||
if(x > bounds.x2) bounds.x2 = x;
|
||
if(y > bounds.y2) bounds.y2 = y;
|
||
}
|
||
}
|
||
|
||
left = bounds.x1;
|
||
top = bounds.y1;
|
||
width = (bounds.x2 - bounds.x1);
|
||
height = (bounds.y2 - bounds.y1);
|
||
}
|
||
else
|
||
{
|
||
left = 0;
|
||
top = 0;
|
||
width = 0;
|
||
height = 0;
|
||
}
|
||
}
|
||
|
||
void CGraphicsPath::GetBoundsAccurate(double& left, double& top, double& width, double& height) const
|
||
{
|
||
agg::conv_curve<agg::path_storage> storage(m_internal->m_agg_ps);
|
||
storage.approximation_scale(25.0);
|
||
storage.approximation_method(agg::curve_inc);
|
||
|
||
double r = 0, b = 0;
|
||
agg::bounding_rect_single(storage, 0,
|
||
&left, &top,
|
||
&r, &b);
|
||
|
||
width = r - left;
|
||
height = b - top;
|
||
}
|
||
|
||
Status CGraphicsPath::Transform(const CMatrix* matrix)
|
||
{
|
||
if (NULL != matrix)
|
||
{
|
||
agg::path_storage p2(m_internal->m_agg_ps);
|
||
agg::conv_transform<agg::path_storage> trans(p2, matrix->m_internal->m_agg_mtx);
|
||
m_internal->m_agg_ps.remove_all();
|
||
//2.3 m_agg_ps.add_path(trans, 0, false);
|
||
m_internal->m_agg_ps.concat_path(trans, 0);
|
||
}
|
||
return Ok;
|
||
}
|
||
|
||
bool CGraphicsPath::_MoveTo(double x, double y)
|
||
{
|
||
if (NULL != m_internal->m_pTransform)
|
||
{
|
||
m_internal->m_pTransform->TransformPoint(x, y);
|
||
}
|
||
return (Ok == MoveTo(x, y));
|
||
}
|
||
bool CGraphicsPath::_LineTo(double x, double y)
|
||
{
|
||
if (NULL != m_internal->m_pTransform)
|
||
{
|
||
m_internal->m_pTransform->TransformPoint(x, y);
|
||
}
|
||
return (Ok == LineTo(x, y));
|
||
}
|
||
bool CGraphicsPath::_CurveTo(double x1, double y1, double x2, double y2, double x3, double y3)
|
||
{
|
||
if (NULL != m_internal->m_pTransform)
|
||
{
|
||
m_internal->m_pTransform->TransformPoint(x1, y1);
|
||
m_internal->m_pTransform->TransformPoint(x2, y2);
|
||
m_internal->m_pTransform->TransformPoint(x3, y3);
|
||
}
|
||
return (Ok == CurveTo(x1, y1, x2, y2, x3, y3));
|
||
}
|
||
bool CGraphicsPath::_Close()
|
||
{
|
||
return (Ok == CloseFigure());
|
||
}
|
||
|
||
Status CGraphicsPath::AddString(const std::wstring& strText, NSFonts::IFontManager* pFont, double x, double y)
|
||
{
|
||
if (NULL == pFont)
|
||
return InvalidParameter;
|
||
|
||
pFont->SetTextMatrix(1, 0, 0, 1, 0, 0);
|
||
pFont->LoadString1(strText, (float)x, (float)y);
|
||
return (TRUE == pFont->GetStringPath(this)) ? Ok : InvalidParameter;
|
||
}
|
||
Status CGraphicsPath::AddString(const unsigned int* pGids, const unsigned int nGidsCount, NSFonts::IFontManager* pFont, double x, double y)
|
||
{
|
||
if (NULL == pFont)
|
||
return InvalidParameter;
|
||
|
||
pFont->SetTextMatrix(1, 0, 0, 1, 0, 0);
|
||
pFont->LoadString1(pGids, nGidsCount, (float)x, (float)y);
|
||
return (TRUE == pFont->GetStringPath(this)) ? Ok : InvalidParameter;
|
||
}
|
||
|
||
Status CGraphicsPath::AddStringC(const LONG& lText, NSFonts::IFontManager* pFont, double x, double y)
|
||
{
|
||
if (NULL == pFont)
|
||
return InvalidParameter;
|
||
|
||
unsigned int _c = (int)lText;
|
||
pFont->SetTextMatrix(1, 0, 0, 1, 0, 0);
|
||
pFont->LoadString1(&_c, 1, (float)x, (float)y);
|
||
return (TRUE == pFont->GetStringPath(this)) ? Ok : InvalidParameter;
|
||
}
|
||
|
||
void CGraphicsPath::z_Stroke(const double& size)
|
||
{
|
||
typedef agg::conv_stroke<agg::path_storage> Path_Conv_Stroke;
|
||
Path_Conv_Stroke pg(m_internal->m_agg_ps);
|
||
pg.line_join(agg::round_join);
|
||
pg.line_cap(agg::round_cap);
|
||
pg.approximation_scale(25.00);
|
||
//pg.miter_limit(0.50);
|
||
|
||
pg.width(size);
|
||
//pg.auto_detect_orientation(true);
|
||
|
||
agg::path_storage psNew;
|
||
//2.3 psNew.add_path(pg, 0, false);
|
||
psNew.concat_path(pg, 0);
|
||
|
||
m_internal->m_agg_ps = psNew;
|
||
}
|
||
|
||
void CGraphicsPath::Widen(const double& size, const Aggplus::LineJoin& join, const CMatrix* matrix, float flatness)
|
||
{
|
||
if (NULL == matrix || 0.0f == flatness)
|
||
return;
|
||
|
||
typedef agg::conv_curve<agg::path_storage> conv_crv_type;
|
||
|
||
typedef agg::conv_contour<conv_crv_type> Path_Conv_Contour;
|
||
|
||
conv_crv_type crv(m_internal->m_agg_ps);
|
||
Path_Conv_Contour pg(crv);
|
||
|
||
pg.miter_limit(0.50);
|
||
//pg.miter_limit_theta(0.05);
|
||
//pg.approximation_scale(2.00);
|
||
|
||
pg.width(size);
|
||
|
||
agg::line_join_e LineJoin;
|
||
switch (join)
|
||
{
|
||
case LineJoinMiter : LineJoin=agg::miter_join; break;
|
||
case LineJoinBevel : LineJoin=agg::bevel_join; break;
|
||
default:
|
||
case LineJoinRound : LineJoin=agg::round_join; break;
|
||
case LineJoinMiterClipped: LineJoin=agg::miter_join_revert; break;
|
||
}
|
||
pg.line_join(LineJoin);
|
||
|
||
pg.auto_detect_orientation(false);
|
||
|
||
agg::path_storage psNew;
|
||
//2.3 psNew.add_path(pg, 0, false);
|
||
//m_agg_ps.concat_path(pg, 0);
|
||
m_internal->m_agg_ps.concat_path(pg, 0);
|
||
m_internal->m_agg_ps = psNew;
|
||
}
|
||
|
||
int CGraphicsPath::EllipseArc(double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, INT bClockDirection)
|
||
{
|
||
int nRet = 0;
|
||
|
||
while ( fAngle1 < 0 )
|
||
fAngle1 += 360;
|
||
|
||
while ( fAngle1 > 360 )
|
||
fAngle1 -= 360;
|
||
|
||
while ( fAngle2 < 0 )
|
||
fAngle2 += 360;
|
||
|
||
while ( fAngle2 >= 360 )
|
||
fAngle2 -= 360;
|
||
|
||
if ( !bClockDirection )
|
||
{
|
||
if ( fAngle1 <= fAngle2 )
|
||
nRet = EllipseArc2( fX, fY, fXRad, fYRad, fAngle1, fAngle2, FALSE );
|
||
else
|
||
{
|
||
nRet += EllipseArc2( fX, fY, fXRad, fYRad, fAngle1, 360, FALSE );
|
||
nRet += EllipseArc2( fX, fY, fXRad, fYRad, 0, fAngle2, FALSE );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ( fAngle1 >= fAngle2 )
|
||
nRet = EllipseArc2( fX, fY, fXRad, fYRad, fAngle1, fAngle2, TRUE );
|
||
else
|
||
{
|
||
nRet += EllipseArc2( fX, fY, fXRad, fYRad, fAngle1, 0, TRUE );
|
||
nRet += EllipseArc2( fX, fY, fXRad, fYRad, 360, fAngle2, TRUE );
|
||
}
|
||
}
|
||
return nRet;
|
||
}
|
||
|
||
double CGraphicsPath::AngToEllPrm(double fAngle, double fXRad, double fYRad)
|
||
{
|
||
// Функция для перевода реального угла в параметрическое задание эллписа
|
||
// т.е. x= a cos(t) y = b sin(t) - параметрическое задание эллписа.
|
||
// x = r cos(p), y = r sin(p) => t = atan2( sin(p) / b, cos(p) / a );
|
||
return atan2( sin( fAngle ) / fYRad, cos( fAngle ) / fXRad );
|
||
}
|
||
|
||
int CGraphicsPath::EllipseArc2(double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, INT bClockDirection)
|
||
{
|
||
// переведем углы в радианы
|
||
int nRet = 0;
|
||
|
||
double dAngle1 = fAngle1 * 3.141592 / 180;
|
||
double dAngle2 = fAngle2 * 3.141592 / 180;
|
||
|
||
// Выясним в каких четвертях находятся начальная и конечная точки
|
||
unsigned int nFirstPointQuard = int(fAngle1) / 90 + 1;
|
||
unsigned int nSecondPointQuard = int(fAngle2) / 90 + 1;
|
||
nSecondPointQuard = std::min( 4, std::max( 1, (int)nSecondPointQuard ) );
|
||
nFirstPointQuard = std::min( 4, std::max( 1, (int)nFirstPointQuard ) );
|
||
// Проведем линию в начальную точку дуги
|
||
double fStartX = 0.0, fStartY = 0.0, fEndX = 0.0, fEndY = 0.0;
|
||
|
||
fStartX = fX + fXRad * cos( AngToEllPrm( dAngle1, fXRad, fYRad ) );
|
||
fStartY = fY + fYRad * sin( AngToEllPrm( dAngle1, fXRad, fYRad ) );
|
||
|
||
LineTo(fStartX, fStartY);
|
||
|
||
// Дальше рисуем по четверям
|
||
|
||
double fCurX = fStartX, fCurY = fStartY;
|
||
double dStartAngle = dAngle1;
|
||
double dEndAngle = 0;
|
||
|
||
if ( !bClockDirection )
|
||
{
|
||
for( unsigned int nIndex = nFirstPointQuard; nIndex <= nSecondPointQuard; nIndex++ )
|
||
{
|
||
if ( nIndex == nSecondPointQuard )
|
||
dEndAngle = dAngle2;
|
||
else
|
||
dEndAngle = (90 * (nIndex ) ) * 3.141592f / 180;
|
||
if ( !( nIndex == nFirstPointQuard ) )
|
||
dStartAngle = (90 * (nIndex - 1 ) ) * 3.141592f / 180;
|
||
|
||
EllipseArc3(fX, fY, fXRad, fYRad, AngToEllPrm( dStartAngle, fXRad, fYRad ), AngToEllPrm( dEndAngle, fXRad, fYRad ), &fEndX, &fEndY, FALSE);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for( unsigned int nIndex = nFirstPointQuard; nIndex >= nSecondPointQuard; nIndex-- )
|
||
{
|
||
if ( nIndex == nFirstPointQuard )
|
||
dStartAngle = dAngle1;
|
||
else
|
||
dStartAngle = (90 * (nIndex ) ) * 3.141592f / 180;
|
||
if ( !( nIndex == nSecondPointQuard ) )
|
||
dEndAngle = (90 * (nIndex - 1 ) ) * 3.141592f / 180;
|
||
else
|
||
dEndAngle = dAngle2;
|
||
|
||
EllipseArc3(fX, fY, fXRad, fYRad, AngToEllPrm( dStartAngle, fXRad, fYRad ), AngToEllPrm( dEndAngle, fXRad, fYRad ), &fEndX, &fEndY, FALSE);
|
||
}
|
||
}
|
||
|
||
return nRet;
|
||
}
|
||
|
||
int CGraphicsPath::EllipseArc3(double fX, double fY, double fXRad, double fYRad, double dAngle1, double dAngle2, double *pfXCur, double *pfYCur, INT bClockDirection)
|
||
{
|
||
// Рассчитаем начальную, конечную и контрольные точки
|
||
double fX1 = 0.0, fX2 = 0.0, fY1 = 0.0, fY2 = 0.0;
|
||
double fCX1 = 0.0, fCX2 = 0.0, fCY1 = 0.0, fCY2 = 0.0;
|
||
|
||
double fAlpha = sin( dAngle2 - dAngle1 ) * ( sqrt( 4.0 + 3.0 * tan( (dAngle2 - dAngle1) / 2.0 ) * tan( (dAngle2 - dAngle1) / 2.0 ) ) - 1.0 ) / 3.0;
|
||
|
||
double fKoef = 1;
|
||
|
||
fX1 = fX + fXRad * cos( dAngle1 );
|
||
fY1 = fY + fYRad * sin( dAngle1 );
|
||
|
||
fX2 = fX + fXRad * cos( dAngle2 );
|
||
fY2 = fY + fYRad * sin( dAngle2 );
|
||
|
||
fCX1 = fX1 - fAlpha * fXRad * sin ( dAngle1 );
|
||
fCY1 = fY1 + fAlpha * fYRad * cos ( dAngle1 );
|
||
|
||
fCX2 = fX2 + fAlpha * fXRad * sin ( dAngle2 );
|
||
fCY2 = fY2 - fAlpha * fYRad * cos ( dAngle2 );
|
||
|
||
if ( !bClockDirection )
|
||
{
|
||
CurveTo(fCX1, fCY1, fCX2, fCY2, fX2, fY2);
|
||
|
||
*pfXCur = fX2;
|
||
*pfYCur = fY2;
|
||
}
|
||
else
|
||
{
|
||
CurveTo(fCX2, fCY2, fCX1, fCY1, fX1, fY1);
|
||
|
||
*pfXCur = fX1;
|
||
*pfYCur = fY1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int CGraphicsPath::Ellipse(double fX, double fY, double fXRad, double fYRad)
|
||
{
|
||
MoveTo(fX - fXRad, fY);
|
||
|
||
double c_fKappa = 0.552;
|
||
CurveTo(fX - fXRad, fY + fYRad * c_fKappa, fX - fXRad * c_fKappa, fY + fYRad, fX, fY + fYRad);
|
||
CurveTo(fX + fXRad * c_fKappa, fY + fYRad, fX + fXRad, fY + fYRad * c_fKappa, fX + fXRad, fY);
|
||
CurveTo(fX + fXRad, fY - fYRad * c_fKappa, fX + fXRad * c_fKappa, fY - fYRad, fX, fY - fYRad);
|
||
CurveTo(fX - fXRad * c_fKappa, fY - fYRad, fX - fXRad, fY - fYRad * c_fKappa, fX - fXRad, fY);
|
||
|
||
return 0;
|
||
}
|
||
|
||
Status CGraphicsPath::AddArc2(double fX, double fY, double fWidth, double fHeight, double fStartAngle, double fSweepAngle)
|
||
{
|
||
if (0 >= fWidth || 0 >= fHeight)
|
||
return InvalidParameter;
|
||
|
||
if ( Is_poly_closed() )
|
||
{
|
||
double dStartAngle = fStartAngle * agg::pi / 180;
|
||
double fStartX = fX + fWidth / 2.0 + fWidth / 2 * cos( AngToEllPrm( dStartAngle, fWidth / 2, fHeight / 2 ) );
|
||
double fStartY = fY + fHeight / 2.0 - fHeight / 2 * sin( AngToEllPrm ( dStartAngle, fWidth / 2, fHeight / 2 ) );
|
||
|
||
// В случае, когда эллипс рисуется целиком используется команда AppendEllipse, в которой команда MoveTo уже есть
|
||
if ( fSweepAngle < 360 )
|
||
if ( Ok != MoveTo( fStartX, fStartY ) )
|
||
return GenericError;
|
||
}
|
||
|
||
INT bClockDirection = FALSE;
|
||
double fEndAngle = 360 - ( fSweepAngle + fStartAngle );
|
||
double fSrtAngle = 360 - fStartAngle;
|
||
if( fSweepAngle > 0 )
|
||
bClockDirection = TRUE;
|
||
|
||
if( fabs(fSweepAngle) >= 360 ) // Целый эллипс
|
||
{
|
||
return (0 == Ellipse(fX + fWidth / 2, fY + fHeight / 2, fWidth / 2, fHeight / 2)) ? Ok : GenericError;
|
||
}
|
||
else // Дуга эллипса
|
||
{
|
||
return (0 == EllipseArc(fX + fWidth / 2, fY + fHeight / 2, fWidth / 2, fHeight / 2, fSrtAngle, fEndAngle, bClockDirection)) ? Ok : GenericError;
|
||
}
|
||
|
||
return Ok;
|
||
}
|
||
|
||
bool CGraphicsPath::IsPointInPath(const double& x, const double& y)
|
||
{
|
||
agg::rasterizer_scanline_aa<agg::rasterizer_sl_clip_dbl> rasterizer;
|
||
agg::conv_curve<agg::path_storage> c_c_path(m_internal->m_agg_ps);
|
||
rasterizer.add_path(c_c_path);
|
||
|
||
return rasterizer.hit_test((int)x, (int)y);
|
||
}
|
||
|
||
unsigned CGraphicsPath::GetCloseCount() const noexcept
|
||
{
|
||
unsigned countClose = 0;
|
||
for (unsigned i = 0; i < m_internal->m_agg_ps.total_vertices(); i++)
|
||
if (IsClosePoint(i))
|
||
countClose++;
|
||
|
||
return countClose;
|
||
}
|
||
|
||
unsigned CGraphicsPath::GetMoveCount() const noexcept
|
||
{
|
||
unsigned countMove = 0;
|
||
for (unsigned i = 0; i < m_internal->m_agg_ps.total_vertices(); i++)
|
||
if (IsMovePoint(i))
|
||
countMove++;
|
||
|
||
return countMove;
|
||
}
|
||
|
||
bool CGraphicsPath::IsClockwise() const noexcept
|
||
{
|
||
return GetArea() >= 0;
|
||
}
|
||
|
||
bool CGraphicsPath::IsMovePoint(unsigned idx) const noexcept
|
||
{
|
||
if (idx >= m_internal->m_agg_ps.total_vertices()) return false;
|
||
return this->m_internal->m_agg_ps.command(idx) == agg::path_cmd_move_to;
|
||
}
|
||
|
||
bool CGraphicsPath::IsCurvePoint(unsigned idx) const noexcept
|
||
{
|
||
if (idx >= m_internal->m_agg_ps.total_vertices()) return false;
|
||
return this->m_internal->m_agg_ps.command(idx) == agg::path_cmd_curve4;
|
||
}
|
||
|
||
bool CGraphicsPath::IsLinePoint(unsigned idx) const noexcept
|
||
{
|
||
if (idx >= m_internal->m_agg_ps.total_vertices()) return false;
|
||
return this->m_internal->m_agg_ps.command(idx) == agg::path_cmd_line_to;
|
||
}
|
||
|
||
bool CGraphicsPath::IsClosePoint(unsigned idx) const noexcept
|
||
{
|
||
if (idx >= m_internal->m_agg_ps.total_vertices()) return false;
|
||
return this->m_internal->m_agg_ps.command(idx) == (agg::path_cmd_end_poly | agg::path_flags_close);
|
||
}
|
||
|
||
std::vector<PointD> CGraphicsPath::GetPoints(unsigned idx, unsigned count) const noexcept
|
||
{
|
||
std::vector<PointD> points;
|
||
unsigned length = m_internal->m_agg_ps.total_vertices();
|
||
for (unsigned i = 0; i < count; i++)
|
||
{
|
||
double x,y;
|
||
if (idx + i > length) break;
|
||
this->m_internal->m_agg_ps.vertex(idx + i, &x, &y);
|
||
points.push_back(PointD(x, y));
|
||
}
|
||
|
||
return points;
|
||
}
|
||
|
||
double CGraphicsPath::GetArea() const noexcept
|
||
{
|
||
double area = 0.0;
|
||
unsigned length = GetPointCount() - 1;
|
||
for (unsigned i = 0; i < length; i++)
|
||
{
|
||
area += GetArea(i, IsCurvePoint(i + 1));
|
||
if (IsCurvePoint(i + 1)) i += 2;
|
||
}
|
||
|
||
return area;
|
||
}
|
||
|
||
double CGraphicsPath::GetArea(unsigned idx, bool isCurve) const noexcept
|
||
{
|
||
double area;
|
||
if (isCurve)
|
||
{
|
||
std::vector<PointD> points = GetPoints(idx, 4);
|
||
area = (points[3].Y - points[0].Y) * (points[1].X + points[2].X)
|
||
- (points[3].X - points[0].X) * (points[1].Y + points[2].Y)
|
||
+ points[1].Y * (points[0].X - points[2].X)
|
||
- points[1].X * (points[0].Y - points[2].Y)
|
||
+ points[3].Y * (points[2].X + points[0].X / 3.0)
|
||
- points[3].X * (points[2].Y + points[0].Y / 3.0);
|
||
}
|
||
else
|
||
{
|
||
std::vector<PointD> points = GetPoints(idx, 2);
|
||
area = 4.0 * (points[1].Y * points[0].X - points[1].X * points[0].Y) / 3.0;
|
||
}
|
||
|
||
return area;
|
||
}
|
||
|
||
std::vector<CGraphicsPath> CGraphicsPath::GetSubPaths() const
|
||
{
|
||
std::vector<CGraphicsPath> result;
|
||
|
||
CGraphicsPath subPath;
|
||
bool close = true;
|
||
for (unsigned i = 0; i < m_internal->m_agg_ps.total_vertices(); i++)
|
||
{
|
||
if (IsMovePoint(i))
|
||
{
|
||
if (!close)
|
||
{
|
||
PointD firstPoint = subPath.GetPoints(0, 1)[0];
|
||
double x, y;
|
||
subPath.GetLastPoint(x, y);
|
||
if ((abs(firstPoint.X - x) >= 1e-2 || abs(firstPoint.Y - y) >= 1e-2) ||
|
||
subPath.GetPointCount() == 1)
|
||
subPath.LineTo(firstPoint.X, firstPoint.Y);
|
||
|
||
subPath.CloseFigure();
|
||
result.push_back(subPath);
|
||
subPath.Reset();
|
||
}
|
||
subPath.StartFigure();
|
||
PointD point = GetPoints(i, 1)[0];
|
||
subPath.MoveTo(point.X, point.Y);
|
||
close = false;
|
||
}
|
||
else if (IsCurvePoint(i))
|
||
{
|
||
std::vector<PointD> points = GetPoints(i, 3);
|
||
subPath.CurveTo(points[0].X, points[0].Y,
|
||
points[1].X, points[1].Y,
|
||
points[2].X, points[2].Y);
|
||
i += 2;
|
||
}
|
||
else if (IsLinePoint(i))
|
||
{
|
||
PointD point = GetPoints(i, 1)[0];
|
||
subPath.LineTo(point.X, point.Y);
|
||
}
|
||
else if (IsClosePoint(i))
|
||
{
|
||
PointD firstPoint = subPath.GetPoints(0, 1)[0];
|
||
double x, y;
|
||
subPath.GetLastPoint(x, y);
|
||
|
||
if ((abs(firstPoint.X - x) >= 1e-2 || abs(firstPoint.Y - y) >= 1e-2) || subPath.GetPointCount() == 1)
|
||
subPath.LineTo(firstPoint.X, firstPoint.Y);
|
||
|
||
subPath.CloseFigure();
|
||
result.push_back(subPath);
|
||
subPath.Reset();
|
||
close = true;
|
||
}
|
||
}
|
||
|
||
if (!close)
|
||
{
|
||
PointD firstPoint = subPath.GetPoints(0, 1)[0];
|
||
double x, y;
|
||
subPath.GetLastPoint(x, y);
|
||
|
||
if ((abs(firstPoint.X - x) <= 1e-2 && abs(firstPoint.Y - y) <= 1e-2) ||
|
||
subPath.GetPointCount() == 1)
|
||
{
|
||
if (!firstPoint.Equals(PointD(x, y)) ||
|
||
subPath.GetPointCount() == 1)
|
||
subPath.LineTo(firstPoint.X, firstPoint.Y);
|
||
subPath.CloseFigure();
|
||
}
|
||
|
||
result.push_back(subPath);
|
||
}
|
||
|
||
return result;
|
||
}
|
||
|
||
CGraphicsPath& CGraphicsPath::operator=(const CGraphicsPath& other) noexcept
|
||
{
|
||
if (&other == this)
|
||
return *this;
|
||
|
||
m_internal = new CGraphicsPath_private;
|
||
m_internal->m_agg_ps = other.m_internal->m_agg_ps;
|
||
m_internal->m_bEvenOdd = other.m_internal->m_bEvenOdd;
|
||
m_internal->m_bIsMoveTo = other.m_internal->m_bIsMoveTo;
|
||
|
||
return *this;
|
||
}
|
||
|
||
CGraphicsPath& CGraphicsPath::operator=(CGraphicsPath&& other) noexcept
|
||
{
|
||
if (&other == this)
|
||
return *this;
|
||
|
||
m_internal = other.m_internal;
|
||
other.m_internal = nullptr;
|
||
|
||
return *this;
|
||
}
|
||
|
||
bool CGraphicsPath::operator==(const CGraphicsPath& other) noexcept
|
||
{
|
||
unsigned pointsCount = GetPointCount(),
|
||
otherPointsCount = other.GetPointCount();
|
||
|
||
if (pointsCount != otherPointsCount)
|
||
return false;
|
||
|
||
std::vector<PointD> points = GetPoints(0, pointsCount),
|
||
otherPoints = other.GetPoints(0, otherPointsCount);
|
||
|
||
for (unsigned i = 0; i < pointsCount; i++)
|
||
if (!points[i].Equals(otherPoints[i]))
|
||
return false;
|
||
|
||
return true;
|
||
}
|
||
}
|
||
|
||
namespace Aggplus
|
||
{
|
||
// Converter
|
||
CGraphicsPathSimpleConverter::CGraphicsPathSimpleConverter()
|
||
{
|
||
m_pRenderer = NULL;
|
||
m_internal = new CGraphicsPathSimpleConverter_private();
|
||
}
|
||
|
||
CGraphicsPathSimpleConverter::~CGraphicsPathSimpleConverter()
|
||
{
|
||
RELEASEINTERFACE(m_pRenderer);
|
||
RELEASEOBJECT(m_internal);
|
||
}
|
||
|
||
void CGraphicsPathSimpleConverter::SetRenderer(IRenderer* pRenderer)
|
||
{
|
||
RELEASEINTERFACE(m_pRenderer);
|
||
m_pRenderer = pRenderer;
|
||
ADDREFINTERFACE(m_pRenderer);
|
||
}
|
||
IRenderer* CGraphicsPathSimpleConverter::GetRenderer(INT bIsAddref)
|
||
{
|
||
if (bIsAddref)
|
||
{
|
||
ADDREFINTERFACE(m_pRenderer);
|
||
}
|
||
|
||
return m_pRenderer;
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::PathCommandMoveTo(double fX, double fY)
|
||
{
|
||
return _MoveTo(fX, fY);
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandLineTo(double fX, double fY)
|
||
{
|
||
return _LineTo(fX, fY);
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandLinesTo(double* pPoints, LONG lCount)
|
||
{
|
||
if (NULL == pPoints)
|
||
return false;
|
||
|
||
double* pData = pPoints;
|
||
|
||
if (2 == lCount)
|
||
{
|
||
return _LineTo(pData[0], pData[1]);
|
||
}
|
||
|
||
if (4 > lCount)
|
||
{
|
||
return false;
|
||
}
|
||
|
||
int nRet = 0;
|
||
|
||
if (!m_internal->m_bIsMoveTo)
|
||
{
|
||
_MoveTo(pData[0], pData[1]);
|
||
}
|
||
|
||
int n = (lCount / 2) - 1;
|
||
|
||
for (int i = 1; i <= n; ++i)
|
||
{
|
||
double* points = &pData[i * 2];
|
||
_LineTo(points[0], points[1]);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::PathCommandCurveTo(double fX1, double fY1, double fX2, double fY2, double fX3, double fY3)
|
||
{
|
||
return _CurveTo(fX1, fY1, fX2, fY2, fX3, fY3);
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandCurvesTo(double* pData, LONG lCount)
|
||
{
|
||
if (NULL == pData)
|
||
return false;
|
||
|
||
if (8 > lCount)
|
||
return false;
|
||
|
||
if (!m_internal->m_bIsMoveTo)
|
||
{
|
||
_MoveTo(pData[0], pData[1]);
|
||
|
||
pData += 2;
|
||
lCount -= 2;
|
||
}
|
||
else
|
||
{
|
||
_LineTo(pData[0], pData[1]);
|
||
|
||
pData += 2;
|
||
lCount -= 2;
|
||
}
|
||
|
||
double* points = pData;
|
||
|
||
int nCountTo = (lCount) / 6;
|
||
for (int i = 0; i < nCountTo; ++i)
|
||
{
|
||
points = pData + 6 * i;
|
||
_CurveTo(points[0], points[1], points[2], points[3], points[4], points[5]);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandArcTo(double fX, double fY, double fWidth, double fHeight, double fStartAngle, double fSweepAngle)
|
||
{
|
||
return AddArc(fX, fY, fWidth, fHeight, -fStartAngle, -fSweepAngle);
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandClose()
|
||
{
|
||
return _Close();
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandEnd()
|
||
{
|
||
return _Reset();
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::PathCommandStart()
|
||
{
|
||
return _Start();
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandGetCurrentPoint(double* fX, double* fY)
|
||
{
|
||
m_internal->m_agg_ps.last_vertex(fX, fY);
|
||
return true;
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandText(const std::wstring& bsText, NSFonts::IFontManager* pManager, double fX, double fY, double fWidth, double fHeight, double fBaseLineOffset)
|
||
{
|
||
return AddString(bsText, pManager, fX, fY + fBaseLineOffset);
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandTextEx(std::wstring& bsText, std::wstring& bsGidText, NSFonts::IFontManager* pManager, double fX, double fY, double fWidth, double fHeight, double fBaseLineOffset, DWORD lFlags)
|
||
{
|
||
if (!bsGidText.empty())
|
||
{
|
||
return PathCommandText(bsGidText, pManager, fX, fY, fWidth, fHeight, fBaseLineOffset);
|
||
}
|
||
|
||
return PathCommandText(bsText, pManager, fX, fY, fWidth, fHeight, fBaseLineOffset);
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::PathCommandText2(const int* pUnicodes, const int* pGids, const int& nCount, NSFonts::IFontManager* pManager,
|
||
const double& x, const double& y, const double& w, const double& h)
|
||
{
|
||
if (NULL == pGids)
|
||
{
|
||
pManager->SetStringGID(FALSE);
|
||
pManager->LoadString1((const unsigned int*)pUnicodes, (unsigned int)nCount, (float)x, (float)y);
|
||
return (TRUE == pManager->GetStringPath(this)) ? true : false;
|
||
}
|
||
else
|
||
{
|
||
pManager->SetStringGID(TRUE);
|
||
pManager->LoadString1((const unsigned int*)pGids, (unsigned int)nCount, (float)x, (float)y);
|
||
return (TRUE == pManager->GetStringPath(this)) ? true : false;
|
||
}
|
||
}
|
||
bool CGraphicsPathSimpleConverter::PathCommandText2(const std::wstring& sUnicodes, const int* pGids, const int& nCount, NSFonts::IFontManager* pManager,
|
||
const double& x, const double& y, const double& w, const double& h)
|
||
{
|
||
if (NULL == pGids)
|
||
{
|
||
pManager->SetStringGID(FALSE);
|
||
pManager->LoadString1(sUnicodes, (float)x, (float)y);
|
||
return (TRUE == pManager->GetStringPath(this)) ? true : false;
|
||
}
|
||
else
|
||
{
|
||
pManager->SetStringGID(TRUE);
|
||
pManager->LoadString1((const unsigned int*)pGids, (unsigned int)nCount, (float)x, (float)y);
|
||
return (TRUE == pManager->GetStringPath(this)) ? true : false;
|
||
}
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::PathCommandGetBounds(double& left, double& top, double& width, double &height)
|
||
{
|
||
unsigned int nTotal = m_internal->m_agg_ps.total_vertices();
|
||
if (nTotal)
|
||
{
|
||
agg::rect_d bounds(1e100, 1e100, -1e100, -1e100);
|
||
double x, y;
|
||
for(unsigned int i = 0; i < nTotal; i++)
|
||
{
|
||
unsigned int nTip = m_internal->m_agg_ps.vertex(i, &x, &y);
|
||
if(agg::is_vertex(nTip))
|
||
{
|
||
if(x < bounds.x1) bounds.x1 = x;
|
||
if(y < bounds.y1) bounds.y1 = y;
|
||
if(x > bounds.x2) bounds.x2 = x;
|
||
if(y > bounds.y2) bounds.y2 = y;
|
||
}
|
||
}
|
||
|
||
left = bounds.x1;
|
||
top = bounds.y1;
|
||
width = (bounds.x2 - bounds.x1);
|
||
height = (bounds.y2 - bounds.y1);
|
||
}
|
||
else
|
||
{
|
||
left = 0;
|
||
top = 0;
|
||
width = 0;
|
||
height = 0;
|
||
}
|
||
return true;
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::_MoveTo(double x, double y)
|
||
{
|
||
m_internal->m_bIsMoveTo = true;
|
||
m_internal->m_agg_ps.move_to(x, y);
|
||
|
||
if (NULL != m_pRenderer)
|
||
{
|
||
m_pRenderer->BeginCommand(c_nSimpleGraphicType);
|
||
m_pRenderer->PathCommandMoveTo(x, y);
|
||
m_pRenderer->EndCommand(c_nSimpleGraphicType);
|
||
}
|
||
return true;
|
||
}
|
||
bool CGraphicsPathSimpleConverter::_LineTo(double x, double y)
|
||
{
|
||
if (!m_internal->m_bIsMoveTo)
|
||
{
|
||
_MoveTo(x, y);
|
||
}
|
||
|
||
m_internal->m_agg_ps.line_to(x, y);
|
||
|
||
if (NULL != m_pRenderer)
|
||
{
|
||
m_pRenderer->BeginCommand(c_nSimpleGraphicType);
|
||
m_pRenderer->PathCommandLineTo(x, y);
|
||
m_pRenderer->EndCommand(c_nSimpleGraphicType);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
bool CGraphicsPathSimpleConverter::_CurveTo(double x1, double y1, double x2, double y2, double x3, double y3)
|
||
{
|
||
if (!m_internal->m_bIsMoveTo)
|
||
{
|
||
_MoveTo(x1, y1);
|
||
}
|
||
|
||
m_internal->m_agg_ps.curve4(x1, y1, x2, y2, x3, y3);
|
||
|
||
if (NULL != m_pRenderer)
|
||
{
|
||
m_pRenderer->BeginCommand(c_nSimpleGraphicType);
|
||
m_pRenderer->PathCommandCurveTo(x1, y1, x2, y2, x3, y3);
|
||
m_pRenderer->EndCommand(c_nSimpleGraphicType);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
bool CGraphicsPathSimpleConverter::_Close()
|
||
{
|
||
m_internal->m_bIsClosed = true;
|
||
m_internal->m_agg_ps.close_polygon();
|
||
|
||
if (NULL != m_pRenderer)
|
||
{
|
||
m_pRenderer->BeginCommand(c_nSimpleGraphicType);
|
||
m_pRenderer->PathCommandClose();
|
||
m_pRenderer->EndCommand(c_nSimpleGraphicType);
|
||
}
|
||
return true;
|
||
}
|
||
bool CGraphicsPathSimpleConverter::_Reset()
|
||
{
|
||
m_internal->m_bEvenOdd = false;
|
||
m_internal->m_bIsMoveTo = false;
|
||
m_internal->m_bIsClosed = false;
|
||
|
||
m_internal->m_agg_ps.remove_all();
|
||
|
||
if (NULL != m_pRenderer)
|
||
{
|
||
m_pRenderer->BeginCommand(c_nSimpleGraphicType);
|
||
m_pRenderer->PathCommandEnd();
|
||
m_pRenderer->EndCommand(c_nSimpleGraphicType);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
bool CGraphicsPathSimpleConverter::_Start()
|
||
{
|
||
m_internal->m_agg_ps.start_new_path();
|
||
|
||
if (NULL != m_pRenderer)
|
||
{
|
||
m_pRenderer->BeginCommand(c_nSimpleGraphicType);
|
||
m_pRenderer->PathCommandStart();
|
||
m_pRenderer->EndCommand(c_nSimpleGraphicType);
|
||
}
|
||
|
||
return true;
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::AddString(const std::wstring& bstrText, NSFonts::IFontManager* pFont, double x, double y)
|
||
{
|
||
if (NULL == pFont)
|
||
return false;
|
||
|
||
pFont->LoadString1(bstrText, (float)x, (float)y);
|
||
return (TRUE == pFont->GetStringPath(this)) ? true : false;
|
||
}
|
||
|
||
int CGraphicsPathSimpleConverter::EllipseArc(double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, INT bClockDirection)
|
||
{
|
||
int nRet = 0;
|
||
|
||
while ( fAngle1 < 0 )
|
||
fAngle1 += 360;
|
||
|
||
while ( fAngle1 > 360 )
|
||
fAngle1 -= 360;
|
||
|
||
while ( fAngle2 < 0 )
|
||
fAngle2 += 360;
|
||
|
||
while ( fAngle2 >= 360 )
|
||
fAngle2 -= 360;
|
||
|
||
if ( !bClockDirection )
|
||
{
|
||
if ( fAngle1 <= fAngle2 )
|
||
nRet = EllipseArc2( fX, fY, fXRad, fYRad, fAngle1, fAngle2, FALSE );
|
||
else
|
||
{
|
||
nRet += EllipseArc2( fX, fY, fXRad, fYRad, fAngle1, 360, FALSE );
|
||
nRet += EllipseArc2( fX, fY, fXRad, fYRad, 0, fAngle2, FALSE );
|
||
}
|
||
}
|
||
else
|
||
{
|
||
if ( fAngle1 >= fAngle2 )
|
||
nRet = EllipseArc2( fX, fY, fXRad, fYRad, fAngle1, fAngle2, TRUE );
|
||
else
|
||
{
|
||
nRet += EllipseArc2( fX, fY, fXRad, fYRad, fAngle1, 0, TRUE );
|
||
nRet += EllipseArc2( fX, fY, fXRad, fYRad, 360, fAngle2, TRUE );
|
||
}
|
||
}
|
||
return nRet;
|
||
}
|
||
|
||
double CGraphicsPathSimpleConverter::AngToEllPrm(double fAngle, double fXRad, double fYRad)
|
||
{
|
||
// Функция для перевода реального угла в параметрическое задание эллписа
|
||
// т.е. x= a cos(t) y = b sin(t) - параметрическое задание эллписа.
|
||
// x = r cos(p), y = r sin(p) => t = atan2( sin(p) / b, cos(p) / a );
|
||
return atan2( sin( fAngle ) / fYRad, cos( fAngle ) / fXRad );
|
||
}
|
||
|
||
int CGraphicsPathSimpleConverter::EllipseArc2(double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, INT bClockDirection)
|
||
{
|
||
// переведем углы в радианы
|
||
int nRet = 0;
|
||
|
||
double dAngle1 = fAngle1 * 3.141592 / 180;
|
||
double dAngle2 = fAngle2 * 3.141592 / 180;
|
||
|
||
// Выясним в каких четвертях находятся начальная и конечная точки
|
||
unsigned int nFirstPointQuard = int(fAngle1) / 90 + 1;
|
||
unsigned int nSecondPointQuard = int(fAngle2) / 90 + 1;
|
||
nSecondPointQuard = std::min( 4, std::max( 1, (int)nSecondPointQuard ) );
|
||
nFirstPointQuard = std::min( 4, std::max( 1, (int)nFirstPointQuard ) );
|
||
// Проведем линию в начальную точку дуги
|
||
double fStartX = 0.0, fStartY = 0.0, fEndX = 0.0, fEndY = 0.0;
|
||
|
||
fStartX = fX + fXRad * cos( AngToEllPrm( dAngle1, fXRad, fYRad ) );
|
||
fStartY = fY + fYRad * sin( AngToEllPrm( dAngle1, fXRad, fYRad ) );
|
||
|
||
_LineTo(fStartX, fStartY);
|
||
|
||
// Дальше рисуем по четверям
|
||
|
||
double fCurX = fStartX, fCurY = fStartY;
|
||
double dStartAngle = dAngle1;
|
||
double dEndAngle = 0;
|
||
|
||
if ( !bClockDirection )
|
||
{
|
||
for( unsigned int nIndex = nFirstPointQuard; nIndex <= nSecondPointQuard; nIndex++ )
|
||
{
|
||
if ( nIndex == nSecondPointQuard )
|
||
dEndAngle = dAngle2;
|
||
else
|
||
dEndAngle = (90 * (nIndex ) ) * 3.141592f / 180;
|
||
if ( !( nIndex == nFirstPointQuard ) )
|
||
dStartAngle = (90 * (nIndex - 1 ) ) * 3.141592f / 180;
|
||
|
||
EllipseArc3(fX, fY, fXRad, fYRad, AngToEllPrm( dStartAngle, fXRad, fYRad ), AngToEllPrm( dEndAngle, fXRad, fYRad ), &fEndX, &fEndY, FALSE);
|
||
}
|
||
}
|
||
else
|
||
{
|
||
for( unsigned int nIndex = nFirstPointQuard; nIndex >= nSecondPointQuard; nIndex-- )
|
||
{
|
||
if ( nIndex == nFirstPointQuard )
|
||
dStartAngle = dAngle1;
|
||
else
|
||
dStartAngle = (90 * (nIndex ) ) * 3.141592f / 180;
|
||
if ( !( nIndex == nSecondPointQuard ) )
|
||
dEndAngle = (90 * (nIndex - 1 ) ) * 3.141592f / 180;
|
||
else
|
||
dEndAngle = dAngle2;
|
||
|
||
EllipseArc3(fX, fY, fXRad, fYRad, AngToEllPrm( dStartAngle, fXRad, fYRad ), AngToEllPrm( dEndAngle, fXRad, fYRad ), &fEndX, &fEndY, FALSE);
|
||
}
|
||
}
|
||
|
||
return nRet;
|
||
}
|
||
|
||
int CGraphicsPathSimpleConverter::EllipseArc3(double fX, double fY, double fXRad, double fYRad, double dAngle1, double dAngle2, double *pfXCur, double *pfYCur, INT bClockDirection)
|
||
{
|
||
// Рассчитаем начальную, конечную и контрольные точки
|
||
double fX1 = 0.0, fX2 = 0.0, fY1 = 0.0, fY2 = 0.0;
|
||
double fCX1 = 0.0, fCX2 = 0.0, fCY1 = 0.0, fCY2 = 0.0;
|
||
|
||
double fAlpha = sin( dAngle2 - dAngle1 ) * ( sqrt( 4.0 + 3.0 * tan( (dAngle2 - dAngle1) / 2.0 ) * tan( (dAngle2 - dAngle1) / 2.0 ) ) - 1.0 ) / 3.0;
|
||
|
||
double fKoef = 1;
|
||
|
||
fX1 = fX + fXRad * cos( dAngle1 );
|
||
fY1 = fY + fYRad * sin( dAngle1 );
|
||
|
||
fX2 = fX + fXRad * cos( dAngle2 );
|
||
fY2 = fY + fYRad * sin( dAngle2 );
|
||
|
||
fCX1 = fX1 - fAlpha * fXRad * sin ( dAngle1 );
|
||
fCY1 = fY1 + fAlpha * fYRad * cos ( dAngle1 );
|
||
|
||
fCX2 = fX2 + fAlpha * fXRad * sin ( dAngle2 );
|
||
fCY2 = fY2 - fAlpha * fYRad * cos ( dAngle2 );
|
||
|
||
if ( !bClockDirection )
|
||
{
|
||
_CurveTo(fCX1, fCY1, fCX2, fCY2, fX2, fY2);
|
||
|
||
*pfXCur = fX2;
|
||
*pfYCur = fY2;
|
||
}
|
||
else
|
||
{
|
||
_CurveTo(fCX2, fCY2, fCX1, fCY1, fX1, fY1);
|
||
|
||
*pfXCur = fX1;
|
||
*pfYCur = fY1;
|
||
}
|
||
return 0;
|
||
}
|
||
|
||
int CGraphicsPathSimpleConverter::Ellipse(double fX, double fY, double fXRad, double fYRad)
|
||
{
|
||
_MoveTo(fX - fXRad, fY);
|
||
|
||
double c_fKappa = 0.552;
|
||
_CurveTo(fX - fXRad, fY + fYRad * c_fKappa, fX - fXRad * c_fKappa, fY + fYRad, fX, fY + fYRad);
|
||
_CurveTo(fX + fXRad * c_fKappa, fY + fYRad, fX + fXRad, fY + fYRad * c_fKappa, fX + fXRad, fY);
|
||
_CurveTo(fX + fXRad, fY - fYRad * c_fKappa, fX + fXRad * c_fKappa, fY - fYRad, fX, fY - fYRad);
|
||
_CurveTo(fX - fXRad * c_fKappa, fY - fYRad, fX - fXRad, fY - fYRad * c_fKappa, fX - fXRad, fY);
|
||
|
||
return 0;
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::AddArc(double fX, double fY, double fWidth, double fHeight, double fStartAngle, double fSweepAngle)
|
||
{
|
||
if (0 >= fWidth || 0 >= fHeight)
|
||
return false;
|
||
|
||
if ( Is_poly_closed() )
|
||
{
|
||
double dStartAngle = fStartAngle * agg::pi / 180;
|
||
double fStartX = fX + fWidth / 2.0 + fWidth / 2 * cos( AngToEllPrm( dStartAngle, fWidth / 2, fHeight / 2 ) );
|
||
double fStartY = fY + fHeight / 2.0 - fHeight / 2 * sin( AngToEllPrm ( dStartAngle, fWidth / 2, fHeight / 2 ) );
|
||
|
||
// В случае, когда эллипс рисуется целиком используется команда AppendEllipse, в которой команда MoveTo уже есть
|
||
if ( fSweepAngle < 360 )
|
||
if ( false == _MoveTo( fStartX, fStartY ) )
|
||
return false;
|
||
}
|
||
|
||
INT bClockDirection = FALSE;
|
||
double fEndAngle = 360 - ( fSweepAngle + fStartAngle );
|
||
double fSrtAngle = 360 - fStartAngle;
|
||
if( fSweepAngle > 0 )
|
||
bClockDirection = TRUE;
|
||
|
||
if( fabs(fSweepAngle) >= 360 ) // Целый эллипс
|
||
{
|
||
return (0 == Ellipse(fX + fWidth / 2, fY + fHeight / 2, fWidth / 2, fHeight / 2)) ? true : false;
|
||
}
|
||
else // Дуга эллипса
|
||
{
|
||
return (0 == EllipseArc(fX + fWidth / 2, fY + fHeight / 2, fWidth / 2, fHeight / 2, fSrtAngle, fEndAngle, bClockDirection)) ? true : false;
|
||
}
|
||
|
||
return Ok;
|
||
}
|
||
|
||
bool CGraphicsPathSimpleConverter::Is_poly_closed()
|
||
{
|
||
if (!m_internal->m_agg_ps.total_vertices())
|
||
return true;
|
||
|
||
double x, y;
|
||
unsigned int nTip = m_internal->m_agg_ps.last_vertex(&x, &y);
|
||
|
||
if (nTip & agg::path_flags_close)
|
||
return true;
|
||
|
||
return false;
|
||
}
|
||
}
|