Beispiel #1
0
//*******************************************************************************
void CBCGPKnob::OnDrawTickMarkTextLabel(CBCGPGraphicsManager* pGM, const CBCGPTextFormat& tf, const CBCGPRect& rectText, const CString& strLabel, double dblVal, int nScale, const CBCGPBrush& br)
{
    if (m_sizeIcon.cx == 0)
    {
        CBCGPCircularGaugeImpl::OnDrawTickMarkTextLabel(pGM, tf, rectText, strLabel, dblVal, nScale, br);
        return;
    }

    if (m_sizeIcon.cy == 0)
    {
        m_sizeIcon.cy = pGM->GetImageSize(m_Icons).cy;
    }

    CBCGPSize sizeIcon(m_sizeIcon.cx * m_sizeScaleRatio.cx, m_sizeIcon.cy * m_sizeScaleRatio.cy);

    double cx = 0.;
    double cy = 0.;

    double angle = 0.;

    if (ValueToAngle(dblVal, angle, nScale))
    {
        cx = sizeIcon.cx * cos(bcg_deg2rad(angle)) / 2;
    }

    CBCGPPoint ptImage(
        rectText.left + max(0., .5 * (rectText.Width() - sizeIcon.cx)) + cx,
        rectText.top + max(0., .5 * (rectText.Height() - sizeIcon.cy)) + cy);

    pGM->DrawImage(m_Icons, ptImage, sizeIcon, 1., CBCGPImage::BCGP_IMAGE_INTERPOLATION_MODE_LINEAR,
                   CBCGPRect(CBCGPPoint(m_sizeIcon.cx * m_nCurrLabelIndex, 0), m_sizeIcon));
}
//*******************************************************************************
CBCGPGeometry* CBCGPChartInterLineColoringEffect::CreateClipGeometry(double dblOrigin)
{
	CBCGPChartAxis* pYAxis = m_pSeries1->GetRelatedAxis(CBCGPChartSeries::AI_Y);

	ASSERT_VALID(pYAxis);

	CBCGPRect rectBounds = pYAxis->GetBoundingRect();
	BOOL bIsVertical = pYAxis->IsVertical();

	double dblVal = pYAxis->PointFromValue(dblOrigin, TRUE);

	rectBounds.Normalize();

	if (bIsVertical)
	{
		rectBounds.top = dblVal;
	}
	else
	{
		rectBounds.right = dblVal;
	}

	CBCGPRectangleGeometry* pGeometry = new CBCGPRectangleGeometry(rectBounds);
	return pGeometry;
}
//*******************************************************************************
void CBCGPChartTextObject::OnDrawShape(CBCGPGraphicsManager* pGM, const CBCGPRect& rectDiagram)
{
	if (m_bDrawConnector && !m_format.m_outlineFormat.m_brLineColor.IsEmpty())
	{
		CBCGPRect rectShape = m_rectScreen;
		rectShape.Normalize();

		if (!rectShape.PtInRect(m_ptAnchor))
		{
			CBCGPPoint pt;

			if (bcg_CS_intersect(rectShape, m_ptAnchor, rectShape.CenterPoint(), pt))
			{
				pGM->DrawLine(m_ptAnchor, pt, m_format.m_outlineFormat.m_brLineColor, 
					m_format.m_outlineFormat.GetLineWidth(TRUE), &m_format.m_outlineFormat.m_strokeStyle);
			}
			
			CBCGPEllipse ellipse(m_ptAnchor, 2 * m_format.GetScaleRatio().cx, 2 * m_format.GetScaleRatio().cy);

			pGM->FillEllipse(ellipse, m_format.m_brFillColor);
			pGM->DrawEllipse(ellipse, m_format.m_outlineFormat.m_brLineColor);
		}
	}

	CBCGPChartObject::OnDrawShape(pGM, rectDiagram);
}
Beispiel #4
0
//********************************************************************************
void CBCGPTreeMapGroup::OnDraw(CBCGPGraphicsManager* pGM, const CBCGPRect& rectClip, const CBCGPBrush& brBorder)
{
	ASSERT_VALID(pGM);

	if (m_rect.IsRectEmpty())
	{
		return;
	}

	CBCGPRect rectInter;
	if (!rectInter.IntersectRect(m_rect, rectClip))
	{
		return;
	}

	if (!m_rectCaption.IsRectEmpty())
	{
		pGM->FillRectangle(m_rectCaption, m_brFillCaption);
		DrawTextWidthShadow(pGM, m_strLabel, m_rectCaption, m_brText, m_tf);
	}

	int nSubNodes = (int)m_arSubNodes.GetSize();
	for (int i = 0; i < nSubNodes; i++)
	{
		m_arSubNodes[i]->OnDraw(pGM, rectClip, brBorder);
	}
}
//*******************************************************************************
void CBCGPTextGaugeImpl::OnDraw(CBCGPGraphicsManager* pGM, const CBCGPRect& /*rectClip*/, DWORD dwFlags)
{
	ASSERT_VALID(pGM);

	if (m_rect.IsRectEmpty() || !m_bIsVisible || m_strText.IsEmpty())
	{
		return;
	}

	if ((dwFlags & m_DefaultDrawFlags) == 0)
	{
		return;
	}

	CBCGPRect rect = m_rect;
	if (m_bIsSubGauge)
	{
		rect.OffsetRect(-m_ptScrollOffset);
	}

	pGM->FillRectangle(rect, GetFillBrush ());

	const CBCGPBrush& br = m_bOff ? m_brTextLight : m_brText;

	CreateResources(CBCGPBrush(), FALSE);
	pGM->DrawText(m_strText, rect, m_textFormat, br);

	pGM->DrawRectangle(rect, GetOutlineBrush (), GetScaleRatioMid());
	
	SetDirty(FALSE);
}
Beispiel #6
0
bool bcg_pointInPie (const CBCGPRect& rect, double dblAngleStart, double dblAngleFinish, const CBCGPPoint& ptTestIn, double dblDoughnutPercent)
{
    if (!rect.PtInRect(ptTestIn))
    {
        return false;
    }

	CBCGPPoint ptTest = ptTestIn;
	CBCGPPoint ptCenter = rect.CenterPoint();
	double dblRadiusX = 0.5 * rect.Width();
	double dblRadiusY = 0.5 * rect.Height();

	if (dblRadiusX > dblRadiusY && dblRadiusX != 0.)
	{
		ptTest.Scale(CBCGPPoint(dblRadiusY / dblRadiusX, 1.0), ptCenter);
	}
	else if (dblRadiusY > dblRadiusX && dblRadiusY != 0.)
	{
		ptTest.Scale(CBCGPPoint(1.0, dblRadiusX / dblRadiusY, 1.0), ptCenter);
	}
	
	double dblAngle = bcg_normalize_rad(bcg_angle(ptCenter, ptTest));

	dblAngleStart = bcg_normalize_rad(dblAngleStart);
	dblAngleFinish = bcg_normalize_rad(dblAngleFinish);

	BOOL bIn = FALSE;
	const BOOL bIsFullEllipse = bcg_IsFullEllipse(bcg_rad2deg(dblAngleStart), bcg_rad2deg(dblAngleFinish), TRUE, 0.1f);

	if (bIsFullEllipse)
	{
		bIn = TRUE;
	}
	else
	{
		if (dblAngleStart > dblAngleFinish)
		{
			bIn = (dblAngle <= dblAngleFinish) || (dblAngleStart <= dblAngle);
		}
		else
		{
			bIn = (dblAngle >= dblAngleStart) && (dblAngle <= dblAngleFinish);
		}
	}

	if (bIn)
	{
		double angleCos = cos(dblAngle);
		double angleSin = sin(dblAngle);

		CBCGPPoint ptEdge(ptCenter.x + angleCos * .5 * rect.Width(), ptCenter.y + angleSin * .5 * rect.Height());
		double r = bcg_distance(ptEdge, ptCenter);
		double distToCenter = bcg_distance(ptTestIn, ptCenter);

		return (distToCenter <= r && distToCenter > r * dblDoughnutPercent);
	}

	return false;
}
Beispiel #7
0
int bcg_CS_clip_inv(const CBCGPRect& rect, CBCGPPoint& pt1_1, CBCGPPoint& pt1_2,
					   CBCGPPoint* pt2_1, CBCGPPoint* pt2_2)
{
	if (rect.IsRectEmpty ())
	{
		return 1;
	}

	BOOL bInRect1 = rect.PtInRect (pt1_1);
	BOOL bInRect2 = rect.PtInRect (pt1_2);
	if (bInRect1 && bInRect2)
	{
		return 0;
	}

	if (pt1_1 == pt1_2)
	{
		return bInRect1 ? 0 : 1;
	}

	CBCGPPoint pt1(pt1_1);
	CBCGPPoint pt2(pt1_2);

	if (!bcg_CS_clip(rect, pt1, pt2))
	{
		return 1;
	}

	int count = 0;

 	if (bInRect1)
 	{
		pt1_1 = pt1;
		count = 1;
	}
	else if (bInRect2)
	{
		pt1_2 = pt2;
		count = 1;
	}
	else
	{
		count = 1;

		if (pt2_1 != NULL && pt2_2 != NULL)
		{
 			*pt2_1 = pt1;
 			*pt2_2 = pt1_2;

			count = 2;
		}

		pt1_2 = pt2;
	}

	return count;
}
Beispiel #8
0
//****************************************************************************
void CBCGPTreeMapNode::OnDraw(CBCGPGraphicsManager* pGM, const CBCGPRect& rectClip, const CBCGPBrush& brBorder)
{
	ASSERT_VALID(pGM);

	if (m_rect.IsRectEmpty())
	{
		return;
	}

	CBCGPRect rectInter;
	if (!rectInter.IntersectRect(m_rect, rectClip))
	{
		return;
	}

	pGM->FillRectangle(m_rect, m_brFill);

	if (!m_strLabel.IsEmpty())
	{
		CBCGPTreeMapGroup* pGroup = DYNAMIC_DOWNCAST(CBCGPTreeMapGroup, m_pParent);
		if (pGroup != NULL)
		{
			ASSERT_VALID(pGroup);
			DrawTextWidthShadow(pGM, m_strLabel, m_rect, pGroup->m_brText, pGroup->m_tf);
		}
	}

	if (m_pParent != NULL)
	{
		ASSERT_VALID(m_pParent);

		if (m_pParent->GetRect().top != m_rect.top)
		{
			pGM->DrawLine(m_rect.left, m_rect.top, m_rect.right, m_rect.top, brBorder);
		}

		if (m_pParent->GetRect().bottom != m_rect.bottom)
		{
			pGM->DrawLine(m_rect.left, m_rect.bottom + 1, m_rect.right, m_rect.bottom + 1, brBorder);
		}

		if (m_pParent->GetRect().left != m_rect.left)
		{
			pGM->DrawLine(m_rect.left, m_rect.top, m_rect.left, m_rect.bottom, brBorder);
		}

		if (m_pParent->GetRect().right != m_rect.right)
		{
			pGM->DrawLine(m_rect.right + 1, m_rect.top, m_rect.right + 1, m_rect.bottom, brBorder);
		}
	}
}
//*******************************************************************************
BOOL CBCGPLinearGaugeImpl::OnSetMouseCursor(const CBCGPPoint& pt)
{
	if (m_bIsInteractiveMode)
	{
		CBCGPRect rect = m_rect;
		rect.DeflateRect(m_nFrameSize, m_nFrameSize);

		if (rect.PtInRect(pt))
		{
			::SetCursor (globalData.GetHandCursor());
			return TRUE;
		}
	}

	return CBCGPGaugeImpl::OnSetMouseCursor(pt);
}
Beispiel #10
0
//********************************************************************************
void CBCGPTreeMapGroup::RecalcSliceLayout(CBCGPGraphicsManager* pGM, int nStart, int nFinish, const CBCGPRect& rect, BOOL bIsVertical)
{
	ASSERT(nStart >= 0);
	ASSERT(nStart < (int)m_arSubNodes.GetSize());
	ASSERT(nFinish >= 0);
	ASSERT(nFinish < (int)m_arSubNodes.GetSize());

	double dblTotal = GetChildrenTotal(nStart, nFinish);

	double x = rect.left;
	double y = rect.top;

	for (int i = nStart; i <= nFinish; i++)
	{
		CBCGPBaseTreeMapNode* pNode = m_arSubNodes[i];
		if (pNode->GetValue() <= 0.0)
		{
			pNode->SetRect(CBCGPRect());
			continue;
		}

		CBCGPRect rectSubNode = rect;

		if (bIsVertical)
		{
			double cx = rect.Width() / dblTotal * pNode->GetValue();

			rectSubNode.left = x;
			rectSubNode.right = x + cx;

			x += cx;
		}
		else
		{
			double cy = rect.Height() / dblTotal * pNode->GetValue();

			rectSubNode.top = y;
			rectSubNode.bottom = y + cy;

			y += cy;
		}

		m_arSubNodes[i]->SetRect(rectSubNode);
		m_arSubNodes[i]->RecalcLayout(pGM);
	}
}
Beispiel #11
0
void CBCGPBaseTreeMapNode::DrawTextWidthShadow(CBCGPGraphicsManager* pGM, const CString& str, const CBCGPRect& rect, const CBCGPBrush& br, const CBCGPTextFormat& tf)
{
	ASSERT_VALID(pGM);

	CBCGPSize sizeText = pGM->GetTextSize(str, tf);

	if (sizeText.cx > rect.Width() || sizeText.cy > rect.Height())
	{
		return;
	}

	CBCGPRect rectShadow = rect;
	rectShadow.OffsetRect(1, 1);

	pGM->DrawText(str, rectShadow, tf, CBCGPBrush(CBCGPColor::Black));
	pGM->DrawText(str, rect, tf, br);
}
//*******************************************************************************
void CBCGPChartObject::OnDrawText(CBCGPGraphicsManager* pGM, const CBCGPRect& /*rectDiagram*/)
{
	ASSERT_VALID(this);

	if (m_strText.IsEmpty() || m_rectScreen.IsRectEmpty())
	{
		return;
	}

	CBCGPRect rectShape = m_rectScreen;
	rectShape.DeflateRect(m_format.GetContentPadding(TRUE));

	const CBCGPBrush& brText = m_format.m_brTextColor.IsEmpty() ? 
		m_pParentChart->GetColors().m_brChartObjectTextColor : m_format.m_brTextColor;

	pGM->DrawText(m_strText, rectShape, m_format.m_textFormat, brText);
}
Beispiel #13
0
//********************************************************************************
void CBCGPTreeMapGroup::RecalcSquarifiedLayout(CBCGPGraphicsManager* pGM, int nStart, int nFinish, const CBCGPRect& rect)
{
	if (nFinish - nStart < 2)
	{
		RecalcSliceLayout(pGM, nStart, nFinish, rect, rect.Width() > rect.Height());
		return;
	}

	ASSERT(nStart >= 0);
	ASSERT(nStart < (int)m_arSubNodes.GetSize());
	ASSERT(nFinish >= 0);
	ASSERT(nFinish < (int)m_arSubNodes.GetSize());

	int i = 0;
	double dblTotal = GetChildrenTotal(nStart, nFinish);

	double dblTotalLeft = 0.;

	for (i = nStart; i <= nFinish; i++)
	{
		dblTotalLeft += max(0.0, m_arSubNodes[i]->GetValue());
		
		if (dblTotalLeft >= 0.5 * dblTotal)
		{
			if (rect.Width() > rect.Height())
			{
				CBCGPRect rectLeft = rect;
				rectLeft.right = rectLeft.left + rectLeft.Width() * dblTotalLeft / dblTotal;

				RecalcSquarifiedLayout(pGM, nStart, i, rectLeft);

				CBCGPRect rectRight = rect;
				rectRight.left = rectLeft.right + 1;

				RecalcSquarifiedLayout(pGM, i + 1, nFinish, rectRight);
			}
			else
			{
				CBCGPRect rectTop = rect;
				rectTop.bottom = rectTop.top + rectTop.Height() * dblTotalLeft / dblTotal;

				RecalcSquarifiedLayout(pGM, nStart, i, rectTop);

				CBCGPRect rectBottom = rect;
				rectBottom.top = rectTop.bottom + 1;

				RecalcSquarifiedLayout(pGM, i + 1, nFinish, rectBottom);
			}

			return;
		}
	}

	ASSERT(FALSE);
}
//*******************************************************************************
BOOL CBCGPLinearGaugeImpl::ValueToPoint(double dblValue, CBCGPPoint& point, int nScale) const
{
	CBCGPGaugeScaleObject* pScale = GetScale(nScale);
	if (pScale == NULL)
	{
		ASSERT(FALSE);
		return FALSE;
	}

	double dblStart = min(pScale->m_dblStart, pScale->m_dblFinish);
	double dblFinish = max(pScale->m_dblStart, pScale->m_dblFinish);

	if (dblValue < dblStart || dblValue > dblFinish)
	{
		return FALSE;
	}

	if (pScale->m_dblFinish == pScale->m_dblStart)
	{
		return FALSE;
	}

	CBCGPRect rect = m_rect;

	if (m_bIsVertical)
	{
		rect.DeflateRect((pScale->m_dblOffsetFromFrame + m_nFrameSize) * m_sizeScaleRatio.cx, m_sizeMaxLabel.cy / 2 + (m_nFrameSize + 1) * m_sizeScaleRatio.cy);
	}
	else
	{
		rect.DeflateRect(m_sizeMaxLabel.cx / 2 + (m_nFrameSize + 1) * m_sizeScaleRatio.cx, (pScale->m_dblOffsetFromFrame + m_nFrameSize) * m_sizeScaleRatio.cy);
	}

	const double dblTotalSize = m_bIsVertical ? rect.Height() : rect.Width();
	const double delta = dblTotalSize * fabs(dblValue - pScale->m_dblStart) / fabs(pScale->m_dblFinish - pScale->m_dblStart);

	point.x = m_bIsVertical ? rect.left : rect.left + delta;
	point.y = m_bIsVertical ? rect.bottom - delta : rect.top;

	return TRUE;
}
//*******************************************************************************
BOOL CBCGPChartLineObject::HitTest(const CBCGPPoint& pt) const
{
	double dblWidth = m_format.m_outlineFormat.GetLineWidth(TRUE) / 2;

	CBCGPPoint ptTopLeft = m_rectScreen.TopLeft();
	CBCGPPoint ptBottomRight = m_rectScreen.BottomRight();

	if (ptTopLeft.x == ptBottomRight.x)
	{
		CBCGPRect r = m_rectScreen.NormalizedRect();
		r.InflateRect(dblWidth, dblWidth);
		return r.PtInRect(pt);
	}
	
	double dblACoef = (ptBottomRight.y - ptTopLeft.y) / (ptBottomRight.x - ptTopLeft.x);
	double dblBCoef = ptBottomRight.y - dblACoef * ptBottomRight.x;
	
	double dblY = dblACoef * pt.x + dblBCoef;

	return pt.y > dblY - dblWidth && pt.y < dblY + dblWidth;
}
Beispiel #16
0
//********************************************************************************
void CBCGPTreeMapGroup::SetRect(const CBCGPRect& rectIn)
{
	CBCGPRect rect = rectIn;

	if (m_sizeMargin != CBCGPSize(-1., -1.))
	{
		rect.DeflateRect(m_sizeMargin);
	}
	else
	{
		for (CBCGPBaseTreeMapNode* pParent = m_pParent; pParent != NULL; pParent = pParent->m_pParent)
		{
			ASSERT_VALID(pParent);

			if (pParent->m_sizeMargin != CBCGPSize(-1., -1.))
			{
				rect.DeflateRect(pParent->m_sizeMargin);
				break;
			}
		}
	}

	m_rect = rect;
}
Beispiel #17
0
//*****************************************************************************************
void CBCGPTreeMap::OnDraw(CBCGPGraphicsManager* pGM, const CBCGPRect& rectClip, DWORD dwFlags)
{
	if (IsDirty())
	{
		m_Root.RecalcLayout(pGM);
		SetDirty(FALSE);
	}

	if ((dwFlags & BCGP_DRAW_STATIC) == 0)
	{
		return;
	}

	if (m_Root.m_arSubNodes.GetSize() > 0)
	{
		pGM->FillRectangle(m_rect, m_brFill);
		m_Root.OnDraw(pGM, rectClip.IsRectEmpty() ? m_rect : rectClip, m_brFill);
	}
}
//*******************************************************************************
BOOL CBCGPLinearGaugeImpl::HitTestValue(const CBCGPPoint& pt, double& dblValue, int nScale, BOOL bInsideGauge) const
{
	CBCGPGaugeScaleObject* pScale = GetScale(nScale);
	if (pScale == NULL)
	{
		ASSERT(FALSE);
		return FALSE;
	}

	CBCGPRect rect = m_rect;
	rect.OffsetRect(-m_ptScrollOffset);

	if (m_bIsVertical)
	{
		rect.DeflateRect((pScale->m_dblOffsetFromFrame + m_nFrameSize) * m_sizeScaleRatio.cx, m_sizeMaxLabel.cy / 2 + (m_nFrameSize + 1) * m_sizeScaleRatio.cy);
	}
	else
	{
		rect.DeflateRect(m_sizeMaxLabel.cx / 2 + (m_nFrameSize + 1) * m_sizeScaleRatio.cx, (pScale->m_dblOffsetFromFrame + m_nFrameSize) * m_sizeScaleRatio.cy);
	}

	if (bInsideGauge && !rect.PtInRect(pt))
	{
		return FALSE;
	}

	if (m_bIsVertical)
	{
		const double dblTotalSize = rect.Height();
		dblValue = pScale->m_dblStart + (rect.bottom - pt.y) * fabs(pScale->m_dblFinish - pScale->m_dblStart) / dblTotalSize;
	}
	else
	{
		const double dblTotalSize = rect.Width();
		dblValue = pScale->m_dblStart + (pt.x - rect.left) * fabs(pScale->m_dblFinish - pScale->m_dblStart) / dblTotalSize;
	}

	return TRUE;
}
//*******************************************************************************
CBCGPRect CBCGPChartObject::OnCalcBoundingRect()
{
	if (m_pXAxis == NULL && m_pYAxis == NULL || m_coordinateMode == CM_AXIS_OUTSIDE_MARK || m_coordinateMode == CM_AXIS_INSIDE_MARK ||
		m_pXAxis != NULL && !m_pXAxis->m_bInitialized)
	{
		return m_pParentChart->GetRect();
	}

	CBCGPRect rectXAxis;
	CBCGPRect rectYAxis;

	if (m_pXAxis != NULL)
	{
		rectXAxis = m_pXAxis->GetBoundingRect();
	}

	if (m_pYAxis != NULL)
	{
		rectYAxis = m_pYAxis->GetBoundingRect();
	}

	if (!rectXAxis.IsRectEmpty() && !rectYAxis.IsRectEmpty())
	{
		rectXAxis.IntersectRect(rectYAxis);
		if (rectXAxis.IsRectEmpty())
		{
			return rectYAxis;
		}
	}
	else if (rectXAxis.IsRectEmpty())
	{
		return rectYAxis;
	}

	return rectXAxis;
}
Beispiel #20
0
//***********************************************************************************************************
void CBCGPBaseVisualCtrl::DoPaint(CDC* pDC, CRect rectClip)
{
	CRect rectClient;
	GetClientRect(rectClient);

	m_bInPaint = TRUE;

	pDC->FillRect(rectClient, &globalData.brWindow);

	globalData.DrawParentBackground (this, pDC);

	CBCGPRect rect = GetRect();

	if (m_pGM == NULL)
	{
		m_pGM = CBCGPGraphicsManager::CreateInstance();
	}

	if (m_pGM == NULL)
	{
		m_bInPaint = FALSE;
		return;
	}

	if (rect.IsRectEmpty())
	{
		CRect rectClient;
		GetClientRect(rectClient);

		SetRect(rectClient);
		rect = GetRect();
	}

	m_pGM->BindDC(pDC, rect);

	if (m_dblScale != 0.0 && m_pGM->IsSupported(BCGP_GRAPHICS_MANAGER_SCALING))
	{
		m_pGM->SetScale(m_dblScale);
	}

	if (!m_pGM->BeginDraw())
	{
		return;
	}

	BOOL bSetClipRect = FALSE;

	if (rectClip.IsRectEmpty())
	{
		rectClip = rect;
	}
	else
	{
		bSetClipRect = TRUE;
		m_pGM->SetClipRect(rectClip);
	}

	OnDraw(m_pGM, rectClip);

	if (bSetClipRect)
	{
		m_pGM->ReleaseClipArea();
	}

	SetDirty(FALSE);

	m_pGM->EndDraw();

	m_bInPaint = FALSE;
}
//*******************************************************************************
void CBCGPLinearGaugeImpl::OnDraw(CBCGPGraphicsManager* pGMSrc, const CBCGPRect& /*rectClip*/, DWORD dwFlags/* = BCGP_DRAW_STATIC | BCGP_DRAW_DYNAMIC*/)
{
	ASSERT_VALID(this);
	ASSERT_VALID(pGMSrc);

	if (m_rect.IsRectEmpty() || !m_bIsVisible)
	{
		return;
	}

	BOOL bCacheImage = m_bCacheImage;

	if (pGMSrc->IsOffscreen())
	{
		bCacheImage = FALSE;
	}

	CBCGPGraphicsManager* pGM = pGMSrc;
	CBCGPGraphicsManager* pGMMem = NULL;

	CBCGPRect rectSaved;

	if (bCacheImage)
	{	
		if (m_ImageCache.GetHandle() == NULL)
		{
			SetDirty();
		}

		if (m_ImageCache.GetHandle() != NULL && !IsDirty() && (dwFlags & BCGP_DRAW_STATIC))
		{
			pGMSrc->DrawImage(m_ImageCache, m_rect.TopLeft());
			dwFlags &= ~BCGP_DRAW_STATIC;

			if (dwFlags == 0)
			{
				return;
			}
		}

		if (dwFlags & BCGP_DRAW_STATIC)
		{
			pGMMem = pGM->CreateOffScreenManager(m_rect, &m_ImageCache);

			if (pGMMem != NULL)
			{
				pGM = pGMMem;

				rectSaved = m_rect;
				m_rect = m_rect - m_rect.TopLeft();
			}
		}
	}

	if (IsDirty())
	{
		int i = 0;

		for (i = 0; i < m_arScales.GetSize(); i++)
		{
			CBCGPGaugeScaleObject* pScale = m_arScales[i];
			ASSERT_VALID(pScale);

			pScale->CleanUp();
		}

		SetDirty(FALSE);
	}

	CBCGPRect rect = m_rect;
	rect.DeflateRect(1., 1.);

	const double scaleRatio = GetScaleRatioMid();
	const double nFrameSize = m_nFrameSize * scaleRatio;

	if (dwFlags & BCGP_DRAW_STATIC)
	{
		const CBCGPBrush& brFill = m_nFrameSize <= 2 ? m_Colors.m_brFill : m_Colors.m_brFrameFill;

		pGM->FillRectangle(rect, brFill);
		pGM->DrawRectangle(rect, m_Colors.m_brFrameOutline, scaleRatio);

		if (scaleRatio == 1.0 && !pGM->IsSupported(BCGP_GRAPHICS_MANAGER_ANTIALIAS))
		{
			CBCGPRect rect1 = rect;
			rect1.DeflateRect(1, 1);

			pGM->DrawRectangle(rect1, m_Colors.m_brFrameOutline);
		}

		if (m_nFrameSize > 2)
		{
			CBCGPRect rectInternal = rect;
			rectInternal.DeflateRect(nFrameSize, nFrameSize);

			pGM->FillRectangle(rectInternal, m_Colors.m_brFill);
			pGM->DrawRectangle(rectInternal, m_Colors.m_brFrameOutline, scaleRatio);
		}

		m_sizeMaxLabel = GetTextLabelMaxSize(pGM);

		int i = 0;

		m_dblMaxRangeSize = 0.;

		// Draw colored ranges:
		for (i = 0; i < m_arRanges.GetSize(); i++)
		{
			CBCGPGaugeColoredRangeObject* pRange = m_arRanges[i];
			ASSERT_VALID(pRange);

			CBCGPRect rectRange;
			CBCGPPolygonGeometry shapeRange;

			if (GetRangeShape(rectRange, shapeRange, 
				pRange->m_dblStartValue, pRange->m_dblFinishValue, 
				pRange->m_dblStartWidth, pRange->m_dblFinishWidth,
				pRange->m_dblOffsetFromFrame, pRange->m_nScale))
			{
				if (!rectRange.IsRectEmpty())
				{
					pGM->FillRectangle(rectRange, pRange->m_brFill);
					pGM->DrawRectangle(rectRange, pRange->m_brOutline, scaleRatio);
				}
				else
				{
					pGM->FillGeometry(shapeRange, pRange->m_brFill);
					pGM->DrawGeometry(shapeRange, pRange->m_brOutline, scaleRatio);
				}

				m_dblMaxRangeSize = max(m_dblMaxRangeSize, max(pRange->m_dblStartWidth, pRange->m_dblFinishWidth) * scaleRatio);
			}
		}

		// Draw scales:
		for (i = 0; i < m_arScales.GetSize(); i++)
		{
			OnDrawScale(pGM, i);
		}
	}

	if (pGMMem != NULL)
	{
		delete pGMMem;
		pGM = pGMSrc;

		m_rect = rectSaved;

		pGMSrc->DrawImage(m_ImageCache, m_rect.TopLeft());
	}

	if (dwFlags & BCGP_DRAW_DYNAMIC)
	{
		CBCGPRect rectSaved = m_rect;
		m_rect.OffsetRect(-m_ptScrollOffset);

		int i = 0;

		for (i = 0; i < m_arData.GetSize(); i++)
		{
			CBCGPPointsArray pts;
			CBCGPPointsArray ptsShadow;

			CreatePointerPoints(pts, i, FALSE);

			if (pGM->IsSupported(BCGP_GRAPHICS_MANAGER_COLOR_OPACITY))
			{
				CreatePointerPoints(ptsShadow, i, TRUE);
			}

			OnDrawPointer(pGM, pts, ptsShadow, i);
		}

		m_rect = rectSaved;
	}
}
//***************************************************************************************
int CBCGPRadialMenuObject::HitTestShape(const CBCGPPoint& pt)
{
	CBCGPRect rect = m_rect;
	if (rect.Width() < rect.Height())
	{
		rect.top += (rect.Height() - rect.Width()) / 2;
		rect.bottom = rect.top + rect.Width();
	}
	else if (rect.Height() < rect.Width())
	{
		rect.left += (rect.Width() - rect.Height()) / 2;
		rect.right = rect.left + rect.Height();
	}

	rect.DeflateRect(2., 2.);

	rect.right -= m_nShadowDepth;
	rect.bottom -= m_nShadowDepth;

	const double radius = rect.Width() / 2;
	const double radiusSmall = INTERNAL_PART * rect.Width() + 1.0;
	const CBCGPPoint center = rect.CenterPoint();

	double dblDistanceToCenter = bcg_distance(pt, center);

	if (dblDistanceToCenter > radius)
	{
		return -1;
	}
	
	const int nItems = (int)m_arItems.GetSize();
	if (dblDistanceToCenter <= radiusSmall)
	{
		return m_bHasCenterButton ? nItems - 1 : -1;
	}

	const int nCircleItems = m_bHasCenterButton ? nItems - 1 : nItems;
	if (nCircleItems == 0)
	{
		return -1;
	}

	double dblDeltaAngle = 360. / nCircleItems;
	double dblStartAngle = 90. - dblDeltaAngle / 2;
	double dblAngle = bcg_normalize_deg(bcg_rad2deg(acos((pt.x - center.x) / dblDistanceToCenter)));
	if (pt.y > center.y)
	{
		dblAngle = 360 - dblAngle;
	}

	int nHit = (int)((dblAngle - dblStartAngle) / dblDeltaAngle);
	if (dblAngle < dblStartAngle)
	{
		nHit = nCircleItems - 1 - (int)((dblStartAngle - dblAngle) / dblDeltaAngle);
	}

	return nHit;
}
//*******************************************************************************
void CBCGPLinearGaugeImpl::OnDrawScale(CBCGPGraphicsManager* pGM, int nScale)
{
	CBCGPGaugeScaleObject* pScale = GetScale(nScale);
	if (pScale == NULL)
	{
		ASSERT(FALSE);
		return;
	}

	const double scaleRatio = GetScaleRatioMid();

	const CBCGPBrush& brFill = pScale->m_brFill.IsEmpty() ? m_Colors.m_brScaleFill : pScale->m_brFill;
	const CBCGPBrush& brOutline = pScale->m_brOutline.IsEmpty() ? m_Colors.m_brScaleOutline : pScale->m_brOutline;

	if (!brFill.IsEmpty() || !brOutline.IsEmpty())
	{
		CBCGPRect rectRange;
		CBCGPPolygonGeometry shapeRange;

		if (GetRangeShape(rectRange, shapeRange, 
			pScale->m_dblStart, pScale->m_dblFinish, 
			pScale->m_dblMajorTickMarkSize, pScale->m_dblMajorTickMarkSize,
			0, nScale))
		{
			if (!rectRange.IsRectEmpty())
			{
				pGM->FillRectangle(rectRange, brFill);
				pGM->DrawRectangle(rectRange, brOutline, scaleRatio);
			}
			else
			{
				pGM->FillGeometry(shapeRange, brFill);
				pGM->DrawGeometry(shapeRange, brOutline, scaleRatio);
			}
		}
	}

	int i = 0;
	double dblStep = (pScale->m_dblFinish > pScale->m_dblStart) ? pScale->m_dblStep : -pScale->m_dblStep;

	double dblMinorTickSize = pScale->m_dblMinorTickMarkSize * scaleRatio;
	double dblMajorTickSize = pScale->m_dblMajorTickMarkSize * scaleRatio;
	CBCGPGaugeScaleObject::BCGP_TICKMARK_POSITION position = pScale->m_MinorTickMarkPosition;
	
	for (double dblVal = pScale->m_dblStart; 
		(dblStep > 0. && dblVal <= pScale->m_dblFinish) || (dblStep < 0. && dblVal >= pScale->m_dblFinish); 
		dblVal += dblStep, i++)
	{
		const BOOL bIsLastTick = (pScale->m_dblFinish > pScale->m_dblStart && dblVal + dblStep > pScale->m_dblFinish);
		BOOL bIsMajorTick = pScale->m_dblMajorTickMarkStep != 0. && ((i % bcg_round(pScale->m_dblMajorTickMarkStep)) == 0);

		if (!bIsMajorTick && (i == 0 || bIsLastTick))
		{
			// Always draw first and last ticks
			bIsMajorTick = TRUE;
		}

		double dblCurrTickSize = bIsMajorTick ? dblMajorTickSize : dblMinorTickSize;

		CBCGPPoint ptFrom;
		ValueToPoint(dblVal, ptFrom, nScale);

		if (!bIsMajorTick && position != CBCGPGaugeScaleObject::BCGP_TICKMARK_POSITION_NEAR && 
			dblCurrTickSize < dblMajorTickSize)
		{
			double dblDeltaTick = dblMajorTickSize - dblCurrTickSize;

			if (position == CBCGPGaugeScaleObject::BCGP_TICKMARK_POSITION_CENTER)
			{
				dblDeltaTick /= 2.0;
			}

			if (m_bIsVertical)
			{
				ptFrom.x += dblDeltaTick;
			}
			else
			{
				ptFrom.y += dblDeltaTick;
			}
		}

		CBCGPPoint ptTo = ptFrom;

		if (m_bIsVertical)
		{
			ptTo.x += dblCurrTickSize;
		}
		else
		{
			ptTo.y += dblCurrTickSize;
		}

		if (dblCurrTickSize > 0.)
		{
			OnDrawTickMark(pGM, ptFrom, ptTo, 
				bIsMajorTick ? pScale->m_MajorTickMarkStyle : pScale->m_MinorTickMarkStyle,
				bIsMajorTick, dblVal, nScale,
				bIsMajorTick ? pScale->m_brTickMarkMajor : pScale->m_brTickMarkMinor,
				bIsMajorTick ? pScale->m_brTickMarkMajorOutline : pScale->m_brTickMarkMinorOutline);
		}

		if (bIsMajorTick)
		{
			CString strLabel;
			GetTickMarkLabel(strLabel, pScale->m_strLabelFormat, dblVal, nScale);

			if (!strLabel.IsEmpty())
			{
				double offset = 0;

				if (pScale->m_dblMajorTickMarkSize == 0.)
				{
					offset = m_dblMaxRangeSize;
				}

				CBCGPRect rectText;

				double xLabel = ptTo.x;
				double yLabel = ptTo.y;

				CBCGPSize sizeText = GetTickMarkTextLabelSize(pGM, strLabel, m_textFormat);

				if (m_bIsVertical)
				{
					rectText.left = xLabel + 2 + offset;
					rectText.top = yLabel - sizeText.cy / 2;
				}
				else
				{
					rectText.left = xLabel - sizeText.cx / 2;
					rectText.top = yLabel + offset;
				}

				rectText.right = rectText.left + sizeText.cx;
				rectText.bottom = rectText.top + sizeText.cy;

				OnDrawTickMarkTextLabel(pGM, m_textFormat, rectText, strLabel, dblVal, nScale, 
					pScale->m_brText.IsEmpty() ? m_Colors.m_brText : pScale->m_brText);
			}
		}
	}
}
//***************************************************************************************
void CBCGPRadialMenuObject::OnDraw(CBCGPGraphicsManager* pGM, const CBCGPRect& /*rectClip*/, DWORD dwFlags)
{
	if (dwFlags == BCGP_DRAW_STATIC)
	{
		return;
	}

	m_nShadowDepth = pGM->IsSupported(BCGP_GRAPHICS_MANAGER_COLOR_OPACITY) ? GetShadowDepth() : 0;

	CBCGPRect rect = m_rect;
	if (rect.Width() < rect.Height())
	{
		rect.top += (rect.Height() - rect.Width()) / 2;
		rect.bottom = rect.top + rect.Width();
	}
	else if (rect.Height() < rect.Width())
	{
		rect.left += (rect.Width() - rect.Height()) / 2;
		rect.right = rect.left + rect.Height();
	}

	rect.DeflateRect(2., 2.);

	rect.right -= m_nShadowDepth;
	rect.bottom -= m_nShadowDepth;

	const double radius = rect.Width() / 2;
	const double radiusSmall = INTERNAL_PART * rect.Width() + 1.0;
	const CBCGPPoint center = rect.CenterPoint();

	CBCGPSize sizeIcon((double)m_cxIcon, 16);

	if (!m_Icons.IsNull())
	{
		sizeIcon.cy = pGM->GetImageSize(m_Icons).cy;
	}

	const int nItems = (int)m_arItems.GetSize();

	if (IsDirty())
	{
		int nCircleItems = m_bHasCenterButton ? nItems - 1 : nItems;

		double dblDeltaAngle = nCircleItems == 0 ? 0. : 360. / nCircleItems;
		double dblStartAngle = 90. - dblDeltaAngle / 2;

		for (int i = 0; i < nItems; i++)
		{
			CBCGPRadialMenuItem* pItem = m_arItems[i];
			ASSERT_VALID(pItem);

			pItem->m_bIsCenter = i == nItems -1 && m_bHasCenterButton;

			pItem->m_Shape.Destroy();
			pItem->m_Shape.Clear();

			if (!pItem->m_bIsCenter)
			{
				double dblFinishAngle = dblStartAngle + dblDeltaAngle;

				const double dblStartAngleRad = bcg_deg2rad(dblStartAngle);
				const double dblFinishAngleRad = bcg_deg2rad(dblFinishAngle);
				const double dblMiddleAngleRad = bcg_deg2rad(dblStartAngle + dblDeltaAngle / 2);

				double angleStartCos = cos(dblStartAngleRad);
				double angleStartSin = sin(dblStartAngleRad);
				double angleFinishCos = cos(dblFinishAngleRad);
				double angleFinishSin = sin(dblFinishAngleRad);

				pItem->m_Shape.SetStart(
					CBCGPPoint(center.x + angleStartCos * radius, center.y - angleStartSin * radius));
				pItem->m_Shape.AddArc(
					CBCGPPoint(center.x + angleFinishCos * radius, center.y - angleFinishSin * radius),
					CBCGPSize(radius, radius), dblStartAngle > dblFinishAngle, FALSE);
				pItem->m_Shape.AddLine(
					CBCGPPoint(center.x + angleFinishCos * radiusSmall, center.y - angleFinishSin * radiusSmall));
				pItem->m_Shape.AddArc(
					CBCGPPoint(center.x + angleStartCos * radiusSmall, center.y - angleStartSin * radiusSmall),
					CBCGPSize(radiusSmall, radiusSmall), dblStartAngle < dblFinishAngle, FALSE);

				pItem->m_ptCenter = CBCGPPoint(
					center.x + cos(dblMiddleAngleRad) * 2 * radius / 3,
					center.y - sin(dblMiddleAngleRad) * 2 * radius / 3);

				dblStartAngle = dblFinishAngle;
			}
			else
			{
				pItem->m_Shape.SetStart(center);
				pItem->m_Shape.AddLine(center);
				pGM->CombineGeometry(pItem->m_Shape, pItem->m_Shape, CBCGPEllipseGeometry(CBCGPEllipse(center, radiusSmall, radiusSmall)), RGN_OR);

				pItem->m_ptCenter = center;
			}
		}
	}

	CBCGPEllipse ellipseInt(center, radiusSmall, radiusSmall);

	CBCGPRect rectShadow = rect;
	rectShadow.OffsetRect(m_nShadowDepth, m_nShadowDepth);

	if (!m_bHasCenterButton && m_pCtrl->GetSafeHwnd() != NULL && (m_pCtrl->GetExStyle() & WS_EX_LAYERED))
	{
		if (m_nShadowDepth > 0)
		{
			CBCGPEllipseGeometry egShadow(rectShadow);

			CBCGPPoint centerShadow = center;
			centerShadow.x += m_nShadowDepth;
			centerShadow.y += m_nShadowDepth;

			CBCGPEllipse ellipseIntShadow(centerShadow, radiusSmall, radiusSmall);
			CBCGPEllipseGeometry egInternalShadow(ellipseIntShadow);

			CBCGPComplexGeometry shapeShadow;
			pGM->CombineGeometry(shapeShadow, egShadow, egInternalShadow, RGN_DIFF);

			pGM->FillGeometry(shapeShadow, m_brShadow);
		}

		CBCGPEllipseGeometry eg(rect);
		CBCGPEllipseGeometry egInternal(ellipseInt);

		CBCGPComplexGeometry shape;
		pGM->CombineGeometry(shape, eg, egInternal, RGN_DIFF);

		pGM->FillGeometry(shape, m_brFill);

	}
	else
	{
		if (m_nShadowDepth > 0)
		{
			pGM->FillEllipse(rectShadow, m_brShadow);
		}

		pGM->FillEllipse(rect, m_brFill);
	}

	pGM->DrawEllipse(rect, m_brBorder);

	if (!pGM->IsSupported(BCGP_GRAPHICS_MANAGER_COLOR_OPACITY))
	{
		CBCGPRect rect1 = rect;
		rect1.DeflateRect(1, 1);

		pGM->DrawEllipse(rect1, m_brFill);
	}

	BOOL bIsCtrlDisabled = m_pCtrl->GetSafeHwnd() != NULL && !m_pCtrl->IsWindowEnabled();

	for (int i = 0; i < nItems; i++)
	{
		CBCGPRadialMenuItem* pItem = m_arItems[i];
		ASSERT_VALID(pItem);

		if (i == m_nHighlighted)
		{
			pGM->FillGeometry(pItem->m_Shape, m_nHighlighted == m_nPressed ? m_brPressed : 
				m_brHighlighted.IsEmpty() ? m_brFill : m_brHighlighted);
		}

		pItem->OnDrawIcon(pGM, bIsCtrlDisabled, m_Icons, sizeIcon);

		pGM->DrawGeometry(pItem->m_Shape, m_brBorder);
	}

	pGM->DrawEllipse(ellipseInt, m_brBorder);

	if (!pGM->IsSupported(BCGP_GRAPHICS_MANAGER_ANTIALIAS))
	{
		rect.InflateRect(1, 1);
		pGM->DrawEllipse(rect, m_brBorder);
	}

	if (m_pCtrl->GetSafeHwnd() != NULL && m_pCtrl->IsFocused() && !m_pCtrl->IsPopup())
	{
		rect.InflateRect(1, 1);
		pGM->DrawEllipse(rect, m_brFocusedBorder);
	}
}
//*******************************************************************************
void CBCGPChartInterLineColoringEffect::OnDraw(CBCGPGraphicsManager* pGM)
{
	ASSERT_VALID(this);

	if (m_pSeries1 == NULL || m_arPointsSeries1.GetSize() < 2 || !IsVisible())
	{
		return;
	}

	BOOL bWasTransparency = CBCGPGraphicsManagerGDI::IsTransparencyEnabled();
	CBCGPGraphicsManagerGDI::EnableTransparency();

	BCGPChartFormatSeries::ChartCurveType curveType = m_pSeries1->GetCurveType();

	if (curveType == BCGPChartFormatSeries::CCT_STEP || curveType == BCGPChartFormatSeries::CCT_REVERSED_STEP ||
		m_pSeries2 != NULL && (m_pSeries2->GetCurveType() == BCGPChartFormatSeries::CCT_STEP || 
								m_pSeries2->GetCurveType() == BCGPChartFormatSeries::CCT_REVERSED_STEP))
	{
		return;
	}

	CBCGPChartAxis* pXAxis = m_pSeries1->GetRelatedAxis(CBCGPChartSeries::AI_X);
	CBCGPRect rectBounds = pXAxis->GetBoundingRect();

	CBCGPChartAxis* pYAxis = m_pSeries1->GetRelatedAxis(CBCGPChartSeries::AI_Y);

	if (pYAxis->m_bReverseOrder)
	{
		pYAxis->IsVertical() ? rectBounds.SwapTopBottom() : rectBounds.SwapLeftRight();
	}

	if (pXAxis->m_bReverseOrder)
	{
		pXAxis->IsVertical() ?  rectBounds.SwapTopBottom() : rectBounds.SwapLeftRight();
	}

	BCGPSeriesColorsPtr colors;
	m_pSeries1->GetColors(colors, -1);

	CBCGPBrush& brFill = m_brTopBrush.IsEmpty() ? *colors.m_pBrElementFillColor : m_brTopBrush;
	
	CBCGPGeometry* pDrawGeometry = CreateGeometry(m_arPointsSeries1, rectBounds, curveType, FALSE);
	CBCGPGeometry* pClipGeometry = m_pSeries2 != NULL ? 
		CreateGeometry(m_arPointsSeries2, rectBounds, m_pSeries2->GetCurveType(), TRUE) : 
		CreateClipGeometry(m_dblOrigin);

	ASSERT_VALID(pDrawGeometry);
	ASSERT_VALID(pClipGeometry);

	DrawEffect(pGM, pDrawGeometry, pClipGeometry, rectBounds, brFill);

	delete pClipGeometry;
	delete pDrawGeometry;

	if (m_pSeries2 != NULL && m_arPointsSeries2.GetSize() > 2 && !m_bTopOnly)
	{
		BCGPSeriesColorsPtr colors;
		m_pSeries2->GetColors(colors, -1);

		pDrawGeometry = CreateGeometry(m_arPointsSeries2, rectBounds, m_pSeries2->GetCurveType(), FALSE);
		pClipGeometry = CreateGeometry(m_arPointsSeries1, rectBounds, curveType, TRUE);

		CBCGPBrush& brFill = m_brBottomBrush.IsEmpty() ? *colors.m_pBrElementFillColor : m_brBottomBrush;
		DrawEffect(pGM, pDrawGeometry, pClipGeometry, rectBounds, brFill);

		delete pClipGeometry;
		delete pDrawGeometry;
	}

	CBCGPGraphicsManagerGDI::EnableTransparency(bWasTransparency);
}
//*******************************************************************************
BOOL CBCGPLinearGaugeImpl::GetRangeShape(CBCGPRect& rect, CBCGPPolygonGeometry& shape, double dblStartValue, double dblFinishValue,
	double dblStartWidth, double dblFinishWidth,
	double dblOffsetFromFrame, int nScale)
{
	rect.SetRectEmpty();
	shape.Clear();

	CBCGPPoint pt1;
	if (!ValueToPoint(dblStartValue, pt1, nScale))
	{
		return FALSE;
	}

	CBCGPPoint pt2;
	if (!ValueToPoint(dblFinishValue, pt2, nScale))
	{
		return FALSE;
	}

	const double scaleRatio = GetScaleRatioMid();

	if (dblStartWidth == 0.)
	{
		CBCGPGaugeScaleObject* pScale = GetScale(nScale);
		if (pScale != NULL)
		{
			dblStartWidth = pScale->m_dblMajorTickMarkSize + pScale->m_dblOffsetFromFrame;
		}
	}

	if (dblFinishWidth == 0.)
	{
		dblFinishWidth = dblStartWidth;
	}

	dblStartWidth *= scaleRatio;
	dblFinishWidth *= scaleRatio;	
	dblOffsetFromFrame *= scaleRatio;

	if (dblFinishWidth == dblStartWidth)
	{
		if (m_bIsVertical)
		{
			rect.left = pt1.x + dblOffsetFromFrame;
			rect.top = pt1.y;
			rect.right = pt1.x + dblStartWidth + dblOffsetFromFrame;
			rect.bottom = pt2.y;
		}
		else
		{
			rect.left = pt1.x;
			rect.top = pt1.y + dblOffsetFromFrame;
			rect.right = pt2.x;
			rect.bottom = pt1.y + dblStartWidth + dblOffsetFromFrame;
		}
	}
	else
	{
		CBCGPPointsArray arPoints;

		if (m_bIsVertical)
		{
			arPoints.Add(CBCGPPoint(pt1.x + dblOffsetFromFrame, pt1.y));
			arPoints.Add(CBCGPPoint(pt2.x + dblOffsetFromFrame, pt2.y));
			arPoints.Add(CBCGPPoint(pt2.x + dblOffsetFromFrame + dblFinishWidth, pt2.y));
			arPoints.Add(CBCGPPoint(pt1.x + dblOffsetFromFrame + dblStartWidth, pt1.y));
		}
		else
		{
			arPoints.Add(CBCGPPoint(pt1.x, pt1.y + dblOffsetFromFrame));
			arPoints.Add(CBCGPPoint(pt2.x, pt2.y + dblOffsetFromFrame));
			arPoints.Add(CBCGPPoint(pt2.x, pt2.y + dblOffsetFromFrame + dblFinishWidth));
			arPoints.Add(CBCGPPoint(pt1.x, pt1.y + dblOffsetFromFrame + dblStartWidth));
		}

		shape.SetPoints(arPoints);
	}

	return TRUE;
}
Beispiel #27
0
BOOL bcg_CS_clip(const CBCGPRect& rect, CBCGPPoint& point1, CBCGPPoint& point2)
{
	if (rect.IsRectEmpty ())
	{
		return TRUE;
	}

	if (point1 == point2)
	{
		return rect.PtInRect(point1);
	}

	int code1 = bcg_CS_code(rect, point1);
	int code2 = bcg_CS_code(rect, point2);

	double kx = 0.0;
	double ky = 0.0;
	CBCGPSize d(point2.x - point1.x, point2.y - point1.y);
	if (d.cx != 0.0)
	{
		ky = d.cy / d.cx;
	}
	else if (d.cy == 0.0)
	{
		if (code1 == 0 && code2 == 0)
		{
			return TRUE;
		}
		else
		{
			return FALSE;
		}
	}

	if (d.cy != 0.0)
	{
		kx = d.cx / d.cy;
	}

	BOOL visible = FALSE;
	CBCGPPoint pt1(point1);
	CBCGPPoint pt2(point2);

	int count_inv = 0;

	for(int i = 0; i < 4; i++)
	{
		if (code1 & code2)
		{
			break;
		}
		else if (code1 == 0 && code2 == 0)
		{
			visible = TRUE;
			break;
		}

		if (code1 == 0)
		{
			int c = code1;
			code1 = code2;
			code2 = c;

			CBCGPPoint p(pt1);
			pt1 = pt2;
			pt2 = p;

			count_inv++;
		}

		if (code1 & 0x01)
		{
			pt1.y += ky * (rect.left - pt1.x);
			pt1.x = rect.left;
		}
		else if (code1 & 0x02)
		{
			pt1.y += ky * (rect.right - pt1.x);
			pt1.x = rect.right;
		}
		else if (code1 & 0x04)
		{
			pt1.x += kx * (rect.top - pt1.y);
			pt1.y = rect.top;
		}
		else if (code1 & 0x08)
		{
			pt1.x += kx * (rect.bottom - pt1.y);
			pt1.y = rect.bottom;
		}

		code1 = bcg_CS_code(rect, pt1);
	}

	if (visible)
	{
		if ((count_inv % 2) != 0)
		{
			point1 = pt1;
			point2 = pt2;
		}
		else
		{
			point1 = pt2;
			point2 = pt1;
		}
	}

	return visible;
}
//*******************************************************************************
void CBCGPChartObject::OnDrawShape(CBCGPGraphicsManager* pGM, const CBCGPRect& /*rectDiagram*/)
{
	ASSERT_VALID(this);

	if (m_rectScreen.IsRectNull() || m_rectScreen.IsRectEmpty())
	{
		return;
	}

	CBCGPRect rectShape = m_rectScreen;
	rectShape.Normalize();

	if (!m_format.m_brFillColor.IsEmpty())
	{
		if (m_dblShadowDepth > 0. && pGM->IsSupported(BCGP_GRAPHICS_MANAGER_COLOR_OPACITY))
		{
			CBCGPRect rectShadow = rectShape;
			rectShadow.OffsetRect(m_dblShadowDepth, m_dblShadowDepth);

			CBCGPGeometry geometryShadow;

			if (!m_szCornerRadius.IsNull())
			{
				pGM->CombineGeometry(geometryShadow, 
					CBCGPRoundedRectangleGeometry(CBCGPRoundedRect(rectShadow, m_szCornerRadius.cx, m_szCornerRadius.cy)), 
					CBCGPRoundedRectangleGeometry(CBCGPRoundedRect(rectShape, m_szCornerRadius.cx, m_szCornerRadius.cy)), 
					RGN_DIFF);
			}
			else
			{
				pGM->CombineGeometry(geometryShadow, 
					CBCGPRectangleGeometry(rectShadow), 
					CBCGPRectangleGeometry(rectShape), 
					RGN_DIFF);
			}

			pGM->FillGeometry(geometryShadow, m_brShadow);
		}

		if (!m_szCornerRadius.IsNull())
		{
			pGM->FillRoundedRectangle(CBCGPRoundedRect(rectShape, m_szCornerRadius.cx, m_szCornerRadius.cy), m_format.m_brFillColor);
		}
		else
		{
			pGM->FillRectangle(rectShape, m_format.m_brFillColor);
		}
	}
	
	if (!m_format.m_outlineFormat.m_brLineColor.IsEmpty())
	{
		if (!m_szCornerRadius.IsNull())
		{
			pGM->DrawRoundedRectangle(CBCGPRoundedRect(rectShape, m_szCornerRadius.cx, m_szCornerRadius.cy), m_format.m_outlineFormat.m_brLineColor, 
							m_format.m_outlineFormat.GetLineWidth(TRUE), 
							&m_format.m_outlineFormat.m_strokeStyle);
		}
		else
		{
			pGM->DrawRectangle(rectShape, m_format.m_outlineFormat.m_brLineColor, 
							m_format.m_outlineFormat.GetLineWidth(TRUE), 
							&m_format.m_outlineFormat.m_strokeStyle);
		}
	}
}
//*******************************************************************************
void CBCGPChartObject::OnCalcScreenPoints(CBCGPGraphicsManager* pGM)
{
	ASSERT_VALID(this);

	m_rectScreen.SetRectEmpty();
	m_rectScreenBounds.SetRectEmpty();
	m_ptAnchor.x = m_ptAnchor.y = 0.;

	if (m_pParentChart == NULL)
	{
		return;
	}

	CBCGPSize szScaleRatio = m_pParentChart->GetScaleRatio();

	CBCGPChartAxis* pXAxis = m_pXAxis;
	CBCGPChartAxis* pYAxis = m_pYAxis;

	if (m_coordinateMode == CBCGPChartObject::CM_CHART_VALUES || 
		m_coordinateMode == CBCGPChartObject::CM_CHART_VALUE_DIST_ANGLE ||
		m_coordinateMode == CBCGPChartObject::CM_AXIS_INSIDE_MARK ||
		m_coordinateMode == CBCGPChartObject::CM_AXIS_OUTSIDE_MARK)
	{
		if (pXAxis == NULL)
		{
			m_pXAxis = pXAxis = m_pParentChart->GetChartAxis(BCGP_CHART_X_PRIMARY_AXIS);
		}

		if (pYAxis == NULL)
		{
			m_pYAxis = pYAxis = m_pParentChart->GetChartAxis(BCGP_CHART_Y_PRIMARY_AXIS);
		}
	}

	CBCGPSize szObjectSize = m_szObjectSize.IsNull() ?  OnCalcObjectSize(pGM) : m_szObjectSize;

	m_rectScreenBounds = OnCalcBoundingRect();

	CBCGPRect rectBounds = m_rectScreenBounds;

	CBCGPPoint ptLeftTop(CBCGPChartObject::_EmptyPoint);
	CBCGPPoint ptRightBottom(CBCGPChartObject::_EmptyPoint);

	if (m_coordinateMode == CBCGPChartObject::CM_PERCENTS)
	{
		if (m_rectCoordinates.left != CBCGPChartObject::_EmptyCoordinate)
		{
			ptLeftTop.x = rectBounds.left + rectBounds.Width() * m_rectCoordinates.left / 100.;
		}

		if (m_rectCoordinates.top != CBCGPChartObject::_EmptyCoordinate)
		{
			ptLeftTop.y = rectBounds.top + rectBounds.Height() * m_rectCoordinates.top / 100;
		}

		if (m_rectCoordinates.right != CBCGPChartObject::_EmptyCoordinate)
		{
			ptRightBottom.x = rectBounds.right - rectBounds.Width() * m_rectCoordinates.right / 100.;
		}

		if (m_rectCoordinates.bottom != CBCGPChartObject::_EmptyCoordinate)
		{
			ptRightBottom.y = rectBounds.bottom - rectBounds.Height() * m_rectCoordinates.bottom / 100.;
		}
	}
	else if (m_coordinateMode == CBCGPChartObject::CM_PIXELS)
	{
		if (m_rectCoordinates.left != CBCGPChartObject::_EmptyCoordinate)
		{
			ptLeftTop.x = rectBounds.left + m_rectCoordinates.left * szScaleRatio.cx;
		}

		if (m_rectCoordinates.top != CBCGPChartObject::_EmptyCoordinate)
		{
			ptLeftTop.y = rectBounds.top + m_rectCoordinates.top * szScaleRatio.cy;
		}

		if (m_rectCoordinates.right != CBCGPChartObject::_EmptyCoordinate)
		{
			ptRightBottom.x = rectBounds.right - m_rectCoordinates.right * szScaleRatio.cx;
		}

		if (m_rectCoordinates.bottom != CBCGPChartObject::_EmptyCoordinate)
		{
			ptRightBottom.y = rectBounds.bottom - m_rectCoordinates.bottom * szScaleRatio.cy;
		}
	}
	else if (m_coordinateMode == CBCGPChartObject::CM_PIXELS_FIXED_SIZE)
	{
		if (m_rectCoordinates.left != CBCGPChartObject::_EmptyCoordinate)
		{
			ptLeftTop.x = rectBounds.left + m_rectCoordinates.left * szScaleRatio.cx;
		}
		else
		{
			ptLeftTop.x = rectBounds.left;
		}

		if (m_rectCoordinates.top != CBCGPChartObject::_EmptyCoordinate)
		{
			ptLeftTop.y = rectBounds.top + m_rectCoordinates.top * szScaleRatio.cy;
		}
		else
		{
			ptLeftTop.y = rectBounds.top;
		}

		if (m_rectCoordinates.right != CBCGPChartObject::_EmptyCoordinate)
		{
			ptRightBottom.x = ptLeftTop.x + m_rectCoordinates.right * szScaleRatio.cx;
		}
		else
		{
			ptRightBottom.x = ptLeftTop.x + rectBounds.Width();
		}

		if (m_rectCoordinates.bottom != CBCGPChartObject::_EmptyCoordinate)
		{
			ptRightBottom.y = ptLeftTop.y + m_rectCoordinates.bottom * szScaleRatio.cy;
		}
		else
		{
			ptRightBottom.y = ptLeftTop.y + rectBounds.Height();
		}
	}
	else if (m_coordinateMode == CBCGPChartObject::CM_CHART_VALUES)
	{
		ASSERT_VALID(pXAxis);
		ASSERT_VALID(pYAxis);

		CBCGPRect rectCoordinates = m_rectCoordinates;

		if (pXAxis != NULL && pXAxis->IsVertical())
		{
			rectCoordinates = CBCGPRect(m_rectCoordinates.bottom, m_rectCoordinates.right,
										m_rectCoordinates.top, m_rectCoordinates.left);
			pXAxis = m_pYAxis;
			pYAxis = m_pXAxis;
		}

 		if (pXAxis->m_bReverseOrder && !pXAxis->IsVertical())
 		{
			if ((rectCoordinates.left != CBCGPChartObject::_EmptyCoordinate && 
				 rectCoordinates.right == CBCGPChartObject::_EmptyCoordinate) || 
				 (rectCoordinates.left == CBCGPChartObject::_EmptyCoordinate && 
				 rectCoordinates.right != CBCGPChartObject::_EmptyCoordinate))
			{
				rectCoordinates.SwapLeftRight();
			}
 		}
 
 		if (pYAxis->m_bReverseOrder && pYAxis->IsVertical())
 		{
			if ((rectCoordinates.top != CBCGPChartObject::_EmptyCoordinate && 
				 rectCoordinates.bottom == CBCGPChartObject::_EmptyCoordinate) || 
				 (rectCoordinates.top == CBCGPChartObject::_EmptyCoordinate && 
				 rectCoordinates.bottom != CBCGPChartObject::_EmptyCoordinate))
			{
				rectCoordinates.SwapTopBottom();
			}
 		}

		if (rectCoordinates.left != CBCGPChartObject::_EmptyCoordinate)
		{
			ptLeftTop.x = pXAxis->PointFromValue(rectCoordinates.left, FALSE);
		}
		else if (szObjectSize.cx == 0)
		{
			ptLeftTop.x = pXAxis->PointFromValue(pXAxis->GetMinDisplayedValue(TRUE), TRUE);
		}

		if (rectCoordinates.top != CBCGPChartObject::_EmptyCoordinate)
		{
			ptLeftTop.y = pYAxis->PointFromValue(rectCoordinates.top, FALSE);
		}
		else if (szObjectSize.cy == 0)
		{
			ptLeftTop.y = pYAxis->PointFromValue(pYAxis->GetMaxDisplayedValue(TRUE), TRUE);
		}

		if (rectCoordinates.right != CBCGPChartObject::_EmptyCoordinate)
		{
			ptRightBottom.x = pXAxis->PointFromValue(rectCoordinates.right, FALSE);
		}
		else if (szObjectSize.cx == 0)
		{
			ptRightBottom.x = pXAxis->PointFromValue(pXAxis->GetMaxDisplayedValue(TRUE), TRUE);
		}

		if (rectCoordinates.bottom != CBCGPChartObject::_EmptyCoordinate)
		{
			ptRightBottom.y = pYAxis->PointFromValue(rectCoordinates.bottom, FALSE);
		}
		else if (szObjectSize.cy == 0)
		{
			ptRightBottom.y = pYAxis->PointFromValue(pYAxis->GetMinDisplayedValue(TRUE), TRUE);
		}
	}
	else if (m_coordinateMode == CM_CHART_VALUE_DIST_ANGLE)
	{
		ASSERT_VALID(pXAxis);
		ASSERT_VALID(pYAxis);

		if (!IsRectOffsetsValid())
		{
			return;
		}

		CBCGPRect rectCoordinates = m_rectCoordinates;

		if (pXAxis != NULL && pXAxis->IsVertical())
		{
			rectCoordinates = CBCGPRect(m_rectCoordinates.top, m_rectCoordinates.left,
										m_rectCoordinates.right, m_rectCoordinates.bottom);
			pXAxis = m_pYAxis;
			pYAxis = m_pXAxis;
		}

		m_ptAnchor.x = pXAxis->PointFromValue(rectCoordinates.left, FALSE);
		m_ptAnchor.y = pYAxis->PointFromValue(rectCoordinates.top, FALSE); 

		double dblDistanceX = rectCoordinates.right * szScaleRatio.cx;
		double dblDistanceY = rectCoordinates.right * szScaleRatio.cy;

		CBCGPPoint ptCenter(m_ptAnchor.x + dblDistanceX * sin(bcg_deg2rad(rectCoordinates.bottom)),
							m_ptAnchor.y - dblDistanceY * cos(bcg_deg2rad(rectCoordinates.bottom)));

		ptLeftTop.SetPoint(ptCenter.x - szObjectSize.cx / 2, ptCenter.y - szObjectSize.cy / 2);
		ptRightBottom.SetPoint(ptCenter.x + szObjectSize.cx / 2, ptCenter.y + szObjectSize.cy / 2);
	}
	else if (m_coordinateMode == CM_AXIS_INSIDE_MARK || m_coordinateMode == CM_AXIS_OUTSIDE_MARK)
	{
		ASSERT_VALID(pXAxis);
		ASSERT_VALID(pYAxis);

		if (m_rectCoordinates.top == _EmptyCoordinate && m_rectCoordinates.left == _EmptyCoordinate)
		{
			return;
		}

		double dblHorzOffset = m_rectCoordinates.right == _EmptyCoordinate ? 0 : m_rectCoordinates.right * szScaleRatio.cx;
		double dblVertOffset = m_rectCoordinates.bottom == _EmptyCoordinate ? 0 : m_rectCoordinates.bottom * szScaleRatio.cy;

		CBCGPChartAxis* pUsedAxis = m_rectCoordinates.left != _EmptyCoordinate ? pXAxis : pYAxis;
		double dblUsedValue = m_rectCoordinates.left != _EmptyCoordinate ? m_rectCoordinates.left : m_rectCoordinates.top;

		double dblScrVal = pUsedAxis->PointFromValue(dblUsedValue, FALSE);
		CBCGPRect rectAxis = pUsedAxis->GetAxisRect(FALSE, FALSE, TRUE);

		CBCGPPoint ptCenter;

		if (m_coordinateMode == CM_AXIS_INSIDE_MARK && 
			(pUsedAxis->m_axisDefaultPosition == CBCGPChartAxis::ADP_BOTTOM || pUsedAxis->m_axisDefaultPosition == CBCGPChartAxis::ADP_LEFT) ||
			m_coordinateMode == CM_AXIS_OUTSIDE_MARK && 
			(pUsedAxis->m_axisDefaultPosition == CBCGPChartAxis::ADP_TOP || pUsedAxis->m_axisDefaultPosition == CBCGPChartAxis::ADP_RIGHT))
		{
			if (pUsedAxis->IsVertical())
			{
				ptCenter.x = rectAxis.right + dblVertOffset + szObjectSize.cx / 2;
				ptCenter.y = dblScrVal - dblHorzOffset;
			}
			else
			{
				ptCenter.x = dblScrVal + dblHorzOffset;
				ptCenter.y = rectAxis.top - dblVertOffset - szObjectSize.cy /2;
			}
		}
		else
		{
			if (pUsedAxis->IsVertical())
			{
				ptCenter.x = rectAxis.left - dblVertOffset - szObjectSize.cx / 2;
				ptCenter.y = dblScrVal - dblHorzOffset;
			}
			else
			{
				ptCenter.x = dblScrVal + dblHorzOffset;
				ptCenter.y = rectAxis.bottom + dblVertOffset + szObjectSize.cy /2;
			}
		}

		if (pUsedAxis->IsVertical() && (ptCenter.y < rectAxis.top || ptCenter.y > rectAxis.bottom) || 
			!pUsedAxis->IsVertical() && (ptCenter.x < rectAxis.left || ptCenter.x > rectAxis.right))
		{
			return;
		}

		ptLeftTop.SetPoint(ptCenter.x - szObjectSize.cx / 2, ptCenter.y - szObjectSize.cy / 2);
		ptRightBottom.SetPoint(ptCenter.x + szObjectSize.cx / 2, ptCenter.y + szObjectSize.cy / 2);
	}
	else
	{
		return;
	}

	if (m_format.m_textFormat.IsWordWrap())
	{
		if (ptLeftTop.x == CBCGPChartObject::_EmptyCoordinate && 
			ptRightBottom.x == CBCGPChartObject::_EmptyCoordinate && 
			szObjectSize.cx == 0)
		{
			ASSERT(FALSE);
			TRACE0(" CBCGPChartObject::OnCalcScreenPoints: Left and right offsets must be specified in order to properly use wrapped text.\n");
			return;
		}

		szObjectSize = pGM->GetTextSize(m_strText, m_format.m_textFormat, ptRightBottom.x - ptLeftTop.x);
	}
	

	if (ptLeftTop.x == CBCGPChartObject::_EmptyCoordinate)
	{
		ptLeftTop.x = ptRightBottom.x - szObjectSize.cx;
	}

	if (ptLeftTop.y == CBCGPChartObject::_EmptyCoordinate)
	{
		ptLeftTop.y = ptRightBottom.y - szObjectSize.cy;
	}

	if (ptRightBottom.x == CBCGPChartObject::_EmptyCoordinate)
	{
		ptRightBottom.x = ptLeftTop.x + szObjectSize.cx;
	}

	if (ptRightBottom.y == CBCGPChartObject::_EmptyCoordinate)
	{
		ptRightBottom.y = ptLeftTop.y + szObjectSize.cy;
	}

	m_rectScreen.SetRect(ptLeftTop, ptRightBottom);
}
Beispiel #30
0
//*******************************************************************************
void CBCGPKnob::CreatePointerPoints(double dblRadius,
                                    CBCGPPointsArray& arPoints,
                                    int nPointerIndex, BOOL bShadow)
{
    if (m_rect.IsRectEmpty())
    {
        return;
    }

    CBCGPRect rect = m_rect;

    CBCGPKnobPointer* pData = DYNAMIC_DOWNCAST(CBCGPKnobPointer, m_arData[nPointerIndex]);
    if (pData == NULL)
    {
        ASSERT(FALSE);
        return;
    }

    CBCGPCircularGaugeScale* pScale = GetScale(pData->GetScale());
    if (pScale == NULL)
    {
        ASSERT(FALSE);
        return;
    }

    const double scaleRatio = GetScaleRatioMid();

    double dblValue = pData->IsAnimated() ? pData->GetAnimatedValue() : pData->GetValue();

    double dblOffset = bcg_clamp(pData->GetOffsetFromCenter(), 0.0, 1.0);
    if (dblOffset == 0.0)
    {
        dblOffset = dblRadius * .5;
    }
    else
    {
        dblOffset = dblRadius * bcg_clamp(dblOffset, 0.5, 1.0);
    }

    double dblAngle = bcg_deg2rad(pScale->GetStartAngle()) - bcg_deg2rad(pScale->GetStartAngle() - pScale->GetFinishAngle()) * (dblValue - pScale->GetStart()) / (pScale->GetFinish() - pScale->GetStart());
    dblAngle = bcg_normalize_rad (dblAngle);

    if (bShadow)
    {
        rect.OffsetRect(2 * m_sizeScaleRatio.cx, 2 * m_sizeScaleRatio.cy);
    }

    CBCGPPoint center((rect.left + rect.right) / 2.0, (rect.top + rect.bottom) / 2.0);

    const double angleCos  = cos(dblAngle);
    const double angleSin  = sin(dblAngle);

    double dblWidth = bcg_clamp(pData->GetWidth(), 0.0, dblRadius / 10.0);
    const double dblPointerAngle = dblAngle - M_PI_2;

    switch (pData->GetStyle())
    {
    case CBCGPKnobPointer::BCGP_KNOB_POINTER_HANDLE:
    {
        dblRadius -= .2 * scaleRatio;
        double dblExtend = (dblRadius * .9);
        double dblSize = dblRadius + dblExtend;
        center.x -= angleCos * dblExtend;
        center.y += angleSin * dblExtend;

        if (dblWidth == 0.0)
        {
            dblWidth = dblRadius / 3.0;
        }

        dblWidth *= 0.5;

        if (dblWidth < 1.0)
        {
            arPoints.Add(center);

            arPoints.Add(CBCGPPoint(
                             center.x + angleCos * dblSize,
                             center.y - angleSin * dblSize));
        }
        else
        {
            double dblArrowLen = max(2.0 * dblWidth, 10.0 * scaleRatio);
            dblSize -= dblArrowLen;

            const double dx = cos(dblPointerAngle) * dblWidth;
            const double dy = -sin(dblPointerAngle) * dblWidth;

            arPoints.Add(CBCGPPoint(center.x + dx, center.y + dy));

            arPoints.Add(CBCGPPoint(center.x - dx, center.y - dy));

            const CBCGPPoint pt1(
                center.x + angleCos * dblSize - dx,
                center.y - angleSin * dblSize - dy);

            const CBCGPPoint pt2(
                center.x + angleCos * dblSize + dx,
                center.y - angleSin * dblSize + dy);

            arPoints.Add(pt1);

            arPoints.Add(CBCGPPoint(
                             center.x + angleCos * (dblSize + dblArrowLen),
                             center.y - angleSin * (dblSize + dblArrowLen)));

            arPoints.Add(pt2);
        }
    }
    break;

    case CBCGPKnobPointer::BCGP_KNOB_POINTER_LINE:
    {
        if (bShadow)
        {
            return;
        }

        if (dblWidth == 0.0)
        {
            dblWidth = 2. * scaleRatio;
        }

        const double dx = cos(dblPointerAngle) * dblWidth;
        const double dy = -sin(dblPointerAngle) * dblWidth;

        arPoints.Add(CBCGPPoint(center.x + angleCos * dblOffset - dx, center.y - angleSin * dblOffset - dy));
        arPoints.Add(CBCGPPoint(center.x + angleCos * dblOffset + dx, center.y - angleSin * dblOffset + dy));

        dblOffset = dblRadius - 4. * scaleRatio;

        arPoints.Add(CBCGPPoint(center.x + angleCos * dblOffset + dx, center.y - angleSin * dblOffset + dy));
        arPoints.Add(CBCGPPoint(center.x + angleCos * dblOffset - dx, center.y - angleSin * dblOffset - dy));
    }
    break;

    case CBCGPKnobPointer::BCGP_KNOB_POINTER_CIRCLE:
    {
        if (bShadow)
        {
            return;
        }

        if (dblWidth == 0.0)
        {
            dblWidth = max(2. * scaleRatio, dblRadius / 8);
        }

        dblOffset = dblRadius - 6. * scaleRatio - dblWidth;

        arPoints.Add(CBCGPPoint(center.x + angleCos * dblOffset, center.y - angleSin * dblOffset));
        arPoints.Add(CBCGPPoint(dblWidth, dblWidth));
    }
    break;
    }
}