YOU CAN CODE!

 

With The Case Of UCanCode.net  Release The Power OF  Visual C++ !   HomeProducts | PurchaseSupport | Downloads  
XD++ Library
DocVizor
TFC Library
Free Products
Technical Support
UCanCode.net


Get Ready to Unleash the Power of UCanCode .NET


UCanCode Software focuses on general application software development. We provide complete solution for developers. No matter you want to develop a simple database workflow application, or an large flow/diagram based system, our product will provide a complete solution for you. Our product had been used by hundreds of top companies around the world!

"100% source code provided! Free you from not daring to use components because of unable to master the key technology of components!"


GDI+ Examples: GDI+ Line/Curve Drawing and Hit Test CObject and CObArray

By Husni Che Ngah.

Introduction

This HitTester demo application was written just to illustrate the basic drawing application framework using GDI/GDI+. It also features the useful curve/line hit test technique together with the implementation of CObject class and CObArray collection class which can be extended for serialization in future. I also added extra features for storing the drawing into *.png file format. CObject and CObArray

Background

Hit test is very important for developing good drawing kind of applications to determine whether the object is currently selected or not. There are various methods that can be used to perform curve or line hit test. For diagonal (straight) line, performing hit test can be done by comparing the two slopes of the object line and the test line. Test line is a line that is virtually constructed starting from the first point of the line shape to the test point. If both slopes match, the object line is hit.

For the case of curved-line, it is a bit difficult to perform hit test. Normally, flatten technique will be used to convert the curved line to a series of connected straight lines before performing slope comparison for each line segment. Phew! Thanks to GDI+ for the GraphicsPath class and its IsOutlineVisible() member function, this hit test tucancode.net can be done very easily.

You can refer to this article from MSDN regarding hit test using Win32. And this one as well for creating cables and connectors...

Using the code

Below are the steps for implementing the object class and GDI+ initialization.

Step 1:

Add the lines below to StdAfx.h file:

#include <gdiplus.h>
#include <Gdiplusinit.h>
using namespace Gdiplus;
#pragma comment(lib, "gdiplus.lib")

Step 2:

Add the lines below to HitTester.h file:

GdiplusStartupInput  m_gdiplusStartupInput;
ULONG_PTR  m_gdiplusToken;

Step 3:

Add the line below to HitTester.cpp file, under the CHitTesterApp::InitInstance() function, before the return statement:

GdiplusStartup(&m_gdiplusToken, &m_gdiplusStartupInput, NULL);

Step 4:

Add the line below to HitTester.cpp file, under the CHitTesterApp::Exitnstance() function:

GdiplusShutdown(m_gdiplusToken);

Step 5:

Add the line below to HitTesterView.h file:

#include "HCNObject.h"

Step 6:

Add the lines below to HitTesterView.cpp:

Collapse
void CHitTesterView::OnLButtonDown(UINT nFlags, CPoint point)
{
  // TODO: Add your message handler code here and/or call default
  CRect rect;
  GetClientRect(rect);

  if(rect.PtInRect(point))
  {
    if(m_bSelectMode)
    {
      // Create DC
      CDC* pDC = this->GetDC();

      VERIFY(pDC != NULL);

      if(pDC != NULL)
      {
        // Set ROP2 mode
        int nR2 = pDC->SetROP2(R2_NOTXORPEN);

        // Create pen
        CPen* newPen = new CPen(PS_SOLID|PS_GEOMETRIC, 1, RGB(255,0,255));
        CPen* pOldPen = pDC->SelectObject(newPen);

        // Run Hit Test
        for(int i=0; i<m_obArray->GetSize(); i++)
        {
          // Get base object
          CHCN_Object* pBase = m_obArray->GetObjectBaseClass(i);

          if(pBase->GetObjectType() == pBase->HCN_Line)
          {
            CHCN_ObjectLine* pLine = (CHCN_ObjectLine*)pBase;

            // Segment Selection mode
            if(m_bSelectSegment)
            {
              if(pLine->IsSegmentHit(point))
              {
                if(pLine->m_bSelected)
                {
                  pLine->m_bSelected = FALSE;
                  pLine->m_bShowNode = FALSE;
                }
                else
                {
                  pLine->m_bSelected = TRUE;
                  pLine->m_bShowNode = TRUE;
                }
              }
            }

            // Node Selection mode
            if(m_bSelectNode)
            {
              if(pLine->IsNodeHit(point))
              {
                if(pLine->m_bSelected)
                {
                  pLine->m_bSelected = FALSE;
                  pLine->m_bShowNode = FALSE;
                }
                else
                {
                  pLine->m_bSelected = TRUE;
                  pLine->m_bShowNode = TRUE;
                }
              }
            }
          }

          // Redraw
          Invalidate();
        }

        // Restore ROP2 mode
        VERIFY(nR2 >= 0);
        pDC->SetROP2(nR2);
        pDC->SelectObject(pOldPen);

        // Clean-up
        pDC->Detach();
        pDC = NULL;
      }
    }

    if(m_bDrawMode)
    {
      ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS));

      if(m_bDrawLine || m_bDrawCurve)
      {
        if(!m_bDrawBegin)
        {
          // Begin drawing
          m_bDrawBegin = TRUE;

          // Create new point array
          m_ptArray = new CHCN_ObjectArray();
        }

        // Add this point
        AddPointToArray(point);
      }
    }
  }
  CView::OnLButtonDown(nFlags, point);
}
Collapse
void CHitTesterView::OnMouseMove(UINT nFlags, CPoint point)
{
  // TODO: Add your message handler code here and/or call default
  CRect rect;
  GetClientRect(rect);

  if( rect.PtInRect(point))
  {
    ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));

    if(m_bDrawMode)
    {
      ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_CROSS));

      if(m_bDrawBegin && (nFlags != MK_LBUTTON))
      {
        // Obtain DC
        CDC* pDC = this->GetDC();

        // Verify it
        VERIFY(pDC != NULL);

        if(pDC != NULL)
        {
          // Set ROP2 mode
          int nR2 = pDC->SetROP2(R2_NOTXORPEN);

          if(m_bDrawLine || m_bDrawCurve)
          {
            // Convert to logical point
            pDC->DPtoLP(&m_pt1);
            pDC->DPtoLP(&m_pt2);

            // Erase previous line
            pDC->MoveTo(m_pt1);
            pDC->LineTo(m_pt2);

            // Set point #2 to current mouse point
            m_pt2 = point;
            pDC->DPtoLP(&m_pt2);

            // Draw new line
            pDC->MoveTo(m_pt1);
            pDC->LineTo(m_pt2);
          }

          // Restore ROP2 mode
          VERIFY(nR2 >= 0);
          pDC->SetROP2(nR2);

          // Clean-up
          pDC->Detach();
          pDC = NULL;
        }
      }
    }
  }
  else
  {
    ::SetCursor(AfxGetApp()->LoadStandardCursor(IDC_ARROW));
  }

  CView::OnMouseMove(nFlags, point);
}
void CHitTesterView::OnDraw(CDC* pDC)
{
  CHitTesterDoc* pDoc = GetDocument();
  ASSERT_VALID(pDoc);
  if (!pDoc)
    return;

  // TODO: add draw code for native data here
  if(m_obArray->GetSize() > 0)
  {
    CRect rect;
    GetClientRect(rect);
    Bitmap bmp(rect.Width(),rect.Height());
    Graphics* graph = Graphics::FromImage(&bmp);

    for(int i=0; i<m_obArray->GetSize(); i++)
    {
      // Get base object
      CHCN_Object* pBase = 
        reinterpret_cast<CHCN_Object*>(m_obArray->GetAt(i));

      if(pBase->GetObjectType() == pBase->HCN_Line)
      {
        CHCN_ObjectLine* pLine = (CHCN_ObjectLine*)m_obArray->GetAt(i);

        // Draw object to bitmap graphics
        pLine->DrawObject(*graph);
      }
    }

    Rect rc(rect.left,rect.top,rect.Width(),rect.Height());

    // Clone the bitmap
    m_Bitmap = bmp.Clone(rc, PixelFormatDontCare);

    // Draw the bitmap graphics
    Graphics graphics(pDC->m_hDC);
    graphics.DrawImage(m_Bitmap, rc);
  }
}
void CHitTesterView::OnFileSave()
{
  // TODO: Add your command handler code here

  // Save the drawing.
  const char szFilter[] = "Image Files (*.png)|*.png|All Files (*.*)|*.*||";
  const char szExt[] = "png";

  CFileDialog* dlg = new CFileDialog(FALSE, szExt, NULL, 
      OFN_CREATEPROMPT|OFN_OVERWRITEPROMPT, szFilter, this);

  if(dlg->DoModal() == IDOK)
  {
    CLSID pngClsid;
    GetEncoderClsid(L"image/png", &pngClsid);
    m_Bitmap->Save(dlg->GetPathName().AllocSysString(), &pngClsid, NULL);
  }
}
int CHitTesterView::GetEncoderClsid(const WCHAR* format, CLSID* pClsid)
{
  UINT num = 0; // number of image encoders
  UINT size = 0; // size of the image encoder array in bytes

  ImageCodecInfo* pImageCodecInfo = NULL;

  GetImageEncodersSize(&num, &size);
  if(size == 0)
    return -1; // Failure

  pImageCodecInfo = (ImageCodecInfo*)(malloc(size));
  if(pImageCodecInfo == NULL)
  return -1; // Failure

  GetImageEncoders(num, size, pImageCodecInfo);

  for(UINT j = 0; j < num; ++j)
  {
    if( wcscmp(pImageCodecInfo[j].MimeType, format) == 0 )
    {
      *pClsid = pImageCodecInfo[j].Clsid;
      free(pImageCodecInfo);
      return j; // Success
    }
  }

  free(pImageCodecInfo);
  return -1; // Failure
}

Downloads 

Download demo project - 41Kb
Download source - 4Kb

CObject and CObArray, VC++ Source Code, MFC Source Code and Visual C++.NET

 

 

Copyright ?1998-2007 UCanCode.Net Software , all rights reserved.
Other product and company names herein may be the trademarks of their respective owners.

Please direct your questions or comments to webmaster@ucancode.net