home *** CD-ROM | disk | FTP | other *** search
/ Tricks of the Windows Gam…ming Gurus (2nd Edition) / Disc2.iso / msdn_vcb / samples / vc98 / atl / atltangram / atlgdiworld / atlgdiworldimpl.cpp < prev    next >
C/C++ Source or Header  |  1998-03-26  |  13KB  |  482 lines

  1. // AtlGdiWorldImpl.cpp : Implementation of CAtlGdiWorld
  2. //
  3. // This is a part of the Active Template Library.
  4. // Copyright (C) 1996-1998 Microsoft Corporation
  5. // All rights reserved.
  6. //
  7. // This source code is only intended as a supplement to the
  8. // Active Template Library Reference and related
  9. // electronic documentation provided with the library.
  10. // See these sources for detailed information regarding the
  11. // Active Template Library product.
  12.  
  13. #include "stdafx.h"
  14. #include "ATLGdiWorld.h"
  15. #include "AtlGdiWorldImpl.h"
  16. #include <math.h>
  17. #include <windows.h>
  18. #include <math.h>
  19. #include <comcat.h>
  20. #include "AtlModel.h"
  21. #include "AtlModel_i.c"
  22. #include "AtlEvent_i.h"
  23. #include "ATLTangramCanvas.h"
  24. #include "ATLTangramModel.h"
  25. #include "ATLTangramCanvas_i.c"
  26. #include "ATLGdiWorld.h"
  27. #include <initguid.h>
  28. #include "AtlWorldCat.h"
  29.  
  30. /////////////////////////////////////////////////////////////////////////////
  31. // CAtlGdiWorld
  32.  
  33. ///////////////////////////////////////////////////////////
  34. //
  35. //  Destructor
  36. //
  37. CAtlGdiWorld::~CAtlGdiWorld()
  38. {
  39. #pragma warning(disable:4786)
  40.     CVisualList::iterator p ;
  41.     for(p = m_VisualList.begin() ; p != m_VisualList.end() ; p++)
  42.     {
  43.         (*p)->ReleaseConnectionPoint();
  44.         (*p)->Release() ;
  45.     }
  46. #pragma warning(default:4786)
  47.  
  48.     ASSERT(m_pCanvasUnknown == NULL) ;
  49.     ASSERT(m_pCanvas == NULL) ;
  50.  
  51. }
  52.  
  53.  
  54. ///////////////////////////////////////////////////////////
  55. //
  56. //  SpecialReg -    Used to register and unregister the component
  57. //                      categories.
  58. //
  59. void CAtlGdiWorld::SpecialReg(BOOL bRegister)
  60. {
  61.     // Create the standard COM Category Manager
  62.     ICatRegister* pICatRegister = NULL ;
  63.     HRESULT hr = ::CoCreateInstance(    CLSID_StdComponentCategoriesMgr,
  64.                                         NULL, CLSCTX_ALL, IID_ICatRegister,
  65.                                         (void**)&pICatRegister) ;
  66.     if (FAILED(hr))
  67.     {
  68.         ErrorMessage("Could not create the ComCat component.", hr);
  69.         return ;
  70.     }
  71.  
  72.     // Fill in a Category Info structure.
  73.     CATEGORYINFO CatInfo ;
  74.     CatInfo.catid               = CATID_AtlTangramWorldCategory ;
  75.     CatInfo.lcid                = LOCALE_SYSTEM_DEFAULT ;
  76.     wcscpy(CatInfo.szDescription, L"Atl Tangram World Component Category" ) ;
  77.  
  78.     // Array of Categories
  79.     int cIDs = 1 ;
  80.     CATID IDs[1] ;
  81.     IDs[0] = CATID_AtlTangramWorldCategory ;
  82.  
  83.     // Register or Unregister
  84.     if (bRegister)
  85.     {
  86.         // Register the component category.
  87.         hr = pICatRegister->RegisterCategories(1, &CatInfo) ;
  88.         ASSERT_HRESULT(hr) ;
  89.  
  90.         // Register this component as a member of TangramWorldCategory
  91.         hr = pICatRegister->RegisterClassImplCategories(    CLSID_AtlGdiWorld,
  92.                                                             cIDs, IDs ) ;
  93.         ASSERT_HRESULT(hr) ;
  94.     }
  95.     else
  96.     {
  97.         // Unregister the component from its categories.
  98.         hr = pICatRegister->UnRegisterClassImplCategories(  CLSID_AtlGdiWorld,
  99.                                                             cIDs, IDs);
  100.         ASSERT_HRESULT(hr) ;
  101.  
  102.         // --- Check to see if there are other components in this category before we delete it. ---
  103.  
  104.         // Get the ICatInformation interface.
  105.         ICatInformation* pICatInformation = NULL ;
  106.         hr = pICatRegister->QueryInterface(IID_ICatInformation, (void**)&pICatInformation) ;
  107.         ASSERT_HRESULT(hr) ;
  108.  
  109.         // Get the IEnumCLSID interface.
  110.         IEnumCLSID* pIEnumCLSID = NULL ;
  111.         hr = pICatInformation->EnumClassesOfCategories(cIDs, IDs, 0, NULL, &pIEnumCLSID) ;
  112.         ASSERT_HRESULT(hr) ;
  113.  
  114.         // Get the next CLSID in the list.
  115.         CLSID clsid ;
  116.         hr = pIEnumCLSID->Next(1, &clsid, NULL) ;
  117.         ASSERT_HRESULT(hr) ;
  118.  
  119.         // If hr is S_FALSE, there no components are in this category, so remove it.
  120.         if (hr == S_FALSE)
  121.         {
  122.             // Unregister the component category
  123.             hr = pICatRegister->UnRegisterCategories(cIDs, IDs) ;
  124.             ASSERT_HRESULT(hr) ;
  125.         }
  126.  
  127.         // CleanUp
  128.         pIEnumCLSID->Release() ;
  129.         pICatInformation->Release() ;
  130.     }
  131.  
  132.     if (pICatRegister)
  133.     {
  134.         pICatRegister->Release() ;
  135.     }
  136. }
  137.  
  138.  
  139. ///////////////////////////////////////////////////////////
  140. //
  141. //                  ITangramWorld Implementation
  142. //
  143. ///////////////////////////////////////////////////////////
  144. //
  145. //  Initialize
  146. //
  147. HRESULT CAtlGdiWorld::Initialize(HWND hWnd, double modelWidth, double modelHeight)
  148. {
  149.     // Preconditions
  150.     if (m_pCanvas != NULL)
  151.     {
  152.         ASSERT(m_pCanvas == NULL) ;
  153.         return E_FAIL ;
  154.     }
  155.  
  156.     if ((hWnd == NULL) || (!::IsWindow(hWnd)) ||
  157.         (modelWidth == 0.0) || (modelHeight == 0.0))
  158.     {
  159.         ASSERT(hWnd != NULL) ;
  160.         ASSERT(::IsWindow(hWnd)) ;
  161.         ASSERT( m_pCanvas != NULL) ;
  162.         ASSERT( (modelWidth != 0.0) && (modelHeight != 0.0)) ;
  163.         return E_INVALIDARG ;
  164.     }
  165.  
  166.     // ----- Aggreate the canvas object to handle Canvas messages ------
  167.  
  168.     // Create the canvas on which the world draws.
  169.     HRESULT hr = ::CoCreateInstance(    CLSID_AtlTangramCanvas,
  170.                                         GetControllingUnknown(),
  171.                                         CLSCTX_INPROC_SERVER,
  172.                                         IID_IUnknown, (void**)&m_pCanvasUnknown) ;
  173.     if (FAILED(hr))
  174.     {
  175.         ATLTRACE("Could not aggregate the canvas component.", hr) ;
  176.         return E_FAIL ;
  177.     }
  178.     TCHAR buf[128];
  179.     wsprintf(buf,_T("World: m_dwRef = %d\n"), m_dwRef);
  180.     ATLTRACE(buf);
  181.  
  182.     //------ Get the ITangramCanvas interface -----
  183.  
  184.     // Query for the ITangramCanvas interface.
  185.     hr = m_pCanvasUnknown->QueryInterface(IID_IAtlTangramCanvas, (void**)&m_pCanvas) ;
  186.     if (FAILED(hr))
  187.     {
  188.         ATLTRACE("Could not get required interface from Canvas.", hr) ;
  189.         m_pCanvas->Release() ;
  190.         m_pCanvas = NULL ;
  191.         return E_FAIL ;
  192.     }
  193.  
  194.  
  195.     // Release the AddRef placed on the outer object by the QI call above.
  196.     GetControllingUnknown()->Release() ;
  197.     wsprintf(buf,_T("World: m_dwRef = %d\n"), m_dwRef);
  198.     ATLTRACE(buf);
  199.  
  200.         // ----- Initialize the canvas component. -----
  201.     RECT rectClient ;
  202.     ::GetClientRect(hWnd, &rectClient) ;
  203.  
  204.     int widthClient = rectClient.right - rectClient.left ;
  205.     int heightClient = rectClient.bottom - rectClient.top ;
  206.     hr = m_pCanvas->Initialize(hWnd, widthClient, heightClient) ;
  207.     ASSERT_HRESULT(hr) ;
  208.  
  209.     // ----- More Initializtion -----
  210.  
  211.     // Paint the background of the canvas black.
  212.     AddUpdateRect(rectClient);
  213.     Animate() ;
  214.  
  215.     //
  216.     // Map the model world (modelWidth x modelHeight)
  217.     // (0,0) in the lower left corner to the physical screen (m_sizeDevice)
  218.     // (0,0) in the upper left corner.
  219.     // Always set up an isotropic world where 1 x model unit = 1 y model unit.
  220.     //
  221.  
  222.     TangramSize2d sizedDevice ;
  223.     sizedDevice.cx = static_cast<double>(widthClient) ;
  224.     sizedDevice.cy = static_cast<double>(heightClient) ;
  225.  
  226.     m_sizedScale.cx = sizedDevice.cx / modelWidth;
  227.     m_sizedScale.cy = sizedDevice.cy / modelHeight ;
  228.     if (m_sizedScale.cx > m_sizedScale.cy)
  229.     {
  230.         m_sizedScale.cx = m_sizedScale.cy ;
  231.         dModelUnits = modelHeight * 100000 ;
  232.         dDeviceUnits = sizedDevice.cy ;
  233.     }
  234.     else
  235.     {
  236.         m_sizedScale.cy = m_sizedScale.cx ;
  237.         dModelUnits = modelWidth * 100000 ;
  238.         dDeviceUnits = sizedDevice.cx ;
  239.     }
  240.     m_sizedScale.cy = -m_sizedScale.cy ; // Negative scaling make Y axis point up.
  241.  
  242.     // Move origin to lower left corner of screen.
  243.     m_ptdDeviceOrg.x = 0.0 ;
  244.     m_ptdDeviceOrg.y = sizedDevice.cy ;
  245.  
  246.     return S_OK ;
  247. }
  248.  
  249. ///////////////////////////////////////////////////////////
  250. //
  251. // DeviceToModel -- Do not use with size parameters because addin the org...
  252. //
  253. HRESULT CAtlGdiWorld::DeviceToModel(POINT ptIn, TangramPoint2d* pptdOut)
  254. {
  255.     if (!IsValidAddress(pptdOut, sizeof(TangramPoint2d), TRUE))
  256.     {
  257.         ASSERT(0) ;
  258.         return E_POINTER ;
  259.     }
  260.  
  261.     pptdOut->x = (static_cast<double>(ptIn.x) - m_ptdDeviceOrg.x) / m_sizedScale.cx ;
  262.     pptdOut->y = (static_cast<double>(ptIn.y) - m_ptdDeviceOrg.y) / m_sizedScale.cy ;
  263.  
  264.     return S_OK ;
  265. }
  266.  
  267.  
  268. ///////////////////////////////////////////////////////////
  269. //
  270. // VisualFromPoint - Finds the visual which contains POINT pt.
  271. //                          Returns the requested IID for the visual.
  272. //
  273. //  Results:
  274. //          S_OK    -   Found a Visual.
  275. //          S_FALSE -   No visual containing this point.
  276. //                      *ppITangramVisual will be NULL.
  277. //
  278. HRESULT CAtlGdiWorld::VisualFromPoint(  POINT pt,
  279.                                     REFIID iid,
  280.                                     IUnknown** ppITangramVisual)
  281. {
  282.     // Preconditions.
  283.     if (!IsValidInterfaceOutParam(ppITangramVisual))
  284.     {
  285.         ASSERT(0) ;
  286.         return E_POINTER ;
  287.     }
  288.  
  289.     // Initialize out parameter.
  290.     *ppITangramVisual = NULL ;
  291.  
  292.     // Search the list in reverse order because the
  293.     // most recently selected and last painted object is
  294.     // on the end.
  295. #pragma warning(disable:4786)
  296.     CVisualList::reverse_iterator ppVisual ;
  297.     for(ppVisual = m_VisualList.rbegin() ; ppVisual != m_VisualList.rend() ; ppVisual++)
  298.     {
  299.         HRESULT hr = (*ppVisual)->IsPtIn(pt) ;
  300.         ASSERT(SUCCEEDED(hr)) ;
  301.         if (hr == S_OK)
  302.         {
  303.             return (*ppVisual)->QueryInterface(iid, (void**)ppITangramVisual) ;
  304.         }
  305.     }
  306. #pragma warning(default:4786)
  307.     return S_FALSE ;
  308. }
  309.  
  310. ///////////////////////////////////////////////////////////
  311. //
  312. // ITangramWorld::CreateVisualForModel Implementation
  313. //
  314. HRESULT CAtlGdiWorld::CreateVisualForModel(IATLTangramModel* pModel)
  315. {
  316.     // Preconditions.
  317.     if (!IsValidInterface(pModel))
  318.     {
  319.         ASSERT(0) ;
  320.         return E_POINTER ;
  321.     }
  322.  
  323.     // Create the visual for this model.
  324.     IAtlTangramGdiVisual* pVisual = NULL ;
  325.     HRESULT hr = ::CoCreateInstance(    CLSID_AtlTangramGdiVisual,
  326.                                         NULL,
  327.                                         CLSCTX_INPROC_SERVER,
  328.                                         IID_IAtlTangramGdiVisual,
  329.                                         (void**)&pVisual) ;
  330.     if (FAILED(hr))
  331.     {
  332.         ATLTRACE("Could not create visual for model.", hr) ;
  333.         return  E_FAIL ;
  334.     }
  335.  
  336.     // Initialize visual.
  337.     hr = pVisual->Initialize(pModel, this) ;
  338.     ASSERT_HRESULT(hr) ;
  339.  
  340.     // Add visual to list.
  341.     m_VisualList.push_back(pVisual) ;
  342.  
  343.     return S_OK ;
  344. }
  345.  
  346.  
  347. ///////////////////////////////////////////////////////////
  348. //
  349. //  SelectVisual
  350. //
  351. HRESULT CAtlGdiWorld::SelectVisual(IAtlTangramVisual* pSelectedVisual, BOOL bSelect)
  352. {
  353.     // Precondition
  354.     if (!IsValidInterface(pSelectedVisual))
  355.     {
  356.         ASSERT(0) ;
  357.         return E_POINTER ;
  358.     }
  359.  
  360.     // Move the selected object to the end of the list so that it
  361.     // will paint last.
  362.     pSelectedVisual->SetSelected(bSelect) ;
  363.  
  364.     if (bSelect)
  365.     {
  366.         // Cycle through the array looking for pSelectedVisual.
  367. #pragma warning(disable:4786)
  368.         CVisualList::iterator ppV ;
  369.         for(ppV = m_VisualList.begin() ; ppV != m_VisualList.end() ; ppV++)
  370.         {
  371.             // Must compare IUnknown pointers.
  372.             if (::InterfacesAreOnSameComponent(*ppV, pSelectedVisual))
  373.             {
  374.                 // Move to the back of the list.
  375.                 m_VisualList.push_back(*ppV) ;
  376.                 // Remove other entry.
  377.                 m_VisualList.erase(ppV) ;
  378.  
  379.                 return S_OK ;
  380.             }
  381.         }
  382. #pragma warning(default:4786)
  383.         return E_FAIL;
  384.     }
  385.     return S_OK ;
  386. }
  387.  
  388. ///////////////////////////////////////////////////////////
  389. //
  390. //  Animate
  391. //
  392. HRESULT CAtlGdiWorld::Animate()
  393. {
  394.     HDC hdc = NULL ;
  395.     HRESULT hr = m_pCanvas->GetHDC(&hdc) ;
  396.     ASSERT_HRESULT(hr) ;
  397.     ASSERT(hdc != NULL) ;
  398.  
  399.     // Now we know the bounding rectangle of the changed area
  400.     // The bounding rectangle can get big if pieces on the opposite side of the screen change.
  401.  
  402.     // Clip the area outside the update area. Some objects we will redraw are only partially in the
  403.     // update region. Clip this region so they don't affect the areas outside.
  404.     HRGN rgnClip = CreateRectRgnIndirect(&m_rectUpdate) ;
  405.     ::ExtSelectClipRgn(hdc, rgnClip, RGN_COPY) ;
  406.     ::DeleteObject(rgnClip) ;
  407.  
  408.     // Erase the update region.
  409.     RECT rectFill ;
  410.     CopyRect(&rectFill, &m_rectUpdate) ;
  411.     rectFill.right++ ; // FillRect doesn't fill bottom and right edges.
  412.     rectFill.bottom++ ;
  413.     ::FillRect(hdc, &rectFill, (HBRUSH)::GetStockObject(BLACK_BRUSH)) ;
  414.  
  415.     // Who has been indirectly affected by the changes.
  416. #pragma warning(disable:4786)
  417.     CVisualList::iterator ppVisual ;
  418.     for(ppVisual = m_VisualList.begin() ; ppVisual != m_VisualList.end() ; ppVisual++)
  419.     {
  420.         IAtlTangramGdiVisual* pVisual = *ppVisual ;
  421.         // See if visual overlaps the update area.
  422.         RECT rectVisual ;
  423.         hr = pVisual->GetBoundingRect(&rectVisual) ;
  424.         ASSERT_HRESULT(hr) ;
  425.  
  426.         RECT rectDummy ;
  427.         if (::IntersectRect(&rectDummy, &m_rectUpdate, &rectVisual))
  428.         {
  429.             // Visual is in the update area.
  430.             hr = pVisual->DrawOn(m_pCanvas) ;
  431.             ASSERT_HRESULT(hr) ;
  432.         }
  433.     }
  434. #pragma warning(default:4786)
  435.  
  436.     // Clipping region is no longer needed.
  437.     ::SelectClipRgn(hdc, NULL) ;
  438.  
  439.     // Move it to the screen.
  440.     hr = m_pCanvas->Update(m_rectUpdate) ;
  441.     ASSERT_HRESULT(hr) ;
  442.  
  443.     // Cleanup
  444.     ::SetRectEmpty(&m_rectUpdate) ;
  445.  
  446.     return S_OK ;
  447. }
  448.  
  449. ///////////////////////////////////////////////////////////
  450. //
  451. //                  ITangramGdiWorld
  452. //
  453. ///////////////////////////////////////////////////////////
  454. //
  455. //  ModelToDevice
  456. //
  457. HRESULT CAtlGdiWorld::ModelToDevice(TangramPoint2d ptdIn, POINT* pptOut)
  458. {
  459.     //Precondition
  460.     if (!IsValidAddress(pptOut, sizeof(TangramPoint2d), TRUE))
  461.     {
  462.         ASSERT(0) ;
  463.         return E_POINTER ;
  464.     }
  465.  
  466.     pptOut->x = static_cast<int>(floor(ptdIn.x * 100000 *  dDeviceUnits / dModelUnits + m_ptdDeviceOrg.x + 0.5)) ;
  467.     pptOut->y = static_cast<int>(floor(ptdIn.y * 100000 * -dDeviceUnits / dModelUnits + m_ptdDeviceOrg.y + 0.5)) ;
  468.  
  469.     return S_OK ;
  470. }
  471.  
  472. ///////////////////////////////////////////////////////////
  473. //
  474. //  AddUpdateRect
  475. //
  476. HRESULT CAtlGdiWorld::AddUpdateRect(RECT rectUpdate)
  477. {
  478.     ::UnionRect(&m_rectUpdate, &m_rectUpdate, &rectUpdate) ;
  479.  
  480.     return S_OK ;
  481. }
  482.