#pragma once #include "..\ap_AggPlusEnums.h" #include "..\ap_AggPlusTypes.h" #include "..\ap_StringFormat.h" #include "..\include\agg_path_storage.h" #pragma warning(disable : 4786) #include "..\include\agg_rasterizer_scanline_aa.h" #include "..\include\agg_conv_transform.h" #include "..\include\agg_conv_stroke.h" #include "..\include\agg_conv_contour.h" #include "..\include\agg_conv_unclose_polygon.h" #include "..\include\agg_bezier_arc.h" #include "..\include\agg_conv_curve.h" #include "Matrix.h" #include "..\..\Objects\ASCFontManager.h" #include "..\..\Objects\Structures.h" class ISimpleGraphicsPath { public: virtual bool _MoveTo(double x, double y) = 0; virtual bool _LineTo(double x, double y) = 0; virtual bool _CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) = 0; virtual bool _Close() = 0; }; namespace Aggplus { class CGraphicsPath : public ISimpleGraphicsPath { friend class CClip; friend class CASCFontManager; friend class CGraphics; public: CGraphicsPath() { m_bEvenOdd = false; } ~CGraphicsPath() { } CGraphicsPath* Clone() { CGraphicsPath* pNew = new CGraphicsPath(); pNew->m_agg_ps = m_agg_ps; pNew->m_bEvenOdd = m_bEvenOdd; return pNew; } inline Status Reset() { m_agg_ps.remove_all(); m_bIsMoveTo = false; return Ok; } inline void SetRuler(bool bEvenOdd) { m_bEvenOdd = bEvenOdd; } public: inline Status StartFigure() { m_agg_ps.start_new_path(); return Ok; } inline Status CloseFigure() { m_agg_ps.close_polygon(); return Ok; } inline bool Is_poly_closed() { if (!m_agg_ps.total_vertices()) return true; double x, y; unsigned int nTip = m_agg_ps.last_vertex(&x, &y); if (nTip & agg::path_flags_close) return true; return false; } inline Status MoveTo(double x, double y) { m_bIsMoveTo = true; m_agg_ps.move_to(x, y); return Ok; } inline Status LineTo(double x, double y) { m_agg_ps.line_to(x, y); return Ok; } inline Status CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) { m_agg_ps.curve4(x1, y1, x2, y2, x3, y3); return Ok; } inline Status AddLine(double x1, double y1, double x2, double y2) { if (Is_poly_closed()) { m_agg_ps.move_to(x1, y1); } else { m_agg_ps.line_to(x1, y1); } m_agg_ps.line_to(x2, y2); return Ok; } inline Status AddLines(double* pPoints, int nCount) { if (4 > nCount) { return InvalidParameter; } int nRet = 0; if (!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) { double* points = &pPoints[i * 2]; m_agg_ps.line_to(points[0], points[1]); } return Ok; } inline Status AddLines(float* pPoints, int nCount) { if (4 > nCount) { return InvalidParameter; } int nRet = 0; if (!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) { float* points = &pPoints[i * 2]; m_agg_ps.line_to((double)points[0], (double)points[1]); } return Ok; } // методы, которые просто будем сводить к трем основным inline Status AddBezier(double x1, double y1, double x2, double y2, double x3, double y3, double x4, double y4) { if (Is_poly_closed()) m_agg_ps.move_to(x1, y1); else m_agg_ps.line_to(x1, y1); m_agg_ps.curve4(x2, y2, x3, y3, x4, y4); return Ok; } inline Status AddBeziers(double* pPoints, int nCount) { if (8 > nCount) return InvalidParameter; if (!m_bIsMoveTo) { MoveTo(pPoints[0], pPoints[1]); } 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_agg_ps.concat_path(curve, 0); } else { 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; } inline Status AddBeziers(float* pPoints, int nCount) { if (8 > nCount) return InvalidParameter; float* points = pPoints; agg::curve4 curve; curve.approximation_method(agg::curve_inc); curve.approximation_scale(25.0); curve.init((double)points[0], (double)points[1], (double)points[2], (double)points[3], (double)points[4], (double)points[5], (double)points[6], (double)points[7]); if (Is_poly_closed()) { m_agg_ps.concat_path(curve, 0); } else { 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((double)points[0], (double)points[1], (double)points[2], (double)points[3], (double)points[4], (double)points[5]); } return Ok; } inline Status AddCurve(double* pPoints, int nCount) { // этим мы не пользуемся. Понадобится - реализую. return AddBeziers(pPoints, nCount); } inline Status 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_agg_ps.join_path(arc, 0); return Ok; } inline Status AddRectangle(double x, double y, double width, double height) { m_agg_ps.move_to(x, y); m_agg_ps.line_to(x + width, y); m_agg_ps.line_to(x + width, y + height); m_agg_ps.line_to(x, y + height); m_agg_ps.close_polygon(); return Ok; } inline Status AddPolygon(double* pPoints, int nCount) { if (2 > nCount) { return InvalidParameter; } int nRet = 0; if (Is_poly_closed()) { m_agg_ps.move_to(pPoints[0], pPoints[1]); } else { 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_agg_ps.line_to(points[0], points[1]); } m_agg_ps.close_polygon(); return Ok; } inline Status AddPath(const CGraphicsPath& oPath) { typedef agg::conv_curve conv_crv_type; agg::path_storage p_copy(m_agg_ps); conv_crv_type p3(p_copy); m_agg_ps.join_path(p3, 0); return Ok; } inline Status 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_agg_ps.concat_path(arc, 0); } else { m_agg_ps.join_path(arc, 0); } return Ok; } ULONG GetPointCount() const { ULONG nPointCount=0; ULONG nTotal = m_agg_ps.total_vertices(); double x, y; for(ULONG i = 0; i < nTotal; ++i) { ULONG nTip = m_agg_ps.vertex(i, &x, &y); if(nTip) { if (!(nTip & agg::path_flags_close)) { ++nPointCount; } } } return nPointCount; } Status GetPathPoints(PointF* points, int count) const { int nTotal = m_agg_ps.total_vertices(); double x, y; int i = 0, k = 0; while (k < count && i < nTotal) { unsigned int nTip = m_agg_ps.vertex(i, &x, &y); if (nTip) { if(!(nTip & agg::path_flags_close)) { points[k].X = float(x); points[k].Y = float(y); ++k; } } ++i; } return Ok; } Status GetLastPoint(double& x, double& y) { m_agg_ps.last_vertex(&x, &y); return Ok; } Status GetPathPoints(double* points, int count) const { int nTotal = m_agg_ps.total_vertices(); double x, y; int i = 0, k = 0; while (k < count && i < nTotal) { unsigned int nTip = 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 GetBounds(double& left, double& top, double& width, double& height) { unsigned int nTotal = 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_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; } } Status Transform(const CMatrix* matrix) { if (NULL != matrix) { agg::path_storage p2(m_agg_ps); agg::conv_transform trans(p2, matrix->m_agg_mtx); m_agg_ps.remove_all(); //2.3 m_agg_ps.add_path(trans, 0, false); m_agg_ps.concat_path(trans, 0); } return Ok; } virtual bool _MoveTo(double x, double y) { return (Ok == MoveTo(x, y)); } virtual bool _LineTo(double x, double y) { return (Ok == LineTo(x, y)); } virtual bool _CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) { return (Ok == CurveTo(x1, y1, x2, y2, x3, y3)); } virtual bool _Close() { return (Ok == CloseFigure()); } Status AddString(BSTR bstrText, IASCFontManager* pFont, double x, double y) { if (NULL == pFont) return InvalidParameter; pFont->LoadString(bstrText, (float)x, (float)y); CASCFontManager* pMan = (CASCFontManager*)pFont; return (S_OK == pMan->GetStringPath3(this)) ? Ok : InvalidParameter; } void z_Stroke(const NSStructures::CPen* Pen) { if (NULL == Pen) return; typedef agg::conv_stroke Path_Conv_Stroke; Path_Conv_Stroke pg(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(Pen->Size); //pg.auto_detect_orientation(true); agg::path_storage psNew; //2.3 psNew.add_path(pg, 0, false); psNew.concat_path(pg, 0); m_agg_ps = psNew; } void Widen(const NSStructures::CPen* Pen, const CMatrix* matrix, float flatness) { if (NULL == Pen || NULL == matrix || 0.0f == flatness) return; typedef agg::conv_curve conv_crv_type; typedef agg::conv_contour Path_Conv_Contour; conv_crv_type crv(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(Pen->Size); agg::line_join_e LineJoin; switch (Pen->LineJoin) { 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_agg_ps.concat_path(pg, 0); m_agg_ps = psNew; } agg::path_storage *z_get_agg_path_storage() { return(&m_agg_ps); } public: agg::path_storage m_agg_ps; bool m_bEvenOdd; bool m_bIsMoveTo; public: int EllipseArc(double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, BOOL 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 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 EllipseArc2(double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, BOOL 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 = min( 4, max( 1, nSecondPointQuard ) ); nFirstPointQuard = min( 4, max( 1, 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 EllipseArc3(double fX, double fY, double fXRad, double fYRad, double dAngle1, double dAngle2, double *pfXCur, double *pfYCur, BOOL bClockDirection = FALSE) { // Рассчитаем начальную, конечную и контрольные точки 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 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 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; } BOOL bClockDirection = FALSE; double fEndAngle = 360 - ( fSweepAngle + fStartAngle ); double fSrtAngle = 360 - fStartAngle; if( fSweepAngle > 0 ) bClockDirection = TRUE; if( abs(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; } }; class CGraphicsPathSimpleConverter : public ISimpleGraphicsPath { friend class CASCFontManager; private: IASCRenderer* m_pRenderer; bool m_bEvenOdd; bool m_bIsMoveTo; bool m_bIsClosed; agg::path_storage m_agg_ps; public: CGraphicsPathSimpleConverter() { m_pRenderer = NULL; m_bEvenOdd = false; m_bIsMoveTo = false; m_bIsClosed = false; } ~CGraphicsPathSimpleConverter() { RELEASEINTERFACE(m_pRenderer); } public: inline void SetRenderer(IASCRenderer* pRenderer) { RELEASEINTERFACE(m_pRenderer); m_pRenderer = pRenderer; ADDREFINTERFACE(m_pRenderer); } inline IASCRenderer* GetRenderer(BOOL bIsAddref = FALSE) { if (bIsAddref) { ADDREFINTERFACE(m_pRenderer); } return m_pRenderer; } public: inline bool PathCommandMoveTo(double fX, double fY) { return _MoveTo(fX, fY); } inline bool PathCommandLineTo(double fX, double fY) { return _LineTo(fX, fY); } inline bool PathCommandLinesTo(SAFEARRAY* pPoints) { if (NULL == pPoints) return false; LONG lCount = pPoints->rgsabound[0].cElements; double * pData = (double*)pPoints->pvData; if (2 == lCount) { return _LineTo(pData[0], pData[1]); } if (4 > lCount) { return false; } int nRet = 0; if (!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; } inline bool PathCommandCurveTo(double fX1, double fY1, double fX2, double fY2, double fX3, double fY3) { return _CurveTo(fX1, fY1, fX2, fY2, fX3, fY3); } inline bool PathCommandCurvesTo(SAFEARRAY* pPoints) { if (NULL == pPoints) return S_FALSE; LONG lCount = pPoints->rgsabound[0].cElements; double * pData = (double*)pPoints->pvData; if (8 > lCount) return false; if (!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; } inline bool PathCommandArcTo(double fX, double fY, double fWidth, double fHeight, double fStartAngle, double fSweepAngle) { return AddArc(fX, fY, fWidth, fHeight, -fStartAngle, -fSweepAngle); } inline bool PathCommandClose() { return _Close(); } inline bool PathCommandEnd() { return _Reset(); } inline bool PathCommandStart() { return _Start(); } inline bool PathCommandGetCurrentPoint(double* fX, double* fY) { m_agg_ps.last_vertex(fX, fY); return true; } inline bool PathCommandText(BSTR bsText, IASCFontManager* pManager, double fX, double fY, double fWidth, double fHeight, double fBaseLineOffset) { return AddString(bsText, pManager, fX, fY + fBaseLineOffset); } inline bool PathCommandTextEx(BSTR bsText, BSTR bsGidText, BSTR bsSourceCodeText, IASCFontManager* pManager, double fX, double fY, double fWidth, double fHeight, double fBaseLineOffset, DWORD lFlags) { if (NULL != bsGidText) { return PathCommandText(bsGidText, pManager, fX, fY, fWidth, fHeight, fBaseLineOffset); } return PathCommandText(bsText, pManager, fX, fY, fWidth, fHeight, fBaseLineOffset); } inline bool PathCommandGetBounds(double& left, double& top, double& width, double &height) { unsigned int nTotal = 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_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; } public: virtual bool _MoveTo(double x, double y) { m_bIsMoveTo = true; 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; } virtual bool _LineTo(double x, double y) { if (!m_bIsMoveTo) { _MoveTo(x, y); } 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; } virtual bool _CurveTo(double x1, double y1, double x2, double y2, double x3, double y3) { if (!m_bIsMoveTo) { _MoveTo(x1, y1); } 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; } virtual bool _Close() { m_bIsClosed = true; m_agg_ps.close_polygon(); if (NULL != m_pRenderer) { m_pRenderer->BeginCommand(c_nSimpleGraphicType); m_pRenderer->PathCommandClose(); m_pRenderer->EndCommand(c_nSimpleGraphicType); } return true; } inline bool _Reset() { m_bEvenOdd = false; m_bIsMoveTo = false; m_bIsClosed = false; m_agg_ps.remove_all(); if (NULL != m_pRenderer) { m_pRenderer->BeginCommand(c_nSimpleGraphicType); m_pRenderer->PathCommandEnd(); m_pRenderer->EndCommand(c_nSimpleGraphicType); } return true; } inline bool _Start() { 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; } protected: bool AddString(BSTR bstrText, IASCFontManager* pFont, double x, double y) { if (NULL == pFont) return false; pFont->LoadString(bstrText, (float)x, (float)y); CASCFontManager* pMan = (CASCFontManager*)pFont; return (S_OK == pMan->GetStringPath3(this)) ? true : false; } int EllipseArc(double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, BOOL 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 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 EllipseArc2(double fX, double fY, double fXRad, double fYRad, double fAngle1, double fAngle2, BOOL 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 = min( 4, max( 1, nSecondPointQuard ) ); nFirstPointQuard = min( 4, max( 1, 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 EllipseArc3(double fX, double fY, double fXRad, double fYRad, double dAngle1, double dAngle2, double *pfXCur, double *pfYCur, BOOL bClockDirection = FALSE) { // Рассчитаем начальную, конечную и контрольные точки 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 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 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; } BOOL bClockDirection = FALSE; double fEndAngle = 360 - ( fSweepAngle + fStartAngle ); double fSrtAngle = 360 - fStartAngle; if( fSweepAngle > 0 ) bClockDirection = TRUE; if( abs(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; } inline bool Is_poly_closed() { if (!m_agg_ps.total_vertices()) return true; double x, y; unsigned int nTip = m_agg_ps.last_vertex(&x, &y); if (nTip & agg::path_flags_close) return true; return false; } }; }