Home

GDI Topics: Using a Trajectory

 
 

Introduction

A trajectory is a path along which an object travels. In one example, an object could go from one point to another without coming back. In another example, an object could go back and forth from one object to another. The study or implementation of trajectories is common in graphics applications, in games, or other simulation types.

Practical Learning Practical Learning: Introducing Trajectories

 

  1. To start this exercise, create an MFC Application named Trajectory1
  2. Create it as a Single Document type
  3. To fill the window's content with a black background, access the source file of the view and change its OnDraw event as follows:
     
    void CTrajectory1View::OnDraw(CDC* pDC)
    {
    	CTrajectory1Doc* pDoc = GetDocument();
    	ASSERT_VALID(pDoc);
    	if (!pDoc)
    		return;
    
    	// TODO: add draw code for native data here
    	CBrush brsBlack;
    	CRect rctClient;
    
    	brsBlack.CreateSolidBrush(RGB(0, 0, 0));
    
    	GetWindowRect(&rctClient);
    
    	CBrush *pOldBrush = pDC->SelectObject(&brsBlack);
    	pDC->Rectangle(0, 0, rctClient.Width(), rctClient.Height());
    
    	pDC->SelectObject(pOldBrush);
    }
  4. Execute the application to see the result

Moving a Shape

The primary implementation of using a trajectory consist of moving an object or a shape along a path. To do this, you can draw the shape in response to a timer.

Practical Learning Practical Learning: Moving a Shape

  1. Using the Resource Symbols dialog box, create an ID named IDT_MOVE
  2. Generate the OnInitialUpdate event for the view class and use it to initialize the timer as follows:
     
    void CTrajectory1View::OnInitialUpdate()
    {
    	CView::OnInitialUpdate();
    
    	SetTimer(IDT_MOVE, 10, NULL);
    	// TODO: Add your specialized code here and/or call the base class
    }
  3. In the view header file, declare the following variables:
     
    // Trajectory1View.h : interface of the CTrajectory1View class
    //
    
    
    #pragma once
    
    
    class CTrajectory1View : public CView
    {
    protected: // create from serialization only
    	CTrajectory1View();
    	DECLARE_DYNCREATE(CTrajectory1View)
    
    // Attributes
    public:
    	CTrajectory1Doc* GetDocument() const;
    
    // Operations
    public:
    	static const int ShapeWidth  = 45;
    	static const int ShapeHeight = 15;
    	int x;
    	int y;
    	bool MovingRight;
    
    // Overrides
    public:
    	virtual void OnDraw(CDC* pDC);  // overridden to draw this view
    	virtual BOOL PreCreateWindow(CREATESTRUCT& cs);
    protected:
    	virtual BOOL OnPreparePrinting(CPrintInfo* pInfo);
    	virtual void OnBeginPrinting(CDC* pDC, CPrintInfo* pInfo);
    	virtual void OnEndPrinting(CDC* pDC, CPrintInfo* pInfo);
    
    // Implementation
    public:
    	virtual ~CTrajectory1View();
    #ifdef _DEBUG
    	virtual void AssertValid() const;
    	virtual void Dump(CDumpContext& dc) const;
    #endif
    
    protected:
    
    // Generated message map functions
    protected:
    	DECLARE_MESSAGE_MAP()
    };
    
    #ifndef _DEBUG  // debug version in Trajectory1View.cpp
    inline CTrajectory1Doc* CTrajectory1View::GetDocument() const
       { return reinterpret_cast<CTrajectory1Doc*>(m_pDocument); }
    #endif
  4. In the constructor of the view class, initialize the variables as follows:
     
    CTrajectory1View::CTrajectory1View()
    {
    	// TODO: add construction code here
    	x = 0;
    	y = 200;
    	MovingRight = false;
    }
  5. Change the OnDraw event as follows:
     
    void CTrajectory1View::OnDraw(CDC* pDC)
    {
    	CTrajectory1Doc* pDoc = GetDocument();
    	ASSERT_VALID(pDoc);
    	if (!pDoc)
    		return;
    
    	// TODO: add draw code for native data here
    	CPen   penYellow;
    	CBrush brsBlack, brsFuchsia;
    	CRect rctClient;
    
    	brsBlack.CreateSolidBrush(RGB(0, 0, 0));
    	brsFuchsia.CreateSolidBrush(RGB(255, 0, 255));
    	penYellow.CreatePen(PS_SOLID, 1, RGB(255, 255, 0));
    
    	GetWindowRect(&rctClient);
    
    	CBrush *pOldBrush = pDC->SelectObject(&brsBlack);
    	pDC->Rectangle(0, 0, rctClient.Width(), rctClient.Height());
    
    	pOldBrush = pDC->SelectObject(&brsFuchsia);
    	CPen *pOldPen = pDC->SelectObject(&penYellow);
    	pDC->RoundRect(this->x, this->y, this->x + ShapeWidth,
    		       this->y + ShapeHeight, 5, 5);
    
    	pDC->SelectObject(pOldPen);
    	pDC->SelectObject(pOldBrush);
    }
  6. Generate the event of the WM_TIMER message for the view class and implement it as follows:
     
    void CTrajectory1View::OnTimer(UINT_PTR nIDEvent)
    {
    	// TODO: Add your message handler code here and/or call default
    	CRect rctClient;
    
    	GetWindowRect(&rctClient);
    
    	if( this->x < 0 )
    	{
    		this->x = 0;
    		this->MovingRight = true;
    	}
    
    	if( this->x > rctClient.Width() - ShapeWidth )
    	{
    		this->x = rctClient.Width() - ShapeWidth;
    		this->MovingRight = FALSE;
    	}
    
    	if( x < 0 )
    	{
    		x = 0;
    		MovingRight = TRUE;
    	}
    
    	if( x > rctClient.Width() - ShapeWidth )
    	{
    		x = rctClient.Width() - ShapeWidth;
    		MovingRight = FALSE;
    	}
    
    	Invalidate();
    
    	if( MovingRight == TRUE )
    		x++;
    	else
    		x--;
    
    	CView::OnTimer(nIDEvent);
    }
  7. Generate the event of the WM_ERASEBKGND message of the view class and change its return value as follows:
     
    BOOL CTrajectory1View::OnEraseBkgnd(CDC* pDC)
    {
    	// TODO: Add your message handler code here and/or call default
    
    	return TRUE; // CView::OnEraseBkgnd(pDC);
    }
  8. Execute the application to see the result:
     
  9. Close it and return to MSVC

Creating a Shape Class

Object oriented programming consists of creating objects that each can perform a specific task and take of assignments related to it. In trajectory-related programming, you can create a class for each object or shape. This can make it easy to recognize and use the object. If you happen to use different objects, you can then customize the behavior of each. 

Practical Learning Practical Learning: Creating a Shape Class

  1. To create a new class, on the main menu, click Project -> Add Class...
  2. In the Templates list, click C++ Class and click Add
  3. Set the Class Name to CShape and press Enter
  4. Change the header file as follows:
     
    #pragma once
    
    class CShape
    {
    public:
    	CShape(void);
    public:
    	~CShape(void);
    
    	static const int Width  = 45;
    	static const int Height = 15;
    	int x;
    	int y;
    	bool MovingRight;
    
    	void Draw(CDC* pDC);
    	void Move(CRect rctClient);
    };
  5. Change the source file as follows:
     
    #include "StdAfx.h"
    #include "Shape.h"
    
    CShape::CShape(void)
    	: x(0),
    	  y(150),
    	  MovingRight(false)
    {
    }
    
    CShape::~CShape(void)
    {
    }
    
    void CShape::Draw(CDC* pDC)
    {
    	CPen   penYellow;
    	CBrush brsFuchsia;
    
    	brsFuchsia.CreateSolidBrush(RGB(255, 0, 255));
    	penYellow.CreatePen(PS_SOLID, 1, RGB(255, 255, 0));
    
    	CBrush *pOldBrush = pDC->SelectObject(&brsFuchsia);
    	CPen *pOldPen = pDC->SelectObject(&penYellow);
    	pDC->RoundRect(this->x,
    		       this->y, 
    		       this->x + CShape::Width,
    		       this->y + CShape::Height,
    		       5,
    		       5);
    
    	pDC->SelectObject(pOldBrush);
    }
    
    void CShape::Move(CRect rctClient)
    {
    	if( this->x < 0 )
    	{
    		this->x = 0;
    		this->MovingRight = true;
    	}
    
    	if( this->x > rctClient.Width() - CShape::Width )
    	{
    		this->x = rctClient.Width() - CShape::Width;
    		this->MovingRight = FALSE;
    	}
    }
  6. Access the header file of the view class and change it as follows:
     
    // Trajectory2View.h : interface of the CTrajectory1View class
    //
    #pragma once
    
    #include "Shape.h"
    
    class CTrajectory1View : public CView
    {
    protected: // create from serialization only
    	CTrajectory1View();
    	DECLARE_DYNCREATE(CTrajectory1View)
    
    // Attributes
    public:
    	CTrajectory1Doc* GetDocument() const;
    
    // Operations
    public:
    	CShape *shape;
    
    // Overrides
    public:
    	. . .
    };
    
    #ifndef _DEBUG  // debug version in Trajectory2View.cpp
    inline CTrajectory1Doc* CTrajectory1View::GetDocument() const
       { return reinterpret_cast<CTrajectory1Doc*>(m_pDocument); }
    #endif
  7. Access the source file of the view class and change it as follows:
     
    // Trajectory2View.cpp : implementation of the CTrajectory1View class
    //
    
    #include "stdafx.h"
    #include "Trajectory2.h"
    
    #include "Trajectory2Doc.h"
    #include "Trajectory2View.h"
    
    #ifdef _DEBUG
    #define new DEBUG_NEW
    #endif
    
    // CTrajectory1View
    
    IMPLEMENT_DYNCREATE(CTrajectory1View, CView)
    
    BEGIN_MESSAGE_MAP(CTrajectory1View, CView)
    	// Standard printing commands
    	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
    	ON_COMMAND(ID_FILE_PRINT_DIRECT, &CView::OnFilePrint)
    	ON_COMMAND(ID_FILE_PRINT_PREVIEW, &CView::OnFilePrintPreview)
    	ON_WM_TIMER()
    	ON_WM_ERASEBKGND()
    END_MESSAGE_MAP()
    
    // CTrajectory1View construction/destruction
    
    CTrajectory1View::CTrajectory1View()
    {
    	// TODO: add construction code here
    	shape = new CShape;
    }
    
    CTrajectory1View::~CTrajectory1View()
    {
    	delete shape;
    }
    
    BOOL CTrajectory1View::PreCreateWindow(CREATESTRUCT& cs)
    {
    	// TODO: Modify the Window class or styles here by modifying
    	//  the CREATESTRUCT cs
    
    	return CView::PreCreateWindow(cs);
    }
    
    // CTrajectory1View drawing
    
    void CTrajectory1View::OnDraw(CDC* pDC)
    {
    	CTrajectory1Doc* pDoc = GetDocument();
    	ASSERT_VALID(pDoc);
    	if (!pDoc)
    		return;
    
    	// TODO: add draw code for native data here
    	CPen   penYellow;
    	CBrush brsBlack, brsFuchsia;
    	CRect rctClient;
    
    	brsBlack.CreateSolidBrush(RGB(0, 0, 0));
    	brsFuchsia.CreateSolidBrush(RGB(255, 0, 255));
    	penYellow.CreatePen(PS_SOLID, 1, RGB(255, 255, 0));
    
    	GetWindowRect(&rctClient);
    
    	CBrush *pOldBrush = pDC->SelectObject(&brsBlack);
    	pDC->Rectangle(0, 0, rctClient.Width(), rctClient.Height());
    
    	shape->Draw(pDC);
    
    	pDC->SelectObject(pOldBrush);
    }
    
    . . .
    
    // CTrajectory1View message handlers
    
    void CTrajectory1View::OnTimer(UINT_PTR nIDEvent)
    {
    	// TODO: Add your message handler code here and/or call default
    	CRect rctClient;
    
    	GetWindowRect(&rctClient);
    	shape->Move(rctClient);
    
    	Invalidate();
    
    	if( shape->MovingRight == TRUE )
    		shape->x++;
    	else
    		shape->x--;
    
    	CView::OnTimer(nIDEvent);
    }
    
    void CTrajectory1View::OnInitialUpdate()
    {
    	CView::OnInitialUpdate();
    
    	SetTimer(IDT_MOVE, 10, NULL);
    	// TODO: Add your specialized code here and/or call the base class
    }
    
    BOOL CTrajectory1View::OnEraseBkgnd(CDC* pDC)
    {
    	// TODO: Add your message handler code here and/or call default
    
    	return TRUE; // CView::OnEraseBkgnd(pDC);
    }
  8. Execute the application and notice that it produces the same result
 

Home Copyright © 2006 FunctionX, Inc. Next