Files
core/DesktopEditor/graphics/GraphicsPath.cpp
Alexander Trofimov 76ee07f61c [copyright] Update copyright header
Co-authored-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
Co-committed-by: Alexander Trofimov <alexander.trofimov@onlyoffice.com>
2026-05-14 08:23:56 +00:00

1585 lines
42 KiB
C++

/*
* Copyright (C) Ascensio System SIA, 2009-2026
*
* 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, together with the
* additional terms provided in the LICENSE file.
*
* 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: https://www.gnu.org/licenses/agpl-3.0.html
*
* You can contact Ascensio System SIA by email at info@onlyoffice.com
* or by postal mail at 20A-6 Ernesta Birznieka-Upisha Street, Riga,
* LV-1050, Latvia, European Union.
*
* The interactive user interfaces in modified versions of the Program
* are required to display Appropriate Legal Notices in accordance with
* Section 5 of the GNU AGPL version 3.
*
* No trademark rights are granted under this License.
*
* All non-code elements of the Product, including illustrations,
* icon sets, and technical writing content, are licensed under the
* Creative Commons Attribution-ShareAlike 4.0 International License:
* https://creativecommons.org/licenses/by-sa/4.0/legalcode
*
* This license applies only to such non-code elements and does not
* modify or replace the licensing terms applicable to the Program's
* source code, which remains licensed under the GNU Affero General
* Public License v3.
*
* SPDX-License-Identifier: AGPL-3.0-only
*/
#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)
{
// we don't use this. Will implement if needed.
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)
{
// Function to convert real angle to parametric ellipse definition
// i.e. x= a cos(t) y = b sin(t) - parametric ellipse definition.
// 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)
{
// convert angles to radians
int nRet = 0;
double dAngle1 = fAngle1 * 3.141592 / 180;
double dAngle2 = fAngle2 * 3.141592 / 180;
// Determine which quadrants the start and end points are in
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 ) );
// Draw a line to the arc start point
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);
// Continue drawing by quadrants
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)
{
// Calculate start, end and control points
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 ) );
// When drawing a full ellipse, AppendEllipse command is used which already contains 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 ) // Full ellipse
{
return (0 == Ellipse(fX + fWidth / 2, fY + fHeight / 2, fWidth / 2, fHeight / 2)) ? Ok : GenericError;
}
else // Ellipse arc
{
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 = 3.0 * ((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)) / 20.0;
}
else
{
std::vector<PointD> points = GetPoints(idx, 2);
area = (points[1].Y * points[0].X - points[1].X * points[0].Y) / 2.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);
bool reverse = IsClockwise() ^ other.IsClockwise();
for (unsigned i = 0; i < pointsCount; i++)
if (!points[i].Equals(otherPoints[reverse ? pointsCount - i - 1 : 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)
{
// Function to convert real angle to parametric ellipse definition
// i.e. x= a cos(t) y = b sin(t) - parametric ellipse definition.
// 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)
{
// convert angles to radians
int nRet = 0;
double dAngle1 = fAngle1 * 3.141592 / 180;
double dAngle2 = fAngle2 * 3.141592 / 180;
// Determine which quadrants the start and end points are in
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 ) );
// Draw a line to the arc start point
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);
// Continue drawing by quadrants
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)
{
// Calculate start, end and control points
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 ) );
// When drawing a full ellipse, AppendEllipse command is used which already contains 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 ) // Full ellipse
{
return (0 == Ellipse(fX + fWidth / 2, fY + fHeight / 2, fWidth / 2, fHeight / 2)) ? true : false;
}
else // Ellipse arc
{
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;
}
}
HRESULT IRenderer::AddPath(const Aggplus::CGraphicsPath& path)
{
if (path.GetPointCount() == 0)
return S_FALSE;
size_t length = path.GetPointCount() + path.GetCloseCount();
std::vector<Aggplus::PointD> points = path.GetPoints(0, length);
for (size_t i = 0; i < length; i++)
{
if (path.IsCurvePoint(i))
{
PathCommandCurveTo(points[i].X, points[i].Y,
points[i + 1].X, points[i + 1].Y,
points[i + 2].X, points[i + 2].Y);
i += 2;
}
else if (path.IsMovePoint(i))
PathCommandMoveTo(points[i].X, points[i].Y);
else if (path.IsLinePoint(i))
PathCommandLineTo(points[i].X, points[i].Y);
else
PathCommandClose();
}
return S_OK;
}