Click here to Skip to main content
15,879,326 members
Articles / Desktop Programming / MFC
Article

Simple Snap-to-Grid cursor to your graphics application

Rate me:
Please Sign up or sign in to vote.
4.98/5 (23 votes)
26 Oct 20024 min read 117.1K   7K   74   14
Simple Class to add Snap-to-Grid capability to a Windows drawing program.

Image 1

Introduction

I have been developing a graphics application, which uses controls to create and place objects on the drawing. I needed an accurate way of placing these objects with a mouse. I remembered that my 3DS MAX program has a snap-to-grid feature. I searched the web and found lots of questions on how to do it, some suggestions, but no code examples. It struck me that this would be a good first article to place on Code Project. As I got this feature to work, I generated an example project, which would be as simple as possible and would help me package SnapCursor class so it could be dropped into any project and used with a minimum of effort.

Features

Once added, SnapCursor works much like the snap-to-grid option on my 3DS MAX graphics program. When enabled, a second, crosshair cursor follows the regular mouse cursor, which is not replaced or eliminated. In the demo project, you can draw circles starting from center, then dragging to the radius that you want. As the regular cursor moves smoothly across the screen, the second cursor jumps in quantum amounts. When the left mouse button is depressed, the center of the circle is placed at the SnapCursor point. The circle drags to the desired size as the cursor jumps. When released, the circle is painted in a final color and the program is ready for the next circle. With the SnapCursor class you can do the following:

  • Set the increment amount
  • Set cursor offset (used in conjunction with increment)
  • Turn the cursor on or off.

Adding SnapCursor to your Project

Develop your project so that your mouse drawing functions in the view, such as OnLButtonDown(), OnMouseMove(), OnLButtonUp() are in place and working without SnapCursor.

  1. Develop your project so that your mouse drawing functions in the view, such as OnLButtonDown(), OnMouseMove(), OnLButtonUp() are in place and working without SnapCursor.
  2. Once satisfied that your draw program is working, add an OnEraseBkgnd() message handler by using your class wizard and choosing WM_ERASEBKGND message for the view. Use this method to fill the client area with a background color and grid.
  3. Copy the SnapCursor.cpp and SnapCursor.h files into your project folder and include them in your project by selecting menu Project->Add To Project->Files.
  4. Add #include "SnapCursor.h" your view class header file.
  5. Add the declaration CSnapCursor m_SnapCursor; as a member of your view class to instantiate the SnapCursor object.
  6. Add m_SnapCursor.GetFirstSnapPos(&point); as the first call in your OnLButtonDown(UINT nFlags, CPoint point). This will convert the point.x and point.y values to the snapped to position before using them as your first drawing point.
  7. Add m_SnapCursor.Draw(pDC, &point); as the first call after the device context instatiation in your
    OnMouseMove(UINT nFlags,
        CPoint point)
    , view class method. This call will continually convert point values to snapped-to values before using them and will also draw and re-draw the snap cursor as the mouse is moved within the client area.
  8. In the OnEraseBkgnd(CDC* pDC) method, add m_SnapCursor.Reset();. This is necessary to make sure that the cursor drawing algorithm is in the proper state if the window is resized or repainted. Otherwise, old cursor positions may re-appear if the the window is resized and the background is repainted.

Code Showing Step 6.

void CSceneView::OnLButtonDown(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
	
    CDC* pDC = GetDC();
    CRect rcClient;
    GetClientRect(&rcClient);

    // Get First Snap position for drawing circle
    m_SnapCursor.GetFirstSnapPos(&point);    // MUST ADD FOR SNAPCURSOR

    m_OriginPoint.x = point.x;  // the rest of the code
    m_OriginPoint.y = point.y;

...	
    CView::OnLButtonDown(nFlags, point);
}

 

Code Showing Step 7.

void CSceneView::OnMouseMove(UINT nFlags, CPoint point) 
{
    // TODO: Add your message handler code here and/or call default
    int old_mode;

    CDC* pDC = GetDC();
    // Display Cursor and get snapped point value
    m_SnapCursor.Draw(pDC, &point);    // MUST ADD FOR SNAPCURSOR

...	// The rest of the code
    CView::OnMouseMove(nFlags, point);
}

 

Code Showing Step 8.

BOOL CSceneView::OnEraseBkgnd(CDC* pDC) 
{
    // TODO: Add your message handler code here and/or call default
    // Reset or re-initialize snapcursor mechanism ("flip-flop")
    m_SnapCursor.Reset(); // MUST ADD FOR SNAPCURSOR
	
... // The rest of the code
}


Adding Optional features

  1. If you wish to set the snap value to something other than 10 pixels, add m_SnapCursor.SetSnapIncrement(value)to your view class constructor or add a method which allows you to change the in the call to m_SnapCursor.SetSnapIncrement(snapvalue);.
  2. m_SnapCursor.SetSnapOffset(X_offset, Y_offset);.is used in conjunction with SetSnapIncrement. It is used to align cursor snap to various grid configurations. For example, if snap increment is set to 50, major grid marks start at something other than 50, the cursor is not going to line up with the major grid lines unless an offset is used. This is tricky, because if you set an offset of 40, 40 and your increment is 5 or 10, its just going to make the cursor follow 50 units away from the Windows cursor. SnapIncrement and SnapOffset have to be used with some thought. The SnapOffset values default to 0, 0.

Methods

Necessary Methods

// Placed in "ButtonDown" method to capture first snapped point
void GetFirstSnapPos(CPoint *point); 

// Placed in "MouseMove" method to convert points and draw cursor
void Draw(CDC *pDC, CPoint *point); 

// Placed in "OnEraseBkgnd" to put SnapCursor in initial state when Window is redrawn
void Reset();

Optional Methods for Custom Settings

// Turn Snap on or off with TRUE or FALSE
void Enable(BOOL bOn);

// Set the snap increment to values other than 10
void SetSnapIncrement(int nIncrement); 

// Set offset from 0,0
void SetSnapOffset(int nX_offset, int nY_offset);

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
United States United States
I am a hybrid CS and EE professional and 3D Animator/Modeler.

I have been a software developer since 1975, having coded in numerous languages, including C, C++, Java, Perl, Basic, PDP-11 and other assembly languages.

I have worked in the UNIX/C/Shellscript world as well as Windows/C++/Visual C++ platform areas.

I use 3DS MAX 7 for Animation and 3D modeling, combined with other graphics applications for overall production

I have worked on a wide variety of projects: language interpreters/compilers, graphics, automatic gpib test control programs, real-time automation in embedded systems, scientific and engineering applications as well as financial and business. I prefer the scientific, control system and engineering related software.

Major companies include Bell Labs, Singer Corporate Research and Tycom Submarine systems both as full-time and a contractor.

Other interests include Vacuum Tube collecting, Ham Radio (WA2WHV), Photography, 3D art, antiques, Abyssinian cats, Piano, travel, nature.

Comments and Discussions

 
GeneralImprovement tip Pin
Husni Che Ngah5-Mar-05 21:44
Husni Che Ngah5-Mar-05 21:44 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.