dcsimg

Graph Marker with Hit-Testing

WEBINAR:
On-Demand

Application Security Testing: An Integral Part of DevOps


This article discusses how to create a graph marker class featuring * Hit-testing. * Shape change (circle, rectangle, triangle, and diamond). * Fill /line option. * Size change. A demo project is provided, in which you can find how to use hit-testing to display tool tip for a marker. In CMarker class, attributes include center point, radius, color, shape, size, and fill/line option. A number of get/set interface functions are provided. Three major member functions are Create(), DrawMarker(), and HitTest().

enum TMarkerShape {MARKER_CIRCLE, MARKER_RECTANGLE, MARKER_TRIANGLE, MARKER_DIAMOND};
enum TMarkerFilled {MARKER_FILL, MARKER_LINE};

class CMarker
{
public:
 CMarker();
 ~CMarker();

 void Create(int nRadius, COLORREF Color, TMarkerShape eShape, TMarkerFilled eFill);

// Attributes
private:
 CPoint m_ptCenter;
 int m_nRadius;
 COLORREF m_Color;
 TMarkerShape m_tShape;
 TMarkerFilled m_tType;

 CPen m_pen;
 CBrush m_brush;

public:
 // Shape
 int GetShape() { return int(m_tShape); }
 void SetShape(TMarkerShape eShape) { m_tShape = eShape;}

 // Fill
 int GetType() { return int(m_tType); }
 void SetFill() { m_tType = MARKER_FILL; }
 void SetLine() { m_tType = MARKER_LINE; }
 void SetType(TMarkerFilled eFill) { m_tType = eFill; }
	
 // Size
 int GetSize() const { return m_nRadius; }
 void SetSize(int m_sz) {m_nRadius = (m_sz<1) ? 1 : m_sz; }
	
 // Color
 COLORREF GetColor() const { return m_Color; }
 void SetColor(COLORREF m_cl) { m_Color = m_cl; }

 // Center
 CPoint GetCenter() const { return m_ptCenter; }
 void SetCenter(CPoint pt) { m_ptCenter = pt; }

 void DrawMarker(CDC* pDC) const;
 BOOL HitTest(const CPoint& Point) const;
};
When you create a marker, you have to specify its radius, color, and shape. The center point is (0,0) by default.

void CMarker::Create(int nRadius, COLORREF Color, TMarkerShape eShape, TMarkerFilled eFill)
{
 m_nRadius = nRadius;
 m_Color = Color;
 m_tShape = eShape;
 m_tType = eFill;

 switch(m_tType)
 {
 case MARKER_FILL:
  m_brush.CreateSolidBrush(m_Color);
 break;

 case MARKER_LINE:
  m_brush.CreateSolidBrush(RGB(255,255,255));
 break;
}

 m_pen.CreatePen(PS_SOLID, 1, m_Color);

 m_ptCenter.x = m_ptCenter.y =0;
}
The drawing function DrawMarker() Operates depending on the shape and fill/line option of a marker.

void CMarker::DrawMarker(CDC* pDC) const
{
 ASSERT_VALID(pDC);

 CPen* pOldPen = pDC->SelectObject((CPen*) &m_pen);
 CBrush* pOldBrush = pDC->SelectObject((CBrush*) &m_brush);

 switch(m_tShape)
 {
  case MARKER_CIRCLE:
  {
   CRect Rect;
   Rect.top = m_ptCenter.y - m_nRadius;
   Rect.bottom = m_ptCenter.y + m_nRadius;
   Rect.left = m_ptCenter.x - m_nRadius;
   Rect.right = m_ptCenter.x + m_nRadius;
   VERIFY(pDC->Ellipse(Rect));

   break;
  }

  case MARKER_RECTANGLE:
  {
   CRect Rect; 
   Rect.top = m_ptCenter.y - m_nRadius;
   Rect.bottom = m_ptCenter.y + m_nRadius;
   Rect.left = m_ptCenter.x - m_nRadius;
   Rect.right = m_ptCenter.x + m_nRadius;
   VERIFY(pDC->Rectangle(Rect));

   break;
  }

  case MARKER_TRIANGLE:
  {
   CPoint p[3];
   p[0].x = m_ptCenter.x;             p[0].y = m_ptCenter.y - m_nRadius;
   p[1].x = m_ptCenter.x - m_nRadius; p[1].y = m_ptCenter.y + m_nRadius;
   p[2].x = m_ptCenter.x + m_nRadius; p[2].y = p[1].y;
   VERIFY(pDC->Polygon(p, 3));

   break;
  }

  case MARKER_DIAMOND:
  {
   CPoint p[4];
   p[0].x = m_ptCenter.x;             p[0].y = m_ptCenter.y - m_nRadius;
   p[1].x = m_ptCenter.x + m_nRadius; p[1].y = m_ptCenter.y;
   p[2].x = m_ptCenter.x;             p[2].y = m_ptCenter.y + m_nRadius;
   p[3].x = m_ptCenter.x - m_nRadius; p[3].y = m_ptCenter.y;
   VERIFY(pDC->Polygon(p, 4));

   break;
  }
 }

 pDC->SelectObject(pOldBrush);
 pDC->SelectObject(pOldPen);
}
Hit-testing function HitTest() tests if the cursor hits a marker.

BOOL CMarker::HitTest(const CPoint& Point) const
{
 // Hit-test works best in device coordinates.
 switch(m_tShape)
 {
  case MARKER_CIRCLE:
  {
   CPoint Diff = m_ptCenter - Point;
   return ((Diff.x*Diff.x + Diff.y*Diff.y) <= m_nRadius*m_nRadius);

   break;
  }

  case MARKER_RECTANGLE:
  {
   CRgn rgn;
   rgn.CreateRectRgn(m_ptCenter.x-m_nRadius, m_ptCenter.y-m_nRadius,
   m_ptCenter.x+m_nRadius, m_ptCenter.y+m_nRadius);
   return rgn.PtInRegion(Point);

   break;
  }

  case MARKER_TRIANGLE:
  {
   CPoint p[3];
   p[0].x = m_ptCenter.x;             p[0].y = m_ptCenter.y - m_nRadius;
   p[1].x = m_ptCenter.x - m_nRadius; p[1].y = m_ptCenter.y + m_nRadius;
   p[2].x = m_ptCenter.x + m_nRadius; p[2].y = p[1].y;
   CRgn rgn;
   rgn.CreatePolygonRgn(p, 3, ALTERNATE);
   return rgn.PtInRegion(Point);

   break;
  }

  case MARKER_DIAMOND:
  {
   CPoint p[4];
   p[0].x = m_ptCenter.x;             p[0].y = m_ptCenter.y - m_nRadius;
   p[1].x = m_ptCenter.x + m_nRadius; p[1].y = m_ptCenter.y;
   p[2].x = m_ptCenter.x;             p[2].y = m_ptCenter.y + m_nRadius;
   p[3].x = m_ptCenter.x - m_nRadius; p[3].y = m_ptCenter.y;
   CRgn rgn;
   rgn.CreatePolygonRgn(p, 4, ALTERNATE);
   return rgn.PtInRegion(Point);

   break;
  }

  default:
   return FALSE;
 }
}
In the demo project, I create a simple graph with four series of data. It demonstrates different options of marker, such as shapes, colors, etc. When the cursor hits a marker, the background data value will be displayed by tool tip. Some API functions, such as CRgn::PtInRegion(), in HitTest() work best in device coordinates. It may be a good practice to convert the test-point into device coordinates. In the demo, I create one marker for each data series. If you want to use different marker option within one data series, you have to create one marker for each data point.

Download demo project - 26 KB

Date Last Updated: February 3, 1999



Comments

  • Posted by Legacy on 02/12/1999 12:00am

    Originally posted by: LuDong


    Reply
  • You must have javascript enabled in order to post comments.

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Discover the best practices from HPE’s IT Advisory Consulting Services for migrating and transforming applications in Hybrid IT by capitalizing on innovative platforms, modern application architectures, agile development tools and proven methodologies. There are a number of challenges our customers face when migrating and transforming applications for a Hybrid IT environment. This guide provide proven strategies and application approaches that can help them understand and reduce risks and complexity.

  • The Internet of Things (IoT), a growing network of physical devices with Internet connectivity, can collect and exchange rich data. Organizations utilizing the Enterprise of Things (EoT) are also at a greater risk for data breaches and cybersecurity threats. Healthcare organizations striving to realize the full potential of the EoT must also be able to confidentially and reliably transmit highly sensitive data between connected devices. Read this white paper to discover the best cybersecurity strategies to get …

Most Popular Programming Stories

More for Developers

RSS Feeds

Thanks for your registration, follow us on our social networks to keep up-to-date
×
We have made updates to our Privacy Policy to reflect the implementation of the General Data Protection Regulation.