OSDN Git Service

ゲーム状態取得処理の並列化。
authorMandhelingFreak <mandheling30-freak@yahoo.co.jp>
Sun, 9 Feb 2014 15:09:19 +0000 (00:09 +0900)
committerMandhelingFreak <mandheling30-freak@yahoo.co.jp>
Mon, 17 Feb 2014 18:53:29 +0000 (03:53 +0900)
noncopyable追加。
縮小画像キャッシュ機構を一端削除。
手動でデバッグ自動航行モード切り替えるのがメンドイからメニューに追加。
スケール変更時にデバッグ用の描画時間計測をリセットするように変更。
キー入力を拾っていた箇所をアクセラレーターに置き換え。
等倍に戻すメニュー追加。

23 files changed:
GVONavish/GVONavish/GVOConfig.h
GVONavish/GVONavish/GVOGameProcess.cpp
GVONavish/GVONavish/GVOGameProcess.h
GVONavish/GVONavish/GVOGameStatus.h [new file with mode: 0644]
GVONavish/GVONavish/GVOImage.cpp [new file with mode: 0644]
GVONavish/GVONavish/GVOImage.h
GVONavish/GVONavish/GVONavish.cpp
GVONavish/GVONavish/GVONavish.h
GVONavish/GVONavish/GVONavish.rc
GVONavish/GVONavish/GVONavish.vcxproj
GVONavish/GVONavish/GVONavish.vcxproj.filters
GVONavish/GVONavish/GVONoncopyable.h [new file with mode: 0644]
GVONavish/GVONavish/GVORenderer.cpp [new file with mode: 0644]
GVONavish/GVONavish/GVORenderer.h [new file with mode: 0644]
GVONavish/GVONavish/GVOShip.cpp
GVONavish/GVONavish/GVOShip.h
GVONavish/GVONavish/GVOSpeedMeter.h
GVONavish/GVONavish/GVOSurveyCoordExtractor.h
GVONavish/GVONavish/GVOVector.h
GVONavish/GVONavish/GVOWorldMap.cpp
GVONavish/GVONavish/GVOWorldMap.h
GVONavish/GVONavish/Resource.h
GVONavish/GVONavish/stdafx.h

index 6338fcb..ccd2909 100644 (file)
@@ -16,8 +16,6 @@ private:
        const LPCWSTR m_debugSectionName = L"debug";
 #endif
 
-public:
-       const LPCWSTR m_defaultMapFileName = L"map.png";
 
 public:
        std::wstring m_mapFileName;
@@ -38,6 +36,7 @@ public:
 
        GVOConfig( LPCWSTR fileName )
                : m_fileName( g_makeFullPath( fileName ) )
+               , m_mapFileName( L"map.png" )
                , m_pollingInterval( 1000 )
                , m_windowPos( defaultPosition() )
                , m_windowSize( defaultSize() )
@@ -99,9 +98,9 @@ public:
                std::vector<wchar_t> buf(4096);
 
                section = m_coreSectionName;
-               ::GetPrivateProfileStringW(section, L"map", m_defaultMapFileName, &buf[0], buf.size(), fn);
+               ::GetPrivateProfileStringW(section, L"map", m_mapFileName.c_str(), &buf[0], buf.size(), fn);
                m_mapFileName = &buf[0];
-               m_pollingInterval = ::GetPrivateProfileInt( section, L"polingInterval", m_pollingInterval, fn );
+               m_pollingInterval = ::GetPrivateProfileInt( section, L"pollingInterval", m_pollingInterval, fn );
                m_traceShipPositionEnabled = ::GetPrivateProfileInt( section, L"traceEnabled", m_traceShipPositionEnabled, fn ) != 0;
                m_speedMeterEnabled = ::GetPrivateProfileInt( section, L"speedMeterEnabled", m_speedMeterEnabled, fn ) != 0;
                m_shipVectorLineEnabled = ::GetPrivateProfileInt( section, L"shipVectorLineEnabled", m_shipVectorLineEnabled, fn ) != 0;
index 2fda993..36ade6f 100644 (file)
@@ -1,4 +1,5 @@
 #include "stdafx.h"
+#include <process.h>
 #include "GVONavish.h"
 #include "GVOGameProcess.h"
 #include "GVOWorldMap.h"
@@ -8,11 +9,25 @@
 // \89æ\91\9c\89ð\90Í\83f\83o\83b\83O\97p\81BGVOGameProcess
 //#define GVO_ANALYZE_DEBUG
 
+extern HWND g_hwndMain;
+extern HDC g_hdcMain;
+
 namespace {
 #ifdef GVO_ANALYZE_DEBUG
        LPCWSTR const k_debugImageFileName = L"..\\debug.png";
 #endif
 
+#ifndef NDEBUG
+       // \83f\83o\83b\83O\97p\8e©\93®\8dq\8ds\95Ï\90\94
+       static double s_xDebugAutoCruise;
+       static double s_yDebugAutoCruise;
+       static double s_debugAutoCruiseAngle = 0;
+       static bool s_debugAutoCruiseEnabled = false;
+       static double s_debugAutoCruiseVelocity = 0;
+       static uint32_t s_debugAutoCruiseTurnInterval;
+       static double s_debugAutoCruiseTurnAngle;
+#endif
+
        LPWSTR const k_gvoWindowClassName = L"Greate Voyages Online Game MainFrame";
        LPWSTR const k_gvoWindowCaption = L"\91å\8dq\8aC\8e\9e\91ã Online";
 
@@ -32,13 +47,55 @@ void GVOGameProcess::clear()
 }
 
 
-void GVOGameProcess::setConfig( const GVOConfig& config )
+void GVOGameProcess::setup( const GVOConfig& config )
 {
        m_surveyCoord = config.m_initialSurveyCoord;
+       m_ship.setInitialSurveyCoord( config.m_initialSurveyCoord );
+       m_pollingInterval = config.m_pollingInterval;
+#ifndef NDEBUG
+       s_xDebugAutoCruise = config.m_initialSurveyCoord.x;
+       s_yDebugAutoCruise = config.m_initialSurveyCoord.y;
+       s_debugAutoCruiseEnabled = config.m_debugAutoCruiseEnabled;
+       s_debugAutoCruiseVelocity = config.m_debugAutoCruiseVelocity;
+       s_debugAutoCruiseTurnInterval = config.m_debugAutoCruiseTurnInterval;
+       s_debugAutoCruiseTurnAngle = config.m_debugAutoCruiseTurnAngle;
+#endif
+
+       m_pollingTimerEventID = ::timeSetEvent( m_pollingInterval, 1, LPTIMECALLBACK( m_pollingTimerEvent ), 0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET );
+       m_threadQuitSignal = ::CreateEvent( NULL, TRUE, FALSE, NULL );
+       m_workerThread = (HANDLE)::_beginthreadex( NULL, 0, threadMainThunk, this, 0, NULL );
+}
+
+
+void GVOGameProcess::teardown()
+{
+       if ( m_workerThread ) {
+               ::SetEvent( m_threadQuitSignal );
+               ::WaitForSingleObject( m_workerThread, INFINITE );
+               ::CloseHandle( m_workerThread );
+               ::CloseHandle( m_threadQuitSignal );
+               m_threadQuitSignal = NULL;
+               m_workerThread = NULL;
+       }
+       if ( m_pollingTimerEventID ) {
+               ::timeKillEvent( m_pollingTimerEventID );
+               m_pollingTimerEventID = 0;
+       }
+}
+
+
+#ifndef NDEBUG
+void GVOGameProcess::enableDebugAutoCruise( bool enabled )
+{
+       s_debugAutoCruiseEnabled = enabled;
 }
+#endif
 
 bool GVOGameProcess::updateState()
 {
+       GVOGameStatus status;
+
+
 #ifdef GVO_ANALYZE_DEBUG
        {
                static bool done = false;
@@ -70,6 +127,66 @@ bool GVOGameProcess::updateState()
                return true;
        }
 #endif
+
+       if ( s_debugAutoCruiseEnabled ) {
+               static bool isRandInitialized = false;
+               if ( !isRandInitialized ) {
+                       srand( ::timeGetTime() );
+                       isRandInitialized = true;
+               }
+
+               const double rad = ((s_debugAutoCruiseAngle)* M_PI) / 180;
+               const double vx = ::cos( rad );
+               const double vy = ::sin( rad );
+
+               s_xDebugAutoCruise += vx * s_debugAutoCruiseVelocity;
+               s_yDebugAutoCruise += vy * s_debugAutoCruiseVelocity;
+
+               static DWORD tick = ::timeGetTime();
+               static DWORD count = 0;
+               if ( (tick + s_debugAutoCruiseTurnInterval) < ::timeGetTime() ) {
+                       if ( 10 < (++count) ) {
+                               count = 0;
+                               s_debugAutoCruiseAngle += 90 + (LONG( rand() / double( RAND_MAX ) * 90 ) & ~0x1);
+                       }
+                       else {
+                               s_debugAutoCruiseAngle += (rand() & 1) ? s_debugAutoCruiseTurnAngle : -s_debugAutoCruiseTurnAngle;
+                       }
+                       tick = ::timeGetTime();
+               }
+               s_debugAutoCruiseAngle = fmod( ::fabs( s_debugAutoCruiseAngle ), 360 );
+
+               if ( s_xDebugAutoCruise < 0 ) {
+                       s_xDebugAutoCruise += k_worldWidth;
+               }
+               if ( s_yDebugAutoCruise < 0 ) {
+                       s_yDebugAutoCruise += k_worldHeight;
+               }
+               s_xDebugAutoCruise = fmod( s_xDebugAutoCruise, (double)k_worldWidth );
+               s_yDebugAutoCruise = fmod( s_yDebugAutoCruise, (double)k_worldHeight );
+
+               //// \92n\90}\82ð\8c×\82®\8f\88\97\9d\82Ì\8am\94F\97p\83f\83o\83b\83O\83R\81[\83h
+               //if ( 100 <= s_xDebugAutoCruise && s_xDebugAutoCruise <= (GVOWorldMap::k_worldWidth - 100) ) {
+               //      s_xDebugAutoCruise = 0;
+               //}
+
+               m_surveyCoord.x = LONG( s_xDebugAutoCruise );
+               m_surveyCoord.y = LONG( s_yDebugAutoCruise );
+               uint32_t timeStamp = ::timeGetTime();
+               m_speedMeter.updateVelocity( m_ship.velocity(), timeStamp );
+               m_ship.updateWithSurveyCoord( m_surveyCoord, timeStamp );
+
+               status.m_surveyCoord = m_surveyCoord;
+               status.m_shipVector = m_ship.vector();
+               status.m_shipVelocity = m_speedMeter.velocity();
+
+               ::EnterCriticalSection( &m_lock );
+               m_statusArray.push_back( status );
+               ::SetEvent( m_dataReadyEvent );
+               ::LeaveCriticalSection( &m_lock );
+               return true;
+       }
+
        if ( !m_window ) {
                m_window = ::FindWindow( k_gvoWindowClassName, k_gvoWindowCaption );
        }
@@ -99,11 +216,72 @@ bool GVOGameProcess::updateState()
                if ( !updateSurveyCoord() ) {
                        return false;
                }
+
+               m_speedMeter.updateVelocity( m_ship.velocity(), m_timeStamp );
+               m_ship.updateWithSurveyCoord( m_surveyCoord, m_timeStamp );
+
+               status.m_surveyCoord = m_surveyCoord;
+               status.m_shipVector = m_ship.vector();
+               status.m_shipVelocity = m_speedMeter.velocity();
+
+               ::EnterCriticalSection( &m_lock );
+               m_statusArray.push_back( status );
+               ::SetEvent( m_dataReadyEvent );
+               ::LeaveCriticalSection( &m_lock );
+
                return true;
        }
        return false;
 }
 
+std::vector<GVOGameStatus> GVOGameProcess::getState()
+{
+       std::vector<GVOGameStatus> statusArray;
+
+       ::EnterCriticalSection( &m_lock );
+       m_statusArray.swap( statusArray );
+       ::ResetEvent( m_dataReadyEvent );
+       ::LeaveCriticalSection( &m_lock );
+
+       return statusArray;
+}
+
+
+UINT CALLBACK GVOGameProcess::threadMainThunk( LPVOID arg )
+{
+       GVOGameProcess *self = reinterpret_cast<GVOGameProcess *>(arg);
+       self->threadMain();
+       return 0;
+}
+
+
+void GVOGameProcess::threadMain()
+{
+       std::vector<HANDLE> signals;
+       signals.push_back( m_threadQuitSignal );
+       signals.push_back( m_pollingTimerEvent );
+
+       for ( ; ; ) {
+               const DWORD ret = ::WaitForMultipleObjects( signals.size(), &signals[0], FALSE, INFINITE );
+               if ( signals.size() <= ret ) {
+                       exit( -1 );
+               }
+               HANDLE const active = signals[ret];
+               if ( active == m_threadQuitSignal ) {
+                       break;
+               }
+
+               if ( active == m_pollingTimerEvent ) {
+                       ::ResetEvent( m_pollingTimerEvent );
+                       
+                       updateState();
+
+                       continue;
+               }
+       }
+}
+
+
 void GVOGameProcess::grabImage( HDC hdc, const POINT& offset, const SIZE& size )
 {
        if ( !m_surveyCoordImage.bitmapHandle() ) {
@@ -129,6 +307,7 @@ void GVOGameProcess::grabImage( HDC hdc, const POINT& offset, const SIZE& size )
        ::DeleteDC( hdcMem );
 }
 
+
 bool GVOGameProcess::updateSurveyCoord()
 {
        bool succeeded = false;
index b26a537..c493d76 100644 (file)
@@ -1,14 +1,17 @@
 #pragma once
+#include "GVONoncopyable.h"
 #include "GVOImage.h"
 #include "GVOConfig.h"
+#include "GVOSpeedMeter.h"
+#include "GVOShip.h"
+
+#include "GVOGameStatus.h"
+
 
 
 
 //!@brief \91å\8dq\8aC\8e\9e\91ãOnline\83v\83\8d\83Z\83X
-class GVOGameProcess {
-private:
-       GVOGameProcess( const GVOGameProcess& );
-       GVOGameProcess& operator=(const GVOGameProcess&);
+class GVOGameProcess : private GVONoncopyable {
 private:
        HANDLE m_process;
        HWND m_window;
@@ -17,17 +20,39 @@ private:
        POINT m_surveyCoord;
        DWORD m_timeStamp;
 
+       GVOSpeedMeter m_speedMeter;
+       GVOShip m_ship;
+
+       uint32_t m_pollingInterval;
+       HANDLE m_pollingTimerEvent;
+       UINT m_pollingTimerEventID;
+
+       HANDLE m_workerThread;
+       HANDLE m_threadQuitSignal;
+       HANDLE m_dataReadyEvent;
+       CRITICAL_SECTION m_lock;
+
+       std::vector<GVOGameStatus> m_statusArray;
 public:
        GVOGameProcess() :
                m_process( NULL ),
                m_window( NULL ),
                m_surveyCoord(),
-               m_timeStamp()
+               m_timeStamp(),
+               m_pollingInterval(),
+               m_pollingTimerEvent( ::CreateEvent( NULL, TRUE, TRUE, NULL ) ),
+               m_pollingTimerEventID(),
+               m_workerThread(),
+               m_dataReadyEvent( ::CreateEvent( NULL, TRUE, FALSE, NULL ) )
        {
+               ::InitializeCriticalSection( &m_lock );
        }
        virtual ~GVOGameProcess()
        {
                clear();
+               ::CloseHandle( m_dataReadyEvent );
+               ::CloseHandle( m_pollingTimerEvent );
+               ::DeleteCriticalSection( &m_lock );
        }
 
        HANDLE processHandle() const
@@ -39,18 +64,16 @@ public:
        void clear();
 
        //!@brief \90Ý\92è\8fî\95ñ\82Å\8f\89\8aú\89»\82·\82é
-       void setConfig( const GVOConfig& config );
-
-       //!@brief \83Q\81[\83\80\89æ\96Ê\82ð\93Ç\82Ý\8eæ\82é
-       //!@retval true \93Ç\82Ý\8eæ\82è\90¬\8c÷\81B
-       //!@retval false \93Ç\82Ý\8eæ\82è\8e¸\94s\81B\82¨\82»\82ç\82­\83Q\81[\83\80\82ª\8bN\93®\82µ\82Ä\82¢\82È\82¢\81B
-       bool updateState();
+       void setup( const GVOConfig& config );
+       void teardown();
+#ifndef NDEBUG
+       void enableDebugAutoCruise( bool enabled );
+#endif
 
-       //!@brief \91ª\97Ê\8dÀ\95W
-       POINT surveyCoord() const
-       {
-               return m_surveyCoord;
-       }
+       //!@brief \83Q\81[\83\80\89æ\96Ê\82ð\93Ç\82Ý\8eæ\82Á\82½\8c\8b\89Ê\82ð\8eó\82¯\8eæ\82é
+       //!@return \93Ç\82Ý\8eæ\82è\90¬\8c÷\8e\9e\82É\82Í\91ª\97Ê\8dÀ\95W\81A\8e©\91D\83x\83N\83g\83\8b\81A\8e©\91D\91¬\93x\82ª\8c\8b\89Ê\82Æ\82µ\82Ä\93n\82³\82ê\82é\81B
+       //!@note \8eÀ\8ds\82·\82é\82Æ\92~\90Ï\82³\82ê\82½\8fó\91Ô\82Í\91S\82Ä\8fÁ\8b\8e\82³\82ê\82é
+       std::vector<GVOGameStatus> getState();
 
        //!@brief \83Q\81[\83\80\89æ\96Ê\93Ç\82Ý\8eæ\82è\90¬\8c÷\8e\9e\82Ì\8e\9e\8aÔ
        DWORD timeStamp() const
@@ -58,12 +81,13 @@ public:
                return m_timeStamp;
        }
 
-#ifndef NDEBUG
-       //!@brief \83f\83o\83b\83O\97p
-       void setSurveyCoord( const POINT& worldCoord )
+       // Wait\97p
+       HANDLE dataReadyEvent() const
        {
-               m_surveyCoord = worldCoord;
+               return m_dataReadyEvent;
        }
+
+#ifndef NDEBUG
        //!@brief \83f\83o\83b\83O\97p\91ª\97Ê\8dÀ\95W\89æ\91\9c
        const GVOImage& surveyCoordImage() const
        {
@@ -72,7 +96,9 @@ public:
 #endif
 
 private:
+       bool updateState();
+       static UINT CALLBACK threadMainThunk( LPVOID arg );
+       void threadMain();
        void grabImage( HDC hdc, const POINT& offset, const SIZE& size );
        bool updateSurveyCoord();
 };
-
diff --git a/GVONavish/GVONavish/GVOGameStatus.h b/GVONavish/GVONavish/GVOGameStatus.h
new file mode 100644 (file)
index 0000000..76e4806
--- /dev/null
@@ -0,0 +1,20 @@
+#pragma once
+#include <cinttypes>
+#include <Windows.h>
+
+#include "GVOVector.h"
+
+
+//!@brief \83Q\81[\83\80\83v\83\8d\83Z\83X\82©\82ç\8eæ\93¾\82µ\82½\8fó\91Ô\82ð\93Z\82ß\82½\83\82\83m
+class GVOGameStatus
+{
+public:
+       POINT m_surveyCoord;            //!<@brief \91ª\97Ê\8dÀ\95W
+       GVOVector m_shipVector;         //!<@brief \8e©\91D\82Ì\8cü\82«
+       double m_shipVelocity;          //!<@brief \8e©\91D\82Ì\91¬\93x(\90\84\91ª\92l\81j
+
+       GVOGameStatus() : m_shipVelocity()
+       {
+       }
+
+};
diff --git a/GVONavish/GVONavish/GVOImage.cpp b/GVONavish/GVONavish/GVOImage.cpp
new file mode 100644 (file)
index 0000000..6899389
--- /dev/null
@@ -0,0 +1,140 @@
+#include "stdafx.h"
+
+// PNG\82Æ\82©\82Ì\93Ç\82Ý\8d\9e\82Ý\97p
+#include <objbase.h>
+#include <gdiplus.h>
+#include <gdiplusbitmap.h>
+
+#include "GVOImage.h"
+
+
+namespace {
+       inline uint32_t s_strideFromWidthAndBitsPerPixel( const uint32_t width, const uint32_t bpp )
+       {
+               uint32_t stride = width * bpp / 8;
+               stride = stride + (4 - stride % 4) % 4;
+               return stride;
+       }
+       inline void s_copyImage24From32( uint8_t * const dst, const uint8_t * const src, const uint32_t width, const uint32_t height)
+       {
+               const uint32_t srcStride = s_strideFromWidthAndBitsPerPixel( width, 32 );
+               const uint32_t dstStride = s_strideFromWidthAndBitsPerPixel( width, 24 );
+
+               const uint8_t * s = src;
+               uint8_t * d = dst;
+
+               for ( uint32_t y = 0; y < height; ++y ) {
+                       s = src + (y * srcStride);
+                       d = dst + (y * dstStride);
+                       for ( uint32_t x = 0; x < width; ++x ) {
+                               *d++ = *s++;
+                               *d++ = *s++;
+                               *d++ = *s++;
+                               ++s;
+                       }
+               }
+       }
+}
+
+
+bool GVOImage::stretchCopy( const GVOImage& src, uint32_t width, uint32_t height )
+{
+       if ( !createDIBImage( width, height ) ) {
+               return false;
+       }
+
+       HDC hdc = ::GetDC( NULL );
+       HDC hdcSrc = ::CreateCompatibleDC( hdc );
+       HDC hdcDst = ::CreateCompatibleDC( hdc );
+
+       ::SaveDC( hdcSrc );
+       ::SaveDC( hdcDst );
+       ::SelectObject( hdcDst, m_hbmp );
+       ::SelectObject( hdcSrc, src.m_hbmp );
+
+       if ( m_size.cx != src.m_size.cx || m_size.cy != src.m_size.cy ) {
+               POINT org;
+               ::GetBrushOrgEx( hdcDst, &org );
+               ::SetStretchBltMode( hdcDst, HALFTONE );
+               ::SetBrushOrgEx( hdcDst, org.x, org.y, NULL );
+
+               ::StretchBlt( hdcDst, 0, 0, m_size.cx, m_size.cy,
+                       hdcSrc, 0, 0, src.m_size.cx, src.m_size.cy,
+                       SRCCOPY );
+       }
+       else {
+               ::BitBlt( hdcDst, 0, 0, m_size.cx, m_size.cy,
+                       hdcSrc, 0, 0, SRCCOPY );
+       }
+
+       ::RestoreDC( hdcSrc, -1 );
+       ::DeleteDC( hdcSrc );
+       ::RestoreDC( hdcDst, -1 );
+       ::DeleteDC( hdcDst );
+       ::ReleaseDC( NULL, hdc );
+       m_fileName = L"";
+       return true;
+}
+
+
+bool GVOImage::createDIBImage( int width, int height )
+{
+       reset();
+
+       BITMAPINFOHEADER bmih = { sizeof(bmih) };
+       bmih.biWidth = width;
+       bmih.biHeight = -height;
+       bmih.biPlanes = 1;
+       bmih.biBitCount = 24;
+       bmih.biSizeImage = width * height * 3;
+       m_hbmp = ::CreateDIBSection( NULL, (LPBITMAPINFO)&bmih, DIB_RGB_COLORS, (void **)&m_bits, NULL, 0 );
+       if ( !m_hbmp ) {
+               return false;
+       }
+       m_size.cx = width;
+       m_size.cy = height;
+       m_stride = s_strideFromWidthAndBitsPerPixel( width, bmih.biBitCount );
+       m_fileName = L"";
+       return true;
+}
+
+
+bool GVOImage::loadFromFile( const std::wstring& fileName )
+{
+       reset();
+
+       std::auto_ptr<Gdiplus::Bitmap> image;
+       HBITMAP hbmp = NULL;
+       image.reset( Gdiplus::Bitmap::FromFile( fileName.c_str() ) );
+       image->GetHBITMAP( Gdiplus::Color( 0, 0, 0 ), &hbmp );
+       image.reset();
+       if ( !hbmp ) {
+               return false;
+       }
+       BITMAP bmp = { 0 };
+       ::GetObject( hbmp, sizeof(bmp), &bmp );
+       if ( !createDIBImage( bmp.bmWidth, bmp.bmHeight ) ) {
+               ::DeleteObject( hbmp );
+               return false;
+       }
+
+       std::vector<uint8_t> buffer;
+       buffer.resize( ::GetBitmapBits( hbmp, 0, NULL ) );
+       ::GetBitmapBits( hbmp, buffer.size(), &buffer[0] );
+       ::DeleteObject( hbmp );
+
+       switch ( bmp.bmBitsPixel ) {
+       case 24:
+               ::memcpy( m_bits, &buffer[0], m_stride );
+               break;
+       case 32:
+               ::s_copyImage24From32( m_bits, &buffer[0], m_size.cx, m_size.cy );
+               break;
+       default:
+               return false;
+       }
+
+       m_fileName = fileName;
+
+       return true;
+}
index 0f81836..6a43f43 100644 (file)
@@ -1,27 +1,26 @@
 #pragma once
 #include <cstdint>
 #include <memory>
+#include <vector>
 #include <Windows.h>
 
-// PNG\82Æ\82©\82Ì\93Ç\82Ý\8d\9e\82Ý\97p
-#include <objbase.h>
-#include <gdiplus.h>
-#include <gdiplusbitmap.h>
+#include "GVONoncopyable.h"
 
 
-class GVOImage {
-private:
-       GVOImage( const GVOImage& );
-       GVOImage& operator=(const GVOImage&);
-
+class GVOImage :private GVONoncopyable {
 private:
        std::wstring m_fileName;
        HBITMAP m_hbmp;
        SIZE m_size;
        uint8_t * m_bits;
+       uint32_t m_stride;
 
 public:
-       GVOImage() : m_hbmp(), m_size(), m_bits()
+       GVOImage() :
+               m_hbmp(),
+               m_size(),
+               m_bits(),
+               m_stride()
        {
        }
        ~GVOImage()
@@ -35,49 +34,19 @@ public:
                        m_hbmp = NULL;
                }
                m_fileName = L"";
+               m_size = SIZE();
+               m_stride = 0;
        }
-       bool stretchCopy( const GVOImage& src, const SIZE& size )
+       void copy( const GVOImage & src )
        {
-               return stretchCopy( src, size.cx, size.cy );
+               createDIBImage( src.m_size );
+               ::memcpy( m_bits, src.m_bits, src.m_size.cy * src.m_stride );
        }
-       bool stretchCopy( const GVOImage& src, uint32_t width, uint32_t height )
+       bool stretchCopy( const GVOImage& src, const SIZE& size )
        {
-               const SIZE size = { width, height };
-               if ( !createDIBImage( size ) ) {
-                       return false;
-               }
-
-               HDC hdc = ::GetDC( NULL );
-               HDC hdcSrc = ::CreateCompatibleDC( hdc );
-               HDC hdcDst = ::CreateCompatibleDC( hdc );
-
-               ::SaveDC( hdcSrc );
-               ::SaveDC( hdcDst );
-               ::SelectObject( hdcDst, m_hbmp );
-               ::SelectObject( hdcSrc, src.m_hbmp );
-
-               if ( m_size.cx != src.m_size.cx || m_size.cy != src.m_size.cy ) {
-                       POINT org;
-                       ::GetBrushOrgEx( hdcDst, &org );
-                       ::SetStretchBltMode( hdcDst, HALFTONE );
-                       ::SetBrushOrgEx( hdcDst, org.x, org.y, NULL );
-
-                       ::StretchBlt( hdcDst, 0, 0, m_size.cx, m_size.cy,
-                               hdcSrc, 0, 0, src.m_size.cx, src.m_size.cy,
-                               SRCCOPY );
-               }
-               else {
-                       ::BitBlt( hdcDst, 0, 0, m_size.cx, m_size.cy,
-                               hdcSrc, 0, 0, SRCCOPY );
-               }
-
-               ::RestoreDC( hdcSrc, -1 );
-               ::DeleteDC( hdcSrc );
-               ::RestoreDC( hdcDst, -1 );
-               ::DeleteDC( hdcDst );
-               ::ReleaseDC( NULL, hdc );
-               return true;
+               return stretchCopy( src, size.cx, size.cy );
        }
+       bool stretchCopy( const GVOImage& src, uint32_t width, uint32_t height );
        bool isCompatible( const SIZE& size ) const
        {
                if ( !m_hbmp ) {
@@ -104,6 +73,10 @@ public:
        {
                return m_size.cy;
        }
+       uint32_t stride() const
+       {
+               return m_stride;
+       }
        const uint8_t * imageBits() const
        {
                return m_bits;
@@ -117,55 +90,10 @@ public:
                return m_fileName;
        }
 
-       bool createDIBImage( int width, int height )
-       {
-               reset();
-
-               BITMAPINFOHEADER bmih = { sizeof(bmih) };
-               bmih.biWidth = width;
-               bmih.biHeight = -height;
-               bmih.biPlanes = 1;
-               bmih.biBitCount = 24;
-               bmih.biSizeImage = width * height * 3;
-               m_hbmp = ::CreateDIBSection( NULL, (LPBITMAPINFO)&bmih, DIB_RGB_COLORS, (void **)&m_bits, NULL, 0 );
-               if ( !m_hbmp ) {
-                       return false;
-               }
-               m_size.cx = width;
-               m_size.cy = height;
-               return true;
-       }
+       bool createDIBImage( int width, int height );
        bool createDIBImage( const SIZE& size )
        {
-               reset();
-
-               BITMAPINFOHEADER bmih = { sizeof(bmih) };
-               bmih.biWidth = size.cx;
-               bmih.biHeight = -size.cy;
-               bmih.biPlanes = 1;
-               bmih.biBitCount = 24;
-               bmih.biSizeImage = bmih.biWidth * bmih.biHeight * 3;
-               m_hbmp = ::CreateDIBSection( NULL, (LPBITMAPINFO)&bmih, DIB_RGB_COLORS, (void **)&m_bits, NULL, 0 );
-               if ( !m_hbmp ) {
-                       return false;
-               }
-               m_size = size;
-               return true;
-       }
-       bool loadFromFile( const std::wstring& fileName )
-       {
-               std::auto_ptr<Gdiplus::Bitmap> image;
-               image.reset( Gdiplus::Bitmap::FromFile( fileName.c_str() ) );
-               image->GetHBITMAP( Gdiplus::Color( 0, 0, 0 ), &m_hbmp );
-               image.reset();
-               if ( !m_hbmp ) {
-                       return false;
-               }
-               BITMAP bmp = { 0 };
-               ::GetObject( m_hbmp, sizeof(bmp), &bmp );
-               m_size.cx = bmp.bmWidth;
-               m_size.cy = ::abs( bmp.bmHeight );
-               m_fileName = fileName;
-               return true;
+               return createDIBImage( size.cx, size.cy );
        }
+       bool loadFromFile( const std::wstring& fileName );
 };
index 6849772..a557043 100644 (file)
@@ -21,7 +21,6 @@
 #include "GVOGameProcess.h"
 #include "GVOWorldMap.h"
 #include "GVOShip.h"
-#include "GVOSpeedMeter.h"
 
 
 
@@ -60,8 +59,8 @@ static void s_onPaint( HWND );
 // \83A\83v\83\8a\8f\88\97\9d
 static std::wstring s_makeVersionString();
 static std::wstring s_getMapFileName();
-static void s_updateFrame(HWND hwnd, HDC hdc);
-static void s_updateWindowTitle( HWND );
+static void s_updateFrame(HWND);
+static void s_updateWindowTitle( HWND, POINT, double );
 static void s_toggleKeepForeground( HWND );
 static void s_popupMenu( HWND, int16_t, int16_t );
 static void s_popupCoord( HWND, int16_t, int16_t );
@@ -79,36 +78,29 @@ static HANDLE s_appMutex;
 
 static const std::wstring k_aboutText = s_makeVersionString(); // \83o\81[\83W\83\87\83\93\8fî\95ñ\83e\83L\83X\83g
 
-static GVOImage s_backbuffer;  //!<@brief \95`\89æ\97p24bit\83C\83\81\81[\83W\83o\83b\83t\83@
-static HANDLE s_pollingTimerEvent = ::CreateEvent( NULL, TRUE, TRUE, NULL );
-static UINT s_pollingTimerID = 0;
-
 static Gdiplus::GdiplusStartupInput s_gdisi;
 static ULONG_PTR s_gdiToken;
 
 static GVOConfig s_config( k_configFileName );
 static GVOGameProcess s_gvoGameProcess;
+static GVORenderer s_renderer;
 static GVOWorldMap s_worldMap;
-static GVOShip s_ship;
-static GVOSpeedMeter s_speedMeter;
+static POINT s_latestSurveyCoord;
 
 static UINT s_pollingInterval = 1000;  // \8fó\91Ô\8aÄ\8e\8b\8aÔ\8au\81i1\95b\81j
-static bool s_isUpdated = false;               // \8fó\91Ô\8dX\90V\83t\83\89\83O
 static bool s_isDragging = false;              // \83h\83\89\83b\83O\8fó\91Ô\83t\83\89\83O
 static SIZE s_clientSize;                              // \83N\83\89\83C\83A\83\93\83g\97Ì\88æ\82Ì\91å\82«\82³
 static POINT s_dragOrg;                                        // \83h\83\89\83b\83O\8c´\93_\81i\88Ú\93®\97Ê\8eZ\8fo\97p\81j
 
-
-#ifndef NDEBUG
-// \83f\83o\83b\83O\97p\8e©\93®\8dq\8ds\95Ï\90\94
-static double s_xDebugAutoCruise;
-static double s_yDebugAutoCruise;
-static double s_debugAutoCruiseAngle = 0;
+#ifdef GVO_PERF_CHECK
+typedef std::deque<double> PerfCountList;
+static PerfCountList s_perfCountList;
 #endif
 
 
 
 
+
 int APIENTRY _tWinMain( _In_ HINSTANCE hInstance,
        _In_opt_ HINSTANCE hPrevInstance,
        _In_ LPTSTR    lpCmdLine,
@@ -143,12 +135,10 @@ int APIENTRY _tWinMain( _In_ HINSTANCE hInstance,
                return 0;
        }
 
-       s_pollingTimerID = ::timeSetEvent( s_config.m_pollingInterval, tc.wPeriodMin, LPTIMECALLBACK( s_pollingTimerEvent ), 0, TIME_PERIODIC | TIME_CALLBACK_EVENT_SET );
-
        // \83\81\83C\83\93 \83\81\83b\83Z\81[\83\83\8b\81[\83v:
        const LRESULT retVal = s_mainLoop();
 
-       ::timeKillEvent( s_pollingTimerID );
+       s_gvoGameProcess.teardown();
 
        s_config.save();
        Gdiplus::GdiplusShutdown( s_gdiToken );
@@ -176,17 +166,18 @@ static ATOM MyRegisterClass( HINSTANCE hInstance )
 
 static BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
 {
-       if ( !s_worldMap.loadFromFile( s_config ) ) {
-               // \8e¸\94s\82·\82ê\82Î\95Û\91\82³\82ê\82È\82¢\82Ì\82Å\91å\8fä\95v\81B
-               s_config.m_mapFileName = s_getMapFileName();
-               if ( !s_worldMap.loadFromFile( s_config ) ) {
+       if ( !s_worldMap.loadFromFile( s_config.m_mapFileName ) ) {
+               std::wstring fileName = s_getMapFileName();
+               if ( !s_worldMap.loadFromFile( fileName ) ) {
                        ::MessageBox( NULL,
                                L"\83}\83b\83v\89æ\91\9c\82ð\8aJ\82¯\82Ü\82¹\82ñ\82Å\82µ\82½\81B",
                                k_appName,
                                MB_ICONERROR | MB_SETFOREGROUND | MB_OK );
                        return FALSE;
                }
+               s_config.m_mapFileName = fileName;
        }
+       s_renderer.setWorldMap( s_worldMap );
 
        HWND hwnd;
 
@@ -196,7 +187,11 @@ static BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
        if ( s_config.m_keepForeground ) {
                exStyle |= WS_EX_TOPMOST;
        }
-       hwnd = CreateWindowEx( exStyle, k_windowClassName, k_appName, WS_OVERLAPPEDWINDOW,
+       DWORD style = 0;
+       style |= WS_OVERLAPPEDWINDOW;
+       style |= WS_CLIPCHILDREN;
+       style |= WS_CLIPSIBLINGS;
+       hwnd = CreateWindowEx( exStyle, k_windowClassName, k_appName, style,
                s_config.m_windowPos.x, s_config.m_windowPos.y,
                s_config.m_windowSize.cx, s_config.m_windowSize.cy,
                NULL, NULL, hInstance, NULL );
@@ -205,25 +200,18 @@ static BOOL InitInstance( HINSTANCE hInstance, int nCmdShow )
                return FALSE;
        }
 
+       g_hwndMain = hwnd;
+       g_hdcMain = ::GetDC( g_hwndMain );
+       s_renderer.setup( g_hdcMain );
+
        s_pollingInterval = s_config.m_pollingInterval;
-       s_gvoGameProcess.setConfig( s_config );
-       s_worldMap.setConfig( s_config );
-       s_ship.setInitialSurveyCoord( s_config.m_initialSurveyCoord );
-#ifndef NDEBUG
-       s_xDebugAutoCruise = s_config.m_initialSurveyCoord.x;
-       s_yDebugAutoCruise = s_config.m_initialSurveyCoord.y;
-#endif
+       s_gvoGameProcess.setup( s_config );
+       s_renderer.setConfig( s_config );
 
-       if ( s_gvoGameProcess.updateState() ) {
-               s_ship.updateWithSurveyCoord( s_gvoGameProcess.surveyCoord(), s_gvoGameProcess.timeStamp() );
-               s_worldMap.setShipPosition( s_gvoGameProcess.surveyCoord(), s_config.m_traceShipPositionEnabled );
-       }
-       s_updateWindowTitle( hwnd );
+       s_updateWindowTitle( hwnd, s_config.m_initialSurveyCoord, s_renderer.viewScale() );
 
        ShowWindow( hwnd, nCmdShow );
        UpdateWindow( hwnd );
-       g_hwndMain = hwnd;
-       g_hdcMain = ::GetDC( g_hwndMain );
 
        return TRUE;
 }
@@ -252,7 +240,8 @@ static LRESULT s_mainLoop()
                if ( s_gvoGameProcess.processHandle() ) {
                        handles.push_back( s_gvoGameProcess.processHandle() );
                }
-               handles.push_back( s_pollingTimerEvent );
+               //handles.push_back( s_pollingTimerEvent );
+               handles.push_back( s_gvoGameProcess.dataReadyEvent() );
 
                if ( handles.empty() ) {
                        ::WaitMessage();
@@ -272,9 +261,9 @@ static LRESULT s_mainLoop()
                        s_gvoGameProcess.clear();
                        continue;
                }
-               if ( activeHandle == s_pollingTimerEvent ) {
-                       ::ResetEvent( s_pollingTimerEvent );
-                       s_updateFrame( g_hwndMain, g_hdcMain );
+
+               if ( activeHandle == s_gvoGameProcess.dataReadyEvent() ) {
+                       s_updateFrame( g_hwndMain );
                        continue;
                }
        }
@@ -316,45 +305,55 @@ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
                        break;
                case IDM_TOGGLE_TRACE_SHIP:
                        s_config.m_traceShipPositionEnabled = !s_config.m_traceShipPositionEnabled;
+                       s_renderer.enableTraceShip( s_config.m_traceShipPositionEnabled );
                        break;
                case IDM_ERASE_SHIP_ROUTE:
-                       s_worldMap.clearShipRoute();
+                       s_renderer.clearShipRoute();
                        break;
                case IDM_TOGGLE_KEEP_FOREGROUND:
                        s_toggleKeepForeground( hwnd );
                        break;
                case IDM_TOGGLE_SPEED_METER:
                        s_config.m_speedMeterEnabled = !s_config.m_speedMeterEnabled;
+                       s_renderer.enableSpeedMeter( s_config.m_speedMeterEnabled );
                        ::InvalidateRect( hwnd, NULL, FALSE );
                        break;
                case IDM_TOGGLE_VECTOR_LINE:
                        s_config.m_shipVectorLineEnabled = !s_config.m_shipVectorLineEnabled;
-                       s_worldMap.setVisibleShipRoute( s_config.m_shipVectorLineEnabled );
+                       s_renderer.setVisibleShipRoute( s_config.m_shipVectorLineEnabled );
                        break;
-               default:
-                       return DefWindowProc( hwnd, message, wp, lp );
-               }
-               break;
-
-       case WM_KEYUP:
-               switch ( wp ) {
-               case VK_F1:
-                       s_toggleKeepForeground( hwnd );
+               case IDM_SAME_SCALE:
+                       if ( s_renderer.viewScale() != 1.0 ) {
+                               s_renderer.resetViewScale();
+                       }
                        break;
-               case VK_ADD:
-                       if ( s_worldMap.zoomIn() ) {
-                               s_updateWindowTitle( hwnd );
+               case IDM_ZOOM_IN:
+                       if ( s_renderer.zoomIn() ) {
+#ifdef GVO_PERF_CHECK
+                               s_perfCountList.clear();
+#endif
+                               s_updateWindowTitle( hwnd, s_latestSurveyCoord, s_renderer.viewScale() );
                                ::InvalidateRect( hwnd, NULL, FALSE );
                        }
                        break;
-               case VK_OEM_MINUS:
-                       if ( s_worldMap.zoomOut() ) {
-                               s_updateWindowTitle( hwnd );
+               case IDM_ZOOM_OUT:
+                       if ( s_renderer.zoomOut() ) {
+#ifdef GVO_PERF_CHECK
+                               s_perfCountList.clear();
+#endif
+                               s_updateWindowTitle( hwnd, s_latestSurveyCoord, s_renderer.viewScale() );
                                ::InvalidateRect( hwnd, NULL, FALSE );
                        }
                        break;
-               default:
+#ifndef NDEBUG
+               case IDM_TOGGLE_DEBUG_AUTO_CRUISE:
+                       s_config.m_debugAutoCruiseEnabled = !s_config.m_debugAutoCruiseEnabled;
+                       s_gvoGameProcess.enableDebugAutoCruise( s_config.m_debugAutoCruiseEnabled );
                        break;
+#endif
+
+               default:
+                       return DefWindowProc( hwnd, message, wp, lp );
                }
                break;
 
@@ -383,6 +382,7 @@ LRESULT CALLBACK WndProc( HWND hwnd, UINT message, WPARAM wp, LPARAM lp )
                }
                break;
        case WM_DESTROY:
+               s_renderer.teardown();
                PostQuitMessage( 0 );
                break;
        default:
@@ -430,10 +430,7 @@ static void s_onSize( HWND hwnd, UINT state, WORD cx, WORD cy )
        if ( s_clientSize.cx != cx || s_clientSize.cy != cy ) {
                s_clientSize.cx = cx;
                s_clientSize.cy = cy;
-               if ( !s_backbuffer.isCompatible( s_clientSize ) ) {
-                       s_backbuffer.createDIBImage( s_clientSize );
-               }
-               s_worldMap.setViewSize( s_clientSize );
+               s_renderer.setViewSize( s_clientSize );
        }
 }
 
@@ -443,14 +440,17 @@ static void s_onMouseWheel( HWND hwnd, int16_t delta, UINT vkey, int16_t x, int1
        bool isChanged = false;
 
        if ( 0 < delta ) {
-               isChanged = s_worldMap.zoomIn();
+               isChanged = s_renderer.zoomIn();
        }
        else {
-               isChanged = s_worldMap.zoomOut();
+               isChanged = s_renderer.zoomOut();
        }
 
        if ( isChanged ) {
-               s_updateWindowTitle( hwnd );
+#ifdef GVO_PERF_CHECK
+               s_perfCountList.clear();
+#endif
+               s_updateWindowTitle( hwnd, s_latestSurveyCoord, s_renderer.viewScale() );
                ::InvalidateRect( hwnd, NULL, FALSE );
        }
 }
@@ -469,12 +469,13 @@ static void s_onMouseMove( HWND hwnd, UINT vkey, int16_t x, int16_t y )
                }
                const POINT offset = { -dx, -dy };
 
-               s_worldMap.offsetFocusInViewCoord( offset );
+               s_renderer.offsetFocusInViewCoord( offset );
                ::InvalidateRect( hwnd, NULL, FALSE );
 
                s_dragOrg.x = x;
                s_dragOrg.y = y;
                s_config.m_traceShipPositionEnabled = false;
+               s_renderer.enableTraceShip( s_config.m_traceShipPositionEnabled );
        }
        else {
 
@@ -532,102 +533,29 @@ static void s_onMouseRightButtonUp( HWND hwnd, UINT vkey, int16_t x, int16_t y )
 }
 
 
-// \83_\83u\83\8b\83o\83b\83t\83@\83\8a\83\93\83O\82Å\82¿\82ç\82Â\82«\96h\8e~\81B
 static void s_onPaint( HWND hwnd )
 {
 #ifdef GVO_PERF_CHECK
-       int64_t perfBegin = 0, perfEnd = 0;
-       ::QueryPerformanceCounter((LARGE_INTEGER*)&perfBegin);
+       const int64_t perfBegin = g_queryPerformanceCounter();
 #endif
-       if ( !s_backbuffer.bitmapHandle() ) {
-               s_backbuffer.createDIBImage( s_clientSize );
-       }
-
-       PAINTSTRUCT ps;
-       HDC hdc = BeginPaint( hwnd, &ps );
-       HDC hdcBackbuffer = ::CreateCompatibleDC( hdc );
-       ::SaveDC( hdcBackbuffer );
-       ::SelectObject( hdcBackbuffer, s_backbuffer.bitmapHandle() );
-       RECT rc = { 0, 0, s_clientSize.cx, s_clientSize.cy };
-       ::FillRect( hdcBackbuffer, &rc, (HBRUSH)::GetStockObject( BLACK_BRUSH ) );
-
-       // \95`\89æ\82ðhdcBackbuffer\82É\91Î\82µ\82Ä\8ds\82¤\81B
-       s_worldMap.drawMap( hdcBackbuffer, s_ship );
-
-       // \91¬\93x\8cv\82ð\95`\89æ
-       if ( s_config.m_speedMeterEnabled ) {
-               const double velocity = s_speedMeter.velocityByKnot();
-               wchar_t buf[4096] = { 0 };
-               swprintf( buf, _countof( buf ), L"%.2f kt", velocity );
-
-               RECT rc = { 0 };
-               ::DrawText( hdcBackbuffer, buf, -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_TOP | DT_CALCRECT );
-               const int width = rc.right - rc.left;
-               rc.left = s_clientSize.cx - width;
-               rc.right = rc.left + width;
-               ::DrawText( hdcBackbuffer, buf, -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_TOP );
-       }
-
-#ifndef NDEBUG
-       // \91ª\97Ê\8dÀ\95W\82ð\95`\89æ
-       {
-               const GVOImage& surveyCoordImage = s_gvoGameProcess.surveyCoordImage();
-               HDC hdcSurvey = ::CreateCompatibleDC( hdcBackbuffer );
-               ::SaveDC( hdcSurvey );
-               ::SelectObject( hdcSurvey, surveyCoordImage.bitmapHandle() );
-               ::BitBlt( hdcBackbuffer, 0, 0, surveyCoordImage.size().cx, surveyCoordImage.size().cy,
-                       hdcSurvey, 0, 0, SRCCOPY );
-               ::RestoreDC( hdcSurvey, -1 );
-               ::DeleteDC( hdcSurvey );
-       }
-#endif
-
-#ifndef NDEBUG
-       if ( s_config.m_debugAutoCruiseEnabled ) {
-               const double rad = ((s_debugAutoCruiseAngle)* M_PI) / 180;
-               const double vx = ::cos( rad );
-               const double vy = ::sin( rad );
-               const LONG length = max( s_clientSize.cx, s_clientSize.cy );
-
-               COLORREF rgb = (::fabs(s_ship.vector().angleTo(GVOVector(vx,vy))) <= FLT_EPSILON) ? RGB( 0, 255, 0 ) : RGB( 255, 255, 0 );
-
-               LONG x2 = s_clientSize.cx / 2 + LONG( vx * length );
-               LONG y2 = s_clientSize.cy / 2 + LONG( vy * length );
-               HPEN pen = ::CreatePen( PS_SOLID, 3, rgb );
-               HGDIOBJ old = ::SelectObject( hdcBackbuffer, pen );
-               ::MoveToEx( hdcBackbuffer, s_clientSize.cx / 2, s_clientSize.cy / 2, NULL );
-               ::LineTo( hdcBackbuffer, x2, y2 );
-               ::SelectObject( hdcBackbuffer, old );
-               ::DeleteObject( pen );
-       }
-#endif // #ifndef NDEBUG
-
-       ::BitBlt( hdc, 0, 0, s_clientSize.cx, s_clientSize.cy,
-               hdcBackbuffer, 0, 0, SRCCOPY );
-
-       ::RestoreDC( hdcBackbuffer, -1 );
-       ::DeleteDC( hdcBackbuffer );
-       EndPaint( hwnd, &ps );
 
+       s_renderer.render();
+       ::ValidateRect( hwnd, NULL );
 
 #ifdef GVO_PERF_CHECK
-       ::QueryPerformanceCounter((LARGE_INTEGER*)&perfEnd);
-       int64_t freq = 0;
-       ::QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
+       const int64_t perfEnd = g_queryPerformanceCounter();
+       const int64_t freq = g_queryPerformanceFrequency();
        const double deltaPerSec = (double(perfEnd - perfBegin) / double(freq)) * 1000.0;
-       typedef std::list<double> PerfList;
-       static PerfList perf;
-       perf.push_back(deltaPerSec);
+       s_perfCountList.push_back(deltaPerSec);
 
-       const double ave = std::accumulate( perf.begin(), perf.end(), 0.0 ) / perf.size();
-       if ( 100 < perf.size() ) {
-               perf.pop_front();
+       const double ave = std::accumulate( s_perfCountList.begin(), s_perfCountList.end(), 0.0 ) / s_perfCountList.size();
+       if ( 100 < s_perfCountList.size() ) {
+               s_perfCountList.pop_front();
        }
 
        std::wstring s;
-       s = std::wstring( L"perf:" ) + std::to_wstring( ave ) + L"(ms)\n";
+       s = std::wstring( L"\95`\89æ\91¬\93x:" ) + std::to_wstring( ave ) + L"(ms)\n";
        ::SetWindowText( hwnd, s.c_str() );
-       ::InvalidateRect( hwnd, NULL, FALSE );
 #endif
 }
 
@@ -664,90 +592,33 @@ static std::wstring s_getMapFileName()
 }
 
 
-static void s_updateFrame( HWND hwnd, HDC hdc )
+static void s_updateFrame( HWND hwnd )
 {
-#ifndef NDEBUG
-       if ( s_config.m_debugAutoCruiseEnabled ) {
-               static bool isRandInitialized = false;
-               if ( !isRandInitialized ) {
-                       srand( ::timeGetTime() );
-                       isRandInitialized = true;
-               }
-
-               const double rad = ((s_debugAutoCruiseAngle)* M_PI) / 180;
-               const double vx = ::cos( rad );
-               const double vy = ::sin( rad );
-
-               s_xDebugAutoCruise += vx * s_config.m_debugAutoCruiseVelocity;
-               s_yDebugAutoCruise += vy * s_config.m_debugAutoCruiseVelocity;
-
-               static DWORD tick = ::timeGetTime();
-               static DWORD count = 0;
-               if ( (tick + s_config.m_debugAutoCruiseTurnInterval) < ::timeGetTime() ) {
-                       if ( 10 < (++count) ) {
-                               count = 0;
-                               s_debugAutoCruiseAngle += 90 + (LONG( rand() / double( RAND_MAX ) * 90 ) & ~0x1);
-                       }
-                       else {
-                               s_debugAutoCruiseAngle += (rand() & 1) ? s_config.m_debugAutoCruiseTurnAngle : -s_config.m_debugAutoCruiseTurnAngle;
-                       }
-                       tick = ::timeGetTime();
-               }
-               s_debugAutoCruiseAngle = fmod( ::fabs( s_debugAutoCruiseAngle ), 360 );
-
-               if ( s_xDebugAutoCruise < 0 ) {
-                       s_xDebugAutoCruise += k_worldWidth;
-               }
-               if ( s_yDebugAutoCruise < 0 ) {
-                       s_yDebugAutoCruise += k_worldHeight;
-               }
-               s_xDebugAutoCruise = fmod( s_xDebugAutoCruise, (double)k_worldWidth );
-               s_yDebugAutoCruise = fmod( s_yDebugAutoCruise, (double)k_worldHeight );
-
-               //// \92n\90}\82ð\8c×\82®\8f\88\97\9d\82Ì\8am\94F\97p\83f\83o\83b\83O\83R\81[\83h
-               //if ( 100 <= s_xDebugAutoCruise && s_xDebugAutoCruise <= (GVOWorldMap::k_worldWidth - 100) ) {
-               //      s_xDebugAutoCruise = 0;
-               //}
-
-
-               POINT p = {
-                       LONG( s_xDebugAutoCruise ),
-                       LONG( s_yDebugAutoCruise )
-               };
-               uint32_t timeStamp = ::timeGetTime();
-               s_ship.updateWithSurveyCoord( p, timeStamp );
-               s_speedMeter.updateVelocity( s_ship.velocity(), timeStamp );
-               s_gvoGameProcess.setSurveyCoord( p );
-               s_worldMap.setShipPosition( p, s_config.m_traceShipPositionEnabled );
-               s_worldMap.updateShipRouteMap( hdc );
-               s_updateWindowTitle( hwnd );
-               ::InvalidateRect( hwnd, NULL, FALSE );
+       std::vector<GVOGameStatus> gameStats;
+       gameStats = s_gvoGameProcess.getState();
+       if ( gameStats.empty() ) {
                return;
        }
-#endif // #ifndef NDEBUG
-
-       s_isUpdated = s_gvoGameProcess.updateState();
 
-       if ( s_isUpdated ) {
-               s_config.m_initialSurveyCoord = s_gvoGameProcess.surveyCoord();
-               s_ship.updateWithSurveyCoord( s_gvoGameProcess.surveyCoord(), s_gvoGameProcess.timeStamp() );
-               s_speedMeter.updateVelocity( s_ship.velocity(), s_gvoGameProcess.timeStamp() );
-               s_worldMap.setShipPosition( s_gvoGameProcess.surveyCoord(), s_config.m_traceShipPositionEnabled );
-               s_worldMap.updateShipRouteMap( hdc );
-               s_updateWindowTitle( hwnd );
-               ::InvalidateRect( hwnd, NULL, FALSE );
+       for ( std::vector<GVOGameStatus>::const_iterator it = gameStats.begin(); it != gameStats.end(); ++it ) {
+               const GVOGameStatus& status = *it;
+               s_latestSurveyCoord = status.m_surveyCoord;
+               s_config.m_initialSurveyCoord = s_latestSurveyCoord;
+               s_renderer.updateShipState( s_latestSurveyCoord, status.m_shipVector, status.m_shipVelocity );
        }
+#ifndef GVO_PERF_CHECK
+       s_updateWindowTitle( hwnd, s_latestSurveyCoord, s_renderer.viewScale() );
+#endif
+       ::InvalidateRect( hwnd, NULL, FALSE );
 }
 
 
-static void s_updateWindowTitle( HWND hwnd )
+static void s_updateWindowTitle( HWND hwnd, POINT surveyCoord, double viewScale )
 {
-       const POINT& surveyCoord = s_gvoGameProcess.surveyCoord();
-
        std::vector<wchar_t> buf( 4096 );
        ::swprintf( &buf[0], buf.size(), L"%d,%d - (%.1f%%) - %s %s",
                surveyCoord.x, surveyCoord.y,
-               s_worldMap.viewScale() * s_worldMap.viewScaleOrder(),
+               viewScale * 100.0,
                k_appName, k_version
                );
        ::SetWindowText( hwnd, &buf[0] );
@@ -777,10 +648,23 @@ static void s_popupMenu( HWND hwnd, int16_t x, int16_t y )
        ::CheckMenuItem( popupMenu, IDM_TOGGLE_SPEED_METER, s_config.m_speedMeterEnabled ? MF_CHECKED : MF_UNCHECKED );
        ::CheckMenuItem( popupMenu, IDM_TOGGLE_VECTOR_LINE, s_config.m_shipVectorLineEnabled ? MF_CHECKED : MF_UNCHECKED );
 
+#ifndef NDEBUG
+       MENUITEMINFO mii = { sizeof(mii) };
+       mii.fMask = MIIM_TYPE | MIIM_ID;
+       mii.fType = MFT_STRING;
+       mii.wID = IDM_TOGGLE_DEBUG_AUTO_CRUISE;
+       mii.dwTypeData = L"[DEBUG]\8e©\93®\8dq\8ds\82ð\97L\8cø";
+       ::InsertMenuItem( popupMenu, ::GetMenuItemCount( popupMenu ), TRUE, &mii );
+       
+       ::CheckMenuItem( popupMenu, IDM_TOGGLE_DEBUG_AUTO_CRUISE, s_config.m_debugAutoCruiseEnabled ? MF_CHECKED : MF_UNCHECKED );
+#endif
+
        POINT p = { x, y };
        ::ClientToScreen( hwnd, &p );
        ::TrackPopupMenu( popupMenu, TPM_NONOTIFY | TPM_NOANIMATION | TPM_LEFTALIGN | TPM_TOPALIGN,
                p.x, p.y, 0, hwnd, NULL );
+
+       ::DestroyMenu( popupMenu );
 }
 
 
index 9b1d0bb..916db54 100644 (file)
@@ -35,3 +35,17 @@ inline double g_radianFromDegree( const double degree )
 {
        return degree * (M_PI / 180.0);
 }
+
+inline int64_t g_queryPerformanceCounter()
+{
+       int64_t v = 0;
+       ::QueryPerformanceCounter( (LARGE_INTEGER *)&v );
+       return v;
+}
+
+inline int64_t g_queryPerformanceFrequency()
+{
+       int64_t v = 0;
+       ::QueryPerformanceFrequency( (LARGE_INTEGER *)&v );
+       return v;
+}
index f959f39..27c9acf 100644 (file)
Binary files a/GVONavish/GVONavish/GVONavish.rc and b/GVONavish/GVONavish/GVONavish.rc differ
index 188c644..c84e2e7 100644 (file)
@@ -101,8 +101,11 @@ copy /Y "$(ProjectDir)readme.txt" "$(SolutionDir)archive\$(ProjectName)\readme.t
   <ItemGroup>
     <ClInclude Include="GVOConfig.h" />
     <ClInclude Include="GVOGameProcess.h" />
+    <ClInclude Include="GVOGameStatus.h" />
     <ClInclude Include="GVOImage.h" />
     <ClInclude Include="GVONavish.h" />
+    <ClInclude Include="GVONoncopyable.h" />
+    <ClInclude Include="GVORenderer.h" />
     <ClInclude Include="GVOSpeedMeter.h" />
     <ClInclude Include="GVOSurveyCoordExtractor.h" />
     <ClInclude Include="GVOVector.h" />
@@ -115,7 +118,9 @@ copy /Y "$(ProjectDir)readme.txt" "$(SolutionDir)archive\$(ProjectName)\readme.t
   </ItemGroup>
   <ItemGroup>
     <ClCompile Include="GVOGameProcess.cpp" />
+    <ClCompile Include="GVOImage.cpp" />
     <ClCompile Include="GVONavish.cpp" />
+    <ClCompile Include="GVORenderer.cpp" />
     <ClCompile Include="GVOShip.cpp" />
     <ClCompile Include="GVOSurveyCoordExtractor.cpp" />
     <ClCompile Include="GVOWorldMap.cpp" />
@@ -132,7 +137,9 @@ copy /Y "$(ProjectDir)readme.txt" "$(SolutionDir)archive\$(ProjectName)\readme.t
     <Image Include="small.ico" />
   </ItemGroup>
   <ItemGroup>
-    <Text Include="ReadMe.txt" />
+    <Text Include="ReadMe.txt">
+      <DeploymentContent Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">true</DeploymentContent>
+    </Text>
   </ItemGroup>
   <Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
   <ImportGroup Label="ExtensionTargets">
index 985f828..f71f049 100644 (file)
 <?xml version="1.0" encoding="utf-8"?>
 <Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
   <ItemGroup>
-    <Filter Include="ソース ファイル">
-      <UniqueIdentifier>{4FC737F1-C7A5-4376-A066-2A32D752A2FF}</UniqueIdentifier>
-      <Extensions>cpp;c;cc;cxx;def;odl;idl;hpj;bat;asm;asmx</Extensions>
+    <Filter Include="その他">
+      <UniqueIdentifier>{74884f52-7488-4c60-850e-6b05edacc52d}</UniqueIdentifier>
     </Filter>
-    <Filter Include="ヘッダー ファイル">
-      <UniqueIdentifier>{93995380-89BD-4b04-88EB-625FBE52EBFB}</UniqueIdentifier>
-      <Extensions>h;hh;hpp;hxx;hm;inl;inc;xsd</Extensions>
-    </Filter>
-    <Filter Include="リソース ファイル">
+    <Filter Include="その他\リソース ファイル">
       <UniqueIdentifier>{67DA6AB6-F800-4c08-8B7A-83BB121AAD01}</UniqueIdentifier>
       <Extensions>rc;ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe;resx;tiff;tif;png;wav;mfcribbon-ms</Extensions>
     </Filter>
+    <Filter Include="本体">
+      <UniqueIdentifier>{b14ab327-228c-4825-b165-abb6cc117c44}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="本体\アプリケーション">
+      <UniqueIdentifier>{17d24797-f10b-4315-862e-1d0e1739496f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="本体\ゲームプロセス">
+      <UniqueIdentifier>{810563df-1604-4ced-902d-9bd6b305c74a}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="本体\画像">
+      <UniqueIdentifier>{e9afeb05-3270-4142-a4fd-2ab80da5c7b1}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="本体\画像解析">
+      <UniqueIdentifier>{28825152-a54b-4310-a26d-1311a1e7e120}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="本体\自船">
+      <UniqueIdentifier>{41df506f-09c5-4aca-82ee-435f1cb853b7}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="本体\地図">
+      <UniqueIdentifier>{251fb7e2-7c32-42ce-bd2f-eabe6409bb3f}</UniqueIdentifier>
+    </Filter>
+    <Filter Include="本体\描画">
+      <UniqueIdentifier>{42636208-5d9a-42f1-87ee-2dd90224fe00}</UniqueIdentifier>
+    </Filter>
   </ItemGroup>
   <ItemGroup>
-    <ClInclude Include="stdafx.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="GVOImage.h">
+      <Filter>本体\画像</Filter>
     </ClInclude>
-    <ClInclude Include="targetver.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="GVOShip.h">
+      <Filter>本体\自船</Filter>
     </ClInclude>
-    <ClInclude Include="Resource.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="GVOVector.h">
+      <Filter>本体\自船</Filter>
     </ClInclude>
-    <ClInclude Include="GVONavish.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="GVOVelocity.h">
+      <Filter>本体\自船</Filter>
     </ClInclude>
-    <ClInclude Include="GVOConfig.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="GVOWorldMap.h">
+      <Filter>本体\地図</Filter>
     </ClInclude>
     <ClInclude Include="GVOGameProcess.h">
-      <Filter>ヘッダー ファイル</Filter>
+      <Filter>本体\ゲームプロセス</Filter>
     </ClInclude>
-    <ClInclude Include="GVOImage.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="GVOSpeedMeter.h">
+      <Filter>本体\自船</Filter>
     </ClInclude>
     <ClInclude Include="GVOSurveyCoordExtractor.h">
-      <Filter>ヘッダー ファイル</Filter>
+      <Filter>本体\画像解析</Filter>
     </ClInclude>
-    <ClInclude Include="GVOWorldMap.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="GVOConfig.h">
+      <Filter>本体\アプリケーション</Filter>
     </ClInclude>
-    <ClInclude Include="GVOShip.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="GVONavish.h">
+      <Filter>本体\アプリケーション</Filter>
     </ClInclude>
-    <ClInclude Include="GVOVector.h">
-      <Filter>ã\83\98ã\83\83ã\83\80ã\83¼ ファイル</Filter>
+    <ClInclude Include="Resource.h">
+      <Filter>ã\81\9dã\81®ä»\96\83ªã\82½ã\83¼ã\82¹ ファイル</Filter>
     </ClInclude>
-    <ClInclude Include="GVOSpeedMeter.h">
-      <Filter>ã\83\98ã\83\83ã\83\80ã\83¼ ã\83\95ã\82¡ã\82¤ã\83«</Filter>
+    <ClInclude Include="stdafx.h">
+      <Filter>ã\81\9dã\81®ä»\96</Filter>
     </ClInclude>
-    <ClInclude Include="GVOVelocity.h">
-      <Filter>ヘッダー ファイル</Filter>
+    <ClInclude Include="targetver.h">
+      <Filter>その他</Filter>
+    </ClInclude>
+    <ClInclude Include="GVORenderer.h">
+      <Filter>本体\描画</Filter>
+    </ClInclude>
+    <ClInclude Include="GVOGameStatus.h">
+      <Filter>本体\ゲームプロセス</Filter>
+    </ClInclude>
+    <ClInclude Include="GVONoncopyable.h">
+      <Filter>その他</Filter>
     </ClInclude>
   </ItemGroup>
   <ItemGroup>
-    <ClCompile Include="stdafx.cpp">
-      <Filter>ソース ファイル</Filter>
+    <ClCompile Include="GVOImage.cpp">
+      <Filter>本体\画像</Filter>
     </ClCompile>
-    <ClCompile Include="GVONavish.cpp">
-      <Filter>ソース ファイル</Filter>
+    <ClCompile Include="GVOShip.cpp">
+      <Filter>本体\自船</Filter>
     </ClCompile>
-    <ClCompile Include="GVOGameProcess.cpp">
-      <Filter>ソース ファイル</Filter>
+    <ClCompile Include="GVOWorldMap.cpp">
+      <Filter>本体\地図</Filter>
     </ClCompile>
     <ClCompile Include="GVOSurveyCoordExtractor.cpp">
-      <Filter>ソース ファイル</Filter>
+      <Filter>本体\画像解析</Filter>
     </ClCompile>
-    <ClCompile Include="GVOWorldMap.cpp">
-      <Filter>ソース ファイル</Filter>
+    <ClCompile Include="GVOGameProcess.cpp">
+      <Filter>本体\ゲームプロセス</Filter>
     </ClCompile>
-    <ClCompile Include="GVOShip.cpp">
-      <Filter>ソース ファイル</Filter>
+    <ClCompile Include="GVONavish.cpp">
+      <Filter>本体\アプリケーション</Filter>
+    </ClCompile>
+    <ClCompile Include="stdafx.cpp">
+      <Filter>その他</Filter>
+    </ClCompile>
+    <ClCompile Include="GVORenderer.cpp">
+      <Filter>本体\描画</Filter>
     </ClCompile>
   </ItemGroup>
   <ItemGroup>
     <ResourceCompile Include="GVONavish.rc">
-      <Filter>リソース ファイル</Filter>
+      <Filter>ã\81\9dã\81®ä»\96\83ªã\82½ã\83¼ã\82¹ ã\83\95ã\82¡ã\82¤ã\83«</Filter>
     </ResourceCompile>
   </ItemGroup>
   <ItemGroup>
     <Image Include="small.ico">
-      <Filter>リソース ファイル</Filter>
+      <Filter>ã\81\9dã\81®ä»\96\83ªã\82½ã\83¼ã\82¹ ã\83\95ã\82¡ã\82¤ã\83«</Filter>
     </Image>
     <Image Include="GVONavish.ico">
-      <Filter>リソース ファイル</Filter>
+      <Filter>ã\81\9dã\81®ä»\96\83ªã\82½ã\83¼ã\82¹ ã\83\95ã\82¡ã\82¤ã\83«</Filter>
     </Image>
   </ItemGroup>
   <ItemGroup>
     <Text Include="ReadMe.txt">
-      <Filter>リソース ファイル</Filter>
+      <Filter>ã\81\9dã\81®ä»\96\83ªã\82½ã\83¼ã\82¹ ã\83\95ã\82¡ã\82¤ã\83«</Filter>
     </Text>
   </ItemGroup>
 </Project>
\ No newline at end of file
diff --git a/GVONavish/GVONavish/GVONoncopyable.h b/GVONavish/GVONavish/GVONoncopyable.h
new file mode 100644 (file)
index 0000000..014dd98
--- /dev/null
@@ -0,0 +1,9 @@
+#pragma once
+
+class GVONoncopyable {
+private:
+       GVONoncopyable() = default;
+       GVONoncopyable( const GVONoncopyable & ) = delete;
+       GVONoncopyable& operator=(const GVONoncopyable&) = delete;
+};
+
diff --git a/GVONavish/GVONavish/GVORenderer.cpp b/GVONavish/GVONavish/GVORenderer.cpp
new file mode 100644 (file)
index 0000000..a816082
--- /dev/null
@@ -0,0 +1,424 @@
+#include "stdafx.h"
+#include "GVORenderer.h"
+#include "GVOWorldMap.h"
+#include "GVOConfig.h"
+
+
+
+namespace {
+       const double k_scaleStep = 0.125;       // 12.5%
+       const double k_minScale = 0.125;        // 12.5%
+       const double k_maxScale = 4.00;         // 400%
+
+       // Google\90æ\90\9eH\82­\81u\92n\8b\85\82Ì\8aO\8eü\82Í40,075km\81v\81u1\83m\83b\83g\82Í1.85200km\81v
+       // 1\90¢\8aE\8dÀ\95W\82Í40,075km/16384points
+       // \8eÀ\8e\9e\8aÔ1\95b\82Å\83Q\81[\83\80\93à0.4\8e\9e\8aÔ
+       //
+       // \92n\8b\85\8aO\8eü\82ð\90Ô\93¹\94¼\8ca\82©\82ç\8eZ\8fo\82·\82é\81B
+       // \90Ô\93¹\94¼\8ca\82Í6378.137\82È\82Ì\82Å\8aO\8eü\82Í2*M_PI_*6378.137
+       inline double s_velocityByKnot( const double velocity )
+       {
+               static const double k_knotFactor = (2 * M_PI * 6378.137) / 16384.0 / 0.4 / 1.852;
+               return velocity * k_knotFactor;
+       }
+}
+
+
+void GVORenderer::setup( HDC hdcPrimary )
+{
+       m_hdcPrimary = hdcPrimary;
+}
+
+
+void GVORenderer::teardown()
+{
+       m_hdcPrimary = NULL;
+}
+
+
+void GVORenderer::setWorldMap( const GVOWorldMap& worldMap )
+{
+       m_worldMap = &worldMap;
+       const GVOImage & map = m_worldMap->image();
+       m_mapImage.copy( map );
+}
+
+
+void GVORenderer::setConfig( const GVOConfig& config )
+{
+       m_positionUpdated = config.m_traceShipPositionEnabled;
+       m_focusPointInWorldCoord = config.m_initialSurveyCoord;
+       m_shipPointInWorld = config.m_initialSurveyCoord;
+       m_previousDrawPointInWorld = m_shipPointInWorld;
+       m_shipVectorLineEnabled = config.m_shipVectorLineEnabled;
+       m_speedMeterEnabled = config.m_speedMeterEnabled;
+       m_traceShipEnabled = config.m_traceShipPositionEnabled;
+}
+
+
+void GVORenderer::setViewSize( const SIZE& viewSize )
+{
+       m_viewSize = viewSize;
+}
+
+
+SIZE GVORenderer::scaledMapSize() const
+{
+       SIZE size = {
+               LONG( m_mapImage.width() * m_viewScale ),
+               LONG( m_mapImage.height() * m_viewScale )
+       };
+       return size;
+}
+
+
+POINT GVORenderer::mapOriginInView() const
+{
+       const POINT viewCenter = viewCenterPoint();
+       const SIZE mapSize = scaledMapSize();
+       const POINT worldPosInView = drawOffsetFromWorldCoord( m_focusPointInWorldCoord );
+
+       POINT mapTopLeft = {
+               viewCenter.x - worldPosInView.x,
+               viewCenter.y - worldPosInView.y
+       };
+       if ( m_viewSize.cx < mapSize.cx ) {
+               while ( 0 < mapTopLeft.x ) {
+                       mapTopLeft.x -= mapSize.cx;
+               }
+       }
+
+       return mapTopLeft;
+}
+
+
+void GVORenderer::offsetFocusInViewCoord( const POINT& offset )
+{
+       const double dx = ((double)offset.x / m_viewScale) / m_mapImage.width();
+       const double dy = ((double)offset.y / m_viewScale) / m_mapImage.height();
+
+       LONG x = m_focusPointInWorldCoord.x + LONG( dx * k_worldWidth );
+       LONG y = m_focusPointInWorldCoord.y + LONG( dy * k_worldHeight );
+       y = max( 0, min( y, k_worldHeight ) );
+       while ( x < 0 ) {
+               x += k_worldWidth;
+       }
+       while ( k_worldWidth < x ) {
+               x -= k_worldWidth;
+       }
+
+       m_focusPointInWorldCoord.x = x;
+       m_focusPointInWorldCoord.y = y;
+}
+
+
+bool GVORenderer::zoomIn()
+{
+       double scale = m_viewScale;
+       double step = k_scaleStep;
+
+       scale = m_viewScale + step;
+       if ( k_maxScale < scale ) {
+               scale = k_maxScale;
+       }
+       if ( m_viewScale != scale ) {
+               m_viewScale = scale;
+               return true;
+       }
+       return false;
+}
+
+
+bool GVORenderer::zoomOut()
+{
+       double scale = m_viewScale;
+       double step = k_scaleStep;
+
+       scale = m_viewScale - step;
+       if ( scale < k_minScale ) {
+               scale = k_minScale;
+       }
+       if ( m_viewScale != scale ) {
+               m_viewScale = scale;
+               return true;
+       }
+       return false;
+}
+
+
+void GVORenderer::resetViewScale()
+{
+       m_viewScale = 1.0;
+}
+
+
+POINT GVORenderer::drawOffsetFromWorldCoord( const POINT&worldCoord ) const
+{
+       const POINT worldPosInImage = m_worldMap->imageCoordFromWorldCoord( worldCoord );
+       const POINT drawOffset = {
+               LONG( worldPosInImage.x * m_viewScale ),
+               LONG( worldPosInImage.y * m_viewScale )
+       };
+       return drawOffset;
+}
+
+
+void GVORenderer::updateShipState( const POINT& worldCoord, const GVOVector& shipVector, const double shipVelocity )
+{
+       m_shipVector = shipVector;
+       m_shipVelocity = shipVelocity;
+
+       if ( m_traceShipEnabled ) {
+               m_focusPointInWorldCoord = worldCoord;
+       }
+       if ( m_shipPointInWorld.x != worldCoord.x
+               || m_shipPointInWorld.y != worldCoord.y ) {
+
+               m_positionUpdated = true;
+               m_shipPointInWorld = worldCoord;
+       }
+       updateShipRouteMap();
+}
+
+
+void GVORenderer::updateShipRouteMap()
+{
+       // \88Ú\93®\82µ\82Ä\82¢\82È\82¯\82ê\82Î\89½\82à\82µ\82È\82¢
+       if ( !m_positionUpdated ) {
+               return;
+       }
+       // \88Ú\93®\82µ\82Ä\82¢\82Ä\82à\8dq\98H\82ð\8cq\82°\82È\82¢\82È\82ç\95`\89æ\82µ\82È\82¢
+       if ( !m_linkRoute ) {
+               m_previousDrawPointInWorld = m_shipPointInWorld;
+               m_linkRoute = true;
+               return;
+       }
+
+       const POINT& latestPoint = m_worldMap->imageCoordFromWorldCoord( m_shipPointInWorld );
+       const POINT& previousPoint = m_worldMap->imageCoordFromWorldCoord( m_previousDrawPointInWorld );
+
+       // \95`\89æ\8dÀ\95W\82ª\88ê\8f\8f\82È\82ç\89½\82à\82µ\82È\82¢
+       if ( latestPoint.x == previousPoint.x
+               && latestPoint.y == previousPoint.y ) {
+               return;
+       }
+
+
+       // \8dÅ\90V\8dÀ\95W\82©\82ç\88ê\82Â\91O\82Ì\8dÀ\95W\82Ö\90ü\82ð\88ø\82­
+       // \90¢\8aE\82ð\8c×\82®\8fê\8d\87\82É\8dl\97\82ª\95K\97v\81B
+       // \88Ú\93®\97Ê\82ª\88ê\92è\82ð\92´\82¦\82½\82ç\95`\89æ\8f\88\97\9d\82µ\82È\82¢\82æ\82¤\82É\82·\82é\82Ì\82ª\83X\83}\81[\83g\82©\82È
+
+
+       // \90¢\8aE\82ð\8c×\82¢\82¾\82Æ\82Ý\82È\82·è\87\92l
+       const int k_distanceThreshold = m_mapImage.width() / 2;
+
+       const LONG xMin = min( latestPoint.x, previousPoint.x );
+       const LONG xMax = max( latestPoint.x, previousPoint.x );
+       const int xDistance = xMax - xMin;
+
+
+       HDC hdcMem = ::CreateCompatibleDC( m_hdcPrimary );
+       ::SaveDC( hdcMem );
+
+       HPEN hpen = ::CreatePen( PS_SOLID, 1, RGB( 255, 255, 255 ) );
+
+       ::SelectObject( hdcMem, m_mapImage.bitmapHandle() );
+       ::SelectObject( hdcMem, hpen );
+
+       if ( k_distanceThreshold < xDistance ) {
+               if ( previousPoint.x < latestPoint.x ) {        // \90¢\8aE\82ð\90¼\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
+                       // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
+                       LONG x3 = latestPoint.x - m_mapImage.width();
+
+                       ::MoveToEx( hdcMem, x3, latestPoint.y, NULL );
+                       ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
+
+                       // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
+                       LONG x4 = previousPoint.x + m_mapImage.width();
+                       ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
+                       ::LineTo( hdcMem, x4, previousPoint.y );
+               }
+               else {                          // \90¢\8aE\82ð\93\8c\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
+                       // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
+                       LONG x3 = previousPoint.x - m_mapImage.width();
+
+                       ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
+                       ::LineTo( hdcMem, x3, previousPoint.y );
+
+                       // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
+                       LONG x4 = latestPoint.x + m_mapImage.width();
+                       ::MoveToEx( hdcMem, x4, latestPoint.y, NULL );
+                       ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
+               }
+       }
+       else {
+               // \8c×\82ª\82È\82¢\95`\89æ
+               ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
+               ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
+       }
+
+
+       ::RestoreDC( hdcMem, -1 );
+       ::DeleteDC( hdcMem );
+       ::DeleteObject( hpen );
+
+       m_previousDrawPointInWorld = m_shipPointInWorld;
+}
+
+
+void GVORenderer::clearShipRoute()
+{
+       m_mapImage.copy( m_worldMap->image() );
+}
+
+
+void GVORenderer::render()
+{
+       if ( !m_backBuffer.isCompatible(m_viewSize) ) {
+               m_backBuffer.createDIBImage( m_viewSize );
+       }
+       HDC hdcBackbuffer = ::CreateCompatibleDC( m_hdcPrimary );
+       ::SaveDC( hdcBackbuffer );
+
+       ::SelectObject( hdcBackbuffer, m_backBuffer.bitmapHandle() );
+       RECT rc = { 0, 0, m_backBuffer.width(), m_backBuffer.height()};
+       ::FillRect( hdcBackbuffer, &rc, (HBRUSH)::GetStockObject( BLACK_BRUSH ) );
+
+       drawMap( hdcBackbuffer, m_shipVector );
+
+       if ( m_speedMeterEnabled ) {
+               drawSpeedMeter( hdcBackbuffer, m_shipVelocity );
+       }
+
+       ::BitBlt( m_hdcPrimary, 0, 0, m_backBuffer.width(), m_backBuffer.height(),
+               hdcBackbuffer, 0, 0, SRCCOPY );
+       ::RestoreDC( hdcBackbuffer, -1 );
+       ::DeleteDC( hdcBackbuffer );
+}
+
+
+void GVORenderer::drawMap( HDC hdc, const GVOVector& shipVector )
+{
+       const SIZE mapSize = scaledMapSize();
+       const GVOImage *mapImage = &m_mapImage;
+
+       ::SaveDC( hdc );
+       if ( m_viewScale < 1.0 ) {
+               POINT org;
+               ::GetBrushOrgEx( hdc, &org );
+               ::SetStretchBltMode( hdc, HALFTONE );
+               ::SetBrushOrgEx( hdc, org.x, org.y, NULL );
+       }
+       else {
+               ::SetStretchBltMode( hdc, COLORONCOLOR );
+       }
+
+       HDC hdcMem = ::CreateCompatibleDC( hdc );
+       ::SaveDC( hdcMem );
+
+       ::SelectObject( hdcMem, mapImage->bitmapHandle() );
+
+       const POINT mapTopLeft = mapOriginInView();
+
+       int xDrawOrigin, yDrawOrigin;
+       xDrawOrigin = mapTopLeft.x;
+       yDrawOrigin = mapTopLeft.y;
+
+       if ( 0 < xDrawOrigin ) {
+               xDrawOrigin = (xDrawOrigin % mapSize.cx) - mapSize.cx;
+       }
+       const int xInitial = xDrawOrigin;       // \8d\92[\82Ì\95`\89æ\8aJ\8enx\8dÀ\95W
+       int drawn = xInitial;                           // \95`\89æ\8dÏ\82Ý\8dÀ\95W
+
+       // \90¢\8aE\92n\90}\82ð\89¡\82É\95À\82×\82Ä\95`\89æ
+       // \81i\95`\89æ\8dÅ\93K\89»\82Í\8fÈ\97ª\81j
+       while ( drawn < m_viewSize.cx ) {
+               ::StretchBlt( hdc,
+                       xDrawOrigin, yDrawOrigin,
+                       mapSize.cx, mapSize.cy,
+                       hdcMem,
+                       0, 0,
+                       mapImage->width(), mapImage->height(),
+                       SRCCOPY );
+
+               xDrawOrigin += mapSize.cx;
+               drawn += mapSize.cx;
+       }
+
+
+       const POINT shipPointOffset = drawOffsetFromWorldCoord( m_shipPointInWorld );
+
+       // \90j\98H\97\\91ª\90ü\82ð\95`\89æ
+       if ( shipVector.length() != 0.0 && m_shipVectorLineEnabled ) {
+               const int penWidth = max( 1, int( 1 * m_viewScale ) );
+               HPEN courseLinePen = ::CreatePen( PS_SOLID, penWidth, RGB( 255, 0, 255 ) );
+               HGDIOBJ oldPen = ::SelectObject( hdc, courseLinePen );
+
+               const LONG k_lineLength = k_worldHeight;
+               const POINT reachPointOffset = drawOffsetFromWorldCoord(
+                       shipVector.pointFromOriginWithLength( m_shipPointInWorld, k_lineLength )
+                       );
+
+               // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
+               drawn = xInitial;
+               xDrawOrigin = xInitial;
+               while ( drawn < m_viewSize.cx ) {
+                       const POINT shipPointInView = {
+                               xDrawOrigin + shipPointOffset.x,
+                               yDrawOrigin + shipPointOffset.y
+                       };
+                       const POINT reachPointInView = {
+                               xDrawOrigin + reachPointOffset.x,
+                               yDrawOrigin + reachPointOffset.y
+                       };
+                       ::MoveToEx( hdc, shipPointInView.x, shipPointInView.y, NULL );
+                       ::LineTo( hdc, reachPointInView.x, reachPointInView.y );
+                       xDrawOrigin += mapSize.cx;
+                       drawn += mapSize.cx;
+               }
+
+               ::SelectObject( hdc, oldPen );
+               ::DeleteObject( courseLinePen );
+       }
+
+       // \8e©\91D\82Ì\88Ê\92u\82ð\95`\89æ
+       const SIZE shipMarkSize = { 6, 6 };
+       HBRUSH shipBrush = ::CreateSolidBrush( RGB( 51, 238, 153 ) );
+       HGDIOBJ prevBrush = ::SelectObject( hdc, shipBrush );
+
+       // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
+       drawn = xInitial;
+       xDrawOrigin = xInitial;
+       while ( drawn < m_viewSize.cx ) {
+               ::Ellipse( hdc,
+                       xDrawOrigin + shipPointOffset.x - shipMarkSize.cx / 2,
+                       yDrawOrigin + shipPointOffset.y - shipMarkSize.cy / 2,
+                       xDrawOrigin + shipPointOffset.x + shipMarkSize.cx,
+                       yDrawOrigin + shipPointOffset.y + shipMarkSize.cy );
+
+               xDrawOrigin += mapSize.cx;
+               drawn += mapSize.cx;
+       }
+       ::SelectObject( hdc, prevBrush );
+       ::DeleteObject( shipBrush );
+
+
+       ::RestoreDC( hdcMem, -1 );
+       ::DeleteDC( hdcMem );
+       ::RestoreDC( hdc, -1 );
+}
+
+
+void GVORenderer::drawSpeedMeter( HDC hdc, double shipVelocity )
+{
+       const double velocity = s_velocityByKnot( shipVelocity );
+       wchar_t buf[4096] = { 0 };
+       swprintf( buf, _countof( buf ), L"%.2f kt", velocity );
+
+       RECT rc = { 0 };
+       ::DrawText( hdc, buf, -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_TOP | DT_CALCRECT );
+       const int width = rc.right - rc.left;
+       rc.left = m_viewSize.cx - width;
+       rc.right = rc.left + width;
+       ::DrawText( hdc, buf, -1, &rc, DT_SINGLELINE | DT_RIGHT | DT_TOP );
+}
diff --git a/GVONavish/GVONavish/GVORenderer.h b/GVONavish/GVONavish/GVORenderer.h
new file mode 100644 (file)
index 0000000..307e308
--- /dev/null
@@ -0,0 +1,107 @@
+#pragma once
+#include "GVONoncopyable.h"
+#include "GVOVector.h"
+#include "GVOImage.h"
+
+class GVOConfig;
+class GVOWorldMap;
+
+class GVORenderer :private GVONoncopyable {
+private:
+       const GVOWorldMap * m_worldMap; //!<@brief \90¢\8aE\92n\90}
+       GVOImage m_mapImage;                    //!<@brief \90¢\8aE\92n\90}\89æ\91\9c\8c\93\8dq\90Õ
+
+       HDC m_hdcPrimary;
+       GVOImage m_backBuffer;
+
+       SIZE m_viewSize;
+       double m_viewScale;
+
+       POINT m_focusPointInWorldCoord;         //!<@brief \89æ\96Ê\92\86\89\9b\82É\88Ê\92u\82·\82é\83s\83N\83Z\83\8b\82Ì\90¢\8aE\8dÀ\95W
+       POINT m_shipPointInWorld;                       //!<@brief \8e©\91D\82Ì\88Ê\92u
+       bool m_positionUpdated;                         //!<@brief \8e©\91D\82Ì\88Ê\92u\8dX\90V\8fó\91Ô
+       POINT m_previousDrawPointInWorld;       //!<@brief \92¼\91O\82Ì\95`\89æ\88Ê\92u
+       bool m_linkRoute;                                       //!<@brief \8dq\98H\82ð\8cq\82°\82Ä\95`\89æ\82·\82é\83t\83\89\83O
+       bool m_shipVectorLineEnabled;           //!<@brief \90j\98H\95`\89æ\83t\83\89\83O
+       bool m_speedMeterEnabled;                       //!<@brief \91¬\93x\8cv\95`\89æ\83t\83\89\83O
+       bool m_traceShipEnabled;                        //!<@brief \8e©\91D\88Ê\92u\92Ç\8f]\83t\83\89\83O
+       GVOVector m_shipVector;
+       double m_shipVelocity;
+
+public:
+       GVORenderer() :
+               m_hdcPrimary(),
+               m_viewSize(),
+               m_viewScale(1.0),
+               m_focusPointInWorldCoord(),
+               m_shipPointInWorld(),
+               m_positionUpdated(),
+               m_previousDrawPointInWorld(),
+               m_linkRoute( true ),
+               m_shipVectorLineEnabled( true ),
+               m_speedMeterEnabled( true ),
+               m_traceShipEnabled( true ),
+               m_shipVector(),
+               m_shipVelocity()
+       {
+       }
+       ~GVORenderer()
+       {
+       }
+
+       void setup( HDC hdcPrimary );
+       void teardown();
+
+       void setWorldMap( const GVOWorldMap& worldMap );
+
+       //!@brief \90Ý\92è\8fî\95ñ\82Å\8f\89\8aú\89»\82·\82é
+       void setConfig( const GVOConfig& config );
+
+       void setViewSize( const SIZE& viewSize );
+private:
+       SIZE scaledMapSize() const;
+       POINT mapOriginInView() const;
+
+public:
+       bool zoomIn();
+       bool zoomOut();
+       void resetViewScale();
+
+       inline double viewScale() const
+       {
+               return m_viewScale;
+       }
+
+       //!@note \83h\83\89\83b\83O\8f\88\97\9d\97p
+       void offsetFocusInViewCoord( const POINT& offset );
+
+       //!@brief \8e©\91D\82Ì\8fó\91Ô\82ð\90Ý\92è\82·\82é
+       void updateShipState( const POINT& worldCoord, const GVOVector& shipVector, const double shipVelocity );
+       void enableTraceShip( bool enabled )
+       {
+               m_traceShipEnabled = enabled;
+       }
+       void setVisibleShipRoute( bool visible )
+       {
+               m_shipVectorLineEnabled = visible;
+       }
+       void clearShipRoute();
+       void render();
+       void enableSpeedMeter( bool enabled )
+       {
+               m_speedMeterEnabled = enabled;
+       }
+private:
+       POINT drawOffsetFromWorldCoord( const POINT&worldCoord ) const;
+       void updateShipRouteMap();
+       void drawMap( HDC hdc, const GVOVector& shipVector );
+       void drawSpeedMeter( HDC hdc, double shipVelocity );
+       inline POINT viewCenterPoint() const
+       {
+               POINT p = {
+                       m_viewSize.cx / 2,
+                       m_viewSize.cy / 2
+               };
+               return p;
+       }
+};
index 4349eb0..353d7fd 100644 (file)
@@ -22,17 +22,18 @@ namespace {
        }
 
        // \82»\82Ì\83x\83N\83g\83\8b\82Å\95\\8c»\8fo\97\88\82é\83Q\81[\83\80\93à\95û\8ap\82Ì\95ª\89ð\94\\82ð\8eZ\8fo
+       // \83e\83L\83g\81[\89ß\82¬\82é\82ñ\82Å\82»\82Ì\82¤\82¿\92¼\82·\81B
        inline double s_resolutionForVector( const GVOVector& vector )
        {
-               const double length = ::fabs( vector.length() );
+               const double length = vector.length();
                if ( length == 0.0 ) {
                        return 0.0;
                }
-               if ( length < 2 ) {
-                       return g_radianFromDegree( 90 + 90 / 2 );
-               }
                if ( 90.0 <= length ) {
-                       return M_PI_2 / 90.0;   // \82Q\93x\82ª\8dÅ\8d\82
+                       return g_radianFromDegree( 2 ); // \82Q\93x\82ª\8dÅ\8d\82
+               }
+               if ( length < 2 ) {
+                       return g_radianFromDegree( 90 );
                }
                const double resolution = M_PI_2 / length;
                return resolution;
index f04538e..e668c71 100644 (file)
@@ -2,6 +2,7 @@
 #include <Windows.h>
 #include <vector>
 #include <deque>
+#include "GVONoncopyable.h"
 #include "GVONavish.h"
 #include "GVOVector.h"
 #include "GVOVelocity.h"
@@ -10,7 +11,7 @@
 
 
 
-class GVOShip {
+class GVOShip : private GVONoncopyable {
 private:
        typedef std::deque<GVOVector> VectorArray;
 
@@ -36,12 +37,6 @@ public:
                m_surveyCoord = initialSurveyCoord;
        }
 
-       //!@brief \8e©\91D\82Ì\95û\8cü\82ª\97L\8cø\82©\82Ç\82¤\82©
-       inline bool isVectorEnabled() const
-       {
-               return m_vector.length() != 0.0;
-       }
-
        inline const GVOVector& vector() const
        {
                return m_vector;
@@ -50,17 +45,7 @@ public:
        //!@brief \91ª\97Ê\8dÀ\95W\82É\82æ\82é\8dÅ\90V\88Ê\92u\82ð\8dX\90V
        void updateWithSurveyCoord( const POINT& surveyCoord, const uint32_t timeStamp );
 
-       //!@brief \8c´\93_\82Æ\83x\83N\83g\83\8b\92·\82ð\8ew\92è\82µ\82Ä\8dÀ\95W\82ð\93¾\82é
-       POINT pointFromOriginWithLength( const POINT& origin, const LONG length ) const
-       {
-               GVOVector v = m_vector.normalizedVector();
-               const POINT p = {
-                       origin.x + LONG( v.x() * length ),
-                       origin.y + LONG( v.y() * length )
-               };
-               return p;
-       }
-
+       //!@brief \8e©\91D\82Ì\95b\8aÔ\88Ú\93®\8b\97\97£\81i\90\84\91ª\92l\81j
        inline double velocity() const
        {
                return m_velocityPerSecond.velocity();
index 085e3a0..951ef79 100644 (file)
@@ -1,9 +1,9 @@
 #pragma once
 #include <deque>
 #include <algorithm>
+#include "GVONoncopyable.h"
 
-
-class GVOSpeedMeter {
+class GVOSpeedMeter : private GVONoncopyable {
 private:
        typedef std::deque<double> Array;
        struct VelocityLogItem {
@@ -51,17 +51,9 @@ public:
                m_velocity = fastestVelocity();;
        }
 
-       // Google\90æ\90\9eH\82­\81u\92n\8b\85\82Ì\8aO\8eü\82Í40,075km\81v\81u1\83m\83b\83g\82Í1.85200km\81v
-       // 1\90¢\8aE\8dÀ\95W\82Í40,075km/16384points
-       // \8eÀ\8e\9e\8aÔ1\95b\82Å\83Q\81[\83\80\93à0.4\8e\9e\8aÔ
-       //
-       // \90Ô\93¹\94¼\8ca\82Í6378.137\82Æ\82·\82é\82Æ\8aO\8eü\82Í2*M_PI_*6378.137=40075.016685578483111
-       inline double velocityByKnot() const
+       double velocity() const
        {
-               // 
-               //static const double k_knotFactor = 40075.0 / 16384.0 / 0.4 / 1.85200;
-               static const double k_knotFactor = (2 * M_PI * 6378.137) / 16384.0 / 0.4 / 1.85200;
-               return m_velocity * k_knotFactor;
+               return m_velocity;
        }
 
 private:
index f2b5695..a6eb76f 100644 (file)
@@ -4,10 +4,11 @@
 #include <vector>
 #include <map>
 
+#include "GVONoncopyable.h"
 #include "GVOImage.h"
 
 //!@brief \90\94\92l\82ð\92\8a\8fo\82·\82é\83N\83\89\83X
-class GVOSurveyCoordExtractor {
+class GVOSurveyCoordExtractor : private GVONoncopyable{
 private:
        typedef std::map<const std::string *, int> BitsDictionary;
 
index 474b127..b9d503e 100644 (file)
@@ -97,6 +97,16 @@ public:
                m_length = calcLength( m_x, m_y );
        }
 
+       //!@brief \8c´\93_\82Æ\83x\83N\83g\83\8b\92·\82ð\8ew\92è\82µ\82Ä\8dÀ\95W\82ð\93¾\82é
+       POINT pointFromOriginWithLength( const POINT& origin, const LONG length ) const
+       {
+               GVOVector v = normalizedVector();
+               const POINT p = {
+                       origin.x + LONG( v.x() * length ),
+                       origin.y + LONG( v.y() * length )
+               };
+               return p;
+       }
 private:
        // \83x\83N\83g\83\8b\92·\82ð\8cv\8eZ\82·\82é
        static inline double calcLength(const double x, const double y)
index 7c3fe41..b7da772 100644 (file)
 
 
 
-namespace {
-       const double k_scaleStep = 0.125;       // 12.5%
-       const double k_minScale = 0.125;        // 12.5%
-       const double k_maxScale = 4.00;         // 400%
-}
-
 
-bool GVOWorldMap::loadFromFile( const GVOConfig& config )
+bool GVOWorldMap::loadFromFile( const std::wstring& fileNmee )
 {
+       GVOImage workImage;
        std::wstring filePath;
 
-       filePath = g_makeFullPath( config.m_mapFileName );
-       if ( !m_mapImage.loadFromFile( filePath.c_str() ) ) {
-               filePath = g_makeFullPath( config.m_defaultMapFileName );
-               if ( !m_mapImage.loadFromFile( filePath.c_str() ) ) {
-                       return false;
-               }
+       filePath = g_makeFullPath( fileNmee );
+       if ( !workImage.loadFromFile( filePath.c_str() ) ) {
+               return false;
        }
-
-       // \89æ\91\9c\82Ì\8d\82\82³\82Æ\88Ü\93x\82Ì\94ä\97¦\82Ì\83X\83P\81[\83\8a\83\93\83O\82Ì\8aî\8f\80\82Æ\82È\82é\81B
-       const double mapHeight = m_mapImage.height();
-       m_ratioForImageCoordFromWorldCoord = mapHeight / k_worldHeight;
-
-       SIZE size;
-       size.cx = LONG( m_mapImage.width() * 0.5 );
-       size.cy = LONG( m_mapImage.height() * 0.5 );
-       m_mapImageMipmap1.stretchCopy( m_mapImage, size );
-       size.cx = LONG( m_mapImage.width() * k_minScale );
-       size.cy = LONG( m_mapImage.height() * k_minScale );
-       m_mapImageMipmap2.stretchCopy( m_mapImage, size );
+       m_mapImage.copy( workImage );
+       workImage.reset();
        return true;
 }
 
 
-void GVOWorldMap::setViewSize( const SIZE& viewSize )
-{
-       m_viewSize = viewSize;
-}
-
-
-void GVOWorldMap::offsetFocusInViewCoord( const POINT& offset )
-{
-       const double dx = ((double)offset.x / m_viewScale) / m_mapImage.width();
-       const double dy = ((double)offset.y / m_viewScale) / m_mapImage.height();
-
-       LONG x = m_focusPointInWorldCoord.x + LONG( dx * k_worldWidth );
-       LONG y = m_focusPointInWorldCoord.y + LONG( dy * k_worldHeight );
-       y = max( 0, min( y, k_worldHeight ) );
-       while ( x < 0 ) {
-               x += k_worldWidth;
-       }
-       while ( k_worldWidth < x ) {
-               x -= k_worldWidth;
-       }
-
-       m_focusPointInWorldCoord.x = x;
-       m_focusPointInWorldCoord.y = y;
-}
-
-
-void GVOWorldMap::setConfig( const GVOConfig& config )
-{
-       m_positionUpdated = config.m_traceShipPositionEnabled;
-       m_focusPointInWorldCoord = config.m_initialSurveyCoord;
-       m_shipPointInWorld = config.m_initialSurveyCoord;
-       m_previousDrawPointInWorld = m_shipPointInWorld;
-       m_shipVectorLineEnabled = config.m_shipVectorLineEnabled;
-}
-
-
-void GVOWorldMap::setShipPosition( const POINT& worldCoord, bool isSyncCenter )
-{
-       if ( isSyncCenter ) {
-               m_focusPointInWorldCoord = worldCoord;
-       }
-       if ( m_shipPointInWorld.x != worldCoord.x
-               || m_shipPointInWorld.y != worldCoord.y ) {
-
-               m_positionUpdated = true;
-               m_shipPointInWorld = worldCoord;
-       }
-}
-
-
-bool GVOWorldMap::zoomIn()
-{
-       double scale = m_viewScale;
-       double step = k_scaleStep;
-
-       scale = m_viewScale + step;
-       if ( k_maxScale < scale ) {
-               scale = k_maxScale;
-       }
-       if ( m_viewScale != scale ) {
-               m_viewScale = scale;
-               return true;
-       }
-       return false;
-}
-
-
-bool GVOWorldMap::zoomOut()
-{
-       double scale = m_viewScale;
-       double step = k_scaleStep;
-
-       scale = m_viewScale - step;
-       if ( scale < k_minScale ) {
-               scale = k_minScale;
-       }
-       if ( m_viewScale != scale ) {
-               m_viewScale = scale;
-               return true;
-       }
-       return false;
-}
-
-
-void GVOWorldMap::drawMap( HDC hdc, const GVOShip& ship )
-{
-       const SIZE mapSize = scaledMapSize();
-       GVOImage scaledMapImage;
-       GVOImage *mapImage = &m_mapImage;
-
-       ::SaveDC( hdc );
-       if ( m_viewScale < 1.0 ) {
-               if ( 0.5 <= m_viewScale ) {
-                       mapImage = &m_mapImageMipmap1;
-               }
-               else {
-                       mapImage = &m_mapImageMipmap2;
-               }
-       }
-       ::SetStretchBltMode( hdc, COLORONCOLOR );
-
-       HDC hdcMem = ::CreateCompatibleDC( hdc );
-       ::SaveDC( hdcMem );
-
-       ::SelectObject( hdcMem, mapImage->bitmapHandle() );
-
-       const POINT mapTopLeft = mapOriginInView();
-
-       int xDrawOrigin, yDrawOrigin;
-       xDrawOrigin = mapTopLeft.x;
-       yDrawOrigin = mapTopLeft.y;
-
-       if ( 0 < xDrawOrigin ) {
-               xDrawOrigin = (xDrawOrigin % mapSize.cx) - mapSize.cx;
-       }
-       const int xInitial = xDrawOrigin;       // \8d\92[\82Ì\95`\89æ\8aJ\8enx\8dÀ\95W
-       int drawn = xInitial;                           // \95`\89æ\8dÏ\82Ý\8dÀ\95W
-
-       // \90¢\8aE\92n\90}\82ð\89¡\82É\95À\82×\82Ä\95`\89æ
-       // \81i\95`\89æ\8dÅ\93K\89»\82Í\8fÈ\97ª\81j
-       while ( drawn < m_viewSize.cx ) {
-               ::StretchBlt( hdc,
-                       xDrawOrigin, yDrawOrigin,
-                       mapSize.cx, mapSize.cy,
-                       hdcMem,
-                       0, 0,
-                       mapImage->width(), mapImage->height(),
-                       SRCCOPY );
-
-               xDrawOrigin += mapSize.cx;
-               drawn += mapSize.cx;
-       }
-
-
-       const POINT shipPointOffset = drawOffsetFromWorldCoord( m_shipPointInWorld );
-
-       // \90j\98H\97\\91ª\90ü\82ð\95`\89æ
-       if ( ship.isVectorEnabled() && m_shipVectorLineEnabled ) {
-               const int penWidth = max( 1, int(1 * m_viewScale) );
-               HPEN courseLinePen = ::CreatePen( PS_SOLID, penWidth, RGB( 255, 0, 255 ) );
-               HGDIOBJ oldPen = ::SelectObject( hdc, courseLinePen );
-
-               const LONG k_lineLength = k_worldHeight;
-               const POINT reachPointOffset = drawOffsetFromWorldCoord(
-                       ship.pointFromOriginWithLength( m_shipPointInWorld, k_lineLength )
-                       );
-
-               // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
-               drawn = xInitial;
-               xDrawOrigin = xInitial;
-               while ( drawn < m_viewSize.cx ) {
-                       const POINT shipPointInView = {
-                               xDrawOrigin + shipPointOffset.x,
-                               yDrawOrigin + shipPointOffset.y
-                       };
-                       const POINT reachPointInView = {
-                               xDrawOrigin + reachPointOffset.x,
-                               yDrawOrigin + reachPointOffset.y
-                       };
-                       ::MoveToEx( hdc, shipPointInView.x, shipPointInView.y, NULL );
-                       ::LineTo( hdc, reachPointInView.x, reachPointInView.y );
-                       xDrawOrigin += mapSize.cx;
-                       drawn += mapSize.cx;
-               }
-
-               ::SelectObject( hdc, oldPen );
-               ::DeleteObject( courseLinePen );
-       }
-
-       // \8e©\91D\82Ì\88Ê\92u\82ð\95`\89æ
-       const SIZE shipMarkSize = { 6, 6 };
-       HBRUSH shipBrush = ::CreateSolidBrush( RGB( 51, 238, 153 ) );
-       HGDIOBJ prevBrush = ::SelectObject( hdc, shipBrush );
-
-       // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
-       drawn = xInitial;
-       xDrawOrigin = xInitial;
-       while ( drawn < m_viewSize.cx ) {
-               ::Ellipse( hdc,
-                       xDrawOrigin + shipPointOffset.x - shipMarkSize.cx / 2,
-                       yDrawOrigin + shipPointOffset.y - shipMarkSize.cy / 2,
-                       xDrawOrigin + shipPointOffset.x + shipMarkSize.cx,
-                       yDrawOrigin + shipPointOffset.y + shipMarkSize.cy );
-
-               xDrawOrigin += mapSize.cx;
-               drawn += mapSize.cx;
-       }
-       ::SelectObject( hdc, prevBrush );
-       ::DeleteObject( shipBrush );
-
-
-       ::RestoreDC( hdcMem, -1 );
-       ::DeleteDC( hdcMem );
-       ::RestoreDC( hdc, -1 );
-}
-
-
-void GVOWorldMap::updateShipRouteMap( HDC hdc )
-{
-       // \88Ú\93®\82µ\82Ä\82¢\82È\82¯\82ê\82Î\89½\82à\82µ\82È\82¢
-       if ( !m_positionUpdated ) {
-               return;
-       }
-       // \88Ú\93®\82µ\82Ä\82¢\82Ä\82à\8dq\98H\82ð\8cq\82°\82È\82¢\82È\82ç\95`\89æ\82µ\82È\82¢
-       if ( !m_linkRoute ) {
-               m_previousDrawPointInWorld = m_shipPointInWorld;
-               m_linkRoute = true;
-               return;
-       }
-
-       const POINT& latestPoint = imageCoordFromWorldCoord( m_shipPointInWorld );
-       const POINT& previousPoint = imageCoordFromWorldCoord( m_previousDrawPointInWorld );
-
-       // \95`\89æ\8dÀ\95W\82ª\88ê\8f\8f\82È\82ç\89½\82à\82µ\82È\82¢
-       if ( latestPoint.x == previousPoint.x
-               && latestPoint.y == previousPoint.y ) {
-               return;
-       }
-
-
-       // \8dÅ\90V\8dÀ\95W\82©\82ç\88ê\82Â\91O\82Ì\8dÀ\95W\82Ö\90ü\82ð\88ø\82­
-       // \90¢\8aE\82ð\8c×\82®\8fê\8d\87\82É\8dl\97\82ª\95K\97v\81B
-       // \88Ú\93®\97Ê\82ª\88ê\92è\82ð\92´\82¦\82½\82ç\95`\89æ\8f\88\97\9d\82µ\82È\82¢\82æ\82¤\82É\82·\82é\82Ì\82ª\83X\83}\81[\83g\82©\82È
-
-
-       // \90¢\8aE\82ð\8c×\82¢\82¾\82Æ\82Ý\82È\82·è\87\92l
-       const int k_distanceThreshold = m_mapImage.width() / 2;
-
-       const LONG xMin = min( latestPoint.x, previousPoint.x );
-       const LONG xMax = max( latestPoint.x, previousPoint.x );
-       const int xDistance = xMax - xMin;
-
-
-       HDC hdcMem = ::CreateCompatibleDC( hdc );
-       ::SaveDC( hdcMem );
-
-       HPEN hpen = ::CreatePen( PS_SOLID, 1, RGB( 255, 255, 255 ) );
-
-       ::SelectObject( hdcMem, m_mapImage.bitmapHandle() );
-       ::SelectObject( hdcMem, hpen );
-
-       if ( k_distanceThreshold < xDistance ) {
-               if ( previousPoint.x < latestPoint.x ) {        // \90¢\8aE\82ð\90¼\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
-                       // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
-                       LONG x3 = latestPoint.x - m_mapImage.width();
-
-                       ::MoveToEx( hdcMem, x3, latestPoint.y, NULL );
-                       ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
-
-                       // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
-                       LONG x4 = previousPoint.x + m_mapImage.width();
-                       ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
-                       ::LineTo( hdcMem, x4, previousPoint.y );
-               }
-               else {                          // \90¢\8aE\82ð\93\8c\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
-                       // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
-                       LONG x3 = previousPoint.x - m_mapImage.width();
-
-                       ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
-                       ::LineTo( hdcMem, x3, previousPoint.y );
-
-                       // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
-                       LONG x4 = latestPoint.x + m_mapImage.width();
-                       ::MoveToEx( hdcMem, x4, latestPoint.y, NULL );
-                       ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
-               }
-       }
-       else {
-               // \8c×\82ª\82È\82¢\95`\89æ
-               ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
-               ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
-       }
-
-
-       ::RestoreDC( hdcMem, -1 );
-       ::DeleteDC( hdcMem );
-       ::DeleteObject( hpen );
-
-       m_previousDrawPointInWorld = m_shipPointInWorld;
-}
-
-
-void GVOWorldMap::clearShipRoute()
-{
-       const std::wstring fileName = m_mapImage.fileName();
-       m_mapImage.loadFromFile( fileName );
-}
-
-
-POINT GVOWorldMap::mapOriginInView()
-{
-       const POINT viewCenter = viewCenterPoint();
-       const SIZE mapSize = scaledMapSize();
-       const POINT worldPosInView = drawOffsetFromWorldCoord( m_focusPointInWorldCoord );
-
-       POINT mapTopLeft = {
-               viewCenter.x - worldPosInView.x,
-               viewCenter.y - worldPosInView.y
-       };
-       if ( m_viewSize.cx < mapSize.cx ) {
-               while ( 0 < mapTopLeft.x ) {
-                       mapTopLeft.x -= mapSize.cx;
-               }
-       }
-
-       return mapTopLeft;
-}
-
-
 POINT GVOWorldMap::imageCoordFromWorldCoord( const POINT& worldCoord ) const
 {
        const double xNormPos = worldCoord.x / (double)k_worldWidth;
@@ -359,13 +32,3 @@ POINT GVOWorldMap::imageCoordFromWorldCoord( const POINT& worldCoord ) const
        };
        return worldPosInImage;
 }
-
-POINT GVOWorldMap::drawOffsetFromWorldCoord( const POINT&worldCoord ) const
-{
-       const POINT worldPosInImage = imageCoordFromWorldCoord( worldCoord );
-       const POINT drawOffset = {
-               LONG( worldPosInImage.x * m_viewScale ),
-               LONG( worldPosInImage.y * m_viewScale )
-       };
-       return drawOffset;
-}
index e1971d2..dab2d88 100644 (file)
 #pragma once
-#include <list>
+#include "GVONoncopyable.h"
 #include "GVOImage.h"
-#include "GVOShip.h"
 #include "GVOConfig.h"
+#include "GVOVector.h"
+
+#include "GVORenderer.h"       // \88ê\8e\9e\92u\82«
 
 
 
 
 //!@brief \90¢\8aE\92n\90}
 //!@brief \95\\8e¦\8dÀ\95W\8cn\82Æ\90¢\8aE\8dÀ\95W\8cn\82Ì\95Ï\8a·\82È\82Ç\82ª\8ed\8e\96\81B
-class GVOWorldMap {
+class GVOWorldMap : private GVONoncopyable {
+       friend class GVORenderer;
 public:
 private:
-       //typedef std::list<POINT> ShipRouteType;
-       GVOWorldMap( const GVOWorldMap& );
-       GVOWorldMap& operator=(const GVOWorldMap&);
-
-private:
        GVOImage m_mapImage;
-       GVOImage m_mapImageMipmap1;
-       GVOImage m_mapImageMipmap2;
-       POINT m_focusPointInWorldCoord;
-       SIZE m_viewSize;
-
-       double m_viewScale;
-       double m_ratioForImageCoordFromWorldCoord;      //!<@brief \90¢\8aE\8dÀ\95W\8cn\82©\82ç\89æ\91\9c\8dÀ\95W\8cn\82Ö\82Ì\95Ï\8a·\94ä\97¦
-
-       POINT m_shipPointInWorld;                       //!<@brief \8e©\91D\82Ì\88Ê\92u
-       bool m_positionUpdated;                         //!<@brief \8e©\91D\82Ì\88Ê\92u\8dX\90V\8fó\91Ô
-       POINT m_previousDrawPointInWorld;       //!<@brief \92¼\91O\82Ì\95`\89æ\88Ê\92u
-       bool m_linkRoute;                                       //!<@brief \8dq\98H\82ð\8cq\82°\82Ä\95`\89æ\82·\82é\83t\83\89\83O
-       bool m_shipVectorLineEnabled;           //!<@brief \90j\98H\95`\89æ\83t\83\89\83O
 
 public:
-       GVOWorldMap() :
-               m_viewScale( 1.0 ),
-               m_ratioForImageCoordFromWorldCoord(),
-               m_positionUpdated(),
-               m_linkRoute(),
-               m_shipVectorLineEnabled( true )
+       GVOWorldMap()
        {
        }
 
        virtual ~GVOWorldMap()
        {
-
        }
 
-       bool loadFromFile( const GVOConfig& config );
-       void setViewSize( const SIZE& viewSize );
-       //!@note \83h\83\89\83b\83O\8f\88\97\9d\97p
-       void offsetFocusInViewCoord( const POINT& offset );
-
-       //!@brief \90Ý\92è\8fî\95ñ\82Å\8f\89\8aú\89»\82·\82é
-       void setConfig( const GVOConfig& config );
+       bool loadFromFile( const std::wstring& fileNmee );
 
-       //!@brief \8e©\91D\82Ì\88Ê\92u\82ð\90Ý\92è\82·\82é
-       void setShipPosition( const POINT& worldCoord, bool isSyncCenter );
-
-       bool zoomIn();
-       bool zoomOut();
-       inline double viewScale() const
-       {
-               return m_viewScale;
-       }
-       inline double viewScaleOrder() const
+       const GVOImage& image() const
        {
-               return 100;
+               return m_mapImage;
        }
 
-       //!@note \82Æ\82è\82 \82¦\82¸\95`\89æ\82à\82â\82ç\82µ\82Ä\82¨\82­\81B
-       void drawMap( HDC hdc, const GVOShip& ship );
-       void updateShipRouteMap( HDC hdc );
-       void clearShipRoute();
-
-       void setVisibleShipRoute( bool visible )
-       {
-               m_shipVectorLineEnabled = visible;
-       }
-private:
-       inline POINT viewCenterPoint() const
-       {
-               POINT p = {
-                       m_viewSize.cx / 2,
-                       m_viewSize.cy / 2
-               };
-               return p;
-       }
-       inline SIZE scaledMapSize() const
-       {
-               SIZE size = {
-                       LONG( m_mapImage.width() * m_viewScale ),
-                       LONG( m_mapImage.height() * m_viewScale )
-               };
-               return size;
-       }
-       POINT mapOriginInView();
        POINT imageCoordFromWorldCoord( const POINT& worldCoord ) const;
-       POINT drawOffsetFromWorldCoord( const POINT&worldCoord ) const;
 };
index a432ae2..3ce97d7 100644 (file)
 #define IDM_TOGGLE_KEEP_FOREGROUND 204
 #define IDM_TOGGLE_SPEED_METER 205
 #define IDM_TOGGLE_VECTOR_LINE 206
+#define IDM_TOGGLE_DEBUG_AUTO_CRUISE 207
+#define IDM_SAME_SCALE 208
+#define IDM_ZOOM_IN 209
+#define IDM_ZOOM_OUT 210
 
 #ifndef IDC_STATIC
 #define IDC_STATIC                             -1
index ac525ae..54624a9 100644 (file)
 #define _SECURE_SCL 0
 #endif
 #define WIN32_LEAN_AND_MEAN             // Windows \83w\83b\83_\81[\82©\82ç\8eg\97p\82³\82ê\82Ä\82¢\82È\82¢\95\94\95ª\82ð\8f\9c\8aO\82µ\82Ü\82·\81B
+#define _NO_MIN_MAX
+#include <algorithm>
+using std::min;
+using std::max;
 
 // Windows \83w\83b\83_\81\83t\83@\83C\83\8b:
 #include <windows.h>
@@ -30,3 +34,8 @@
 
 #include <cstdint>
 #include <numeric>
+#include <memory>
+#include <vector>
+#include <map>
+#include <set>
+#include <deque>