OSDN Git Service

refs #2
[gvonavish/GVONavish.git] / GVONavish / GVONavish / GVOWorldMap.cpp
1 #include "stdafx.h"
2 #include <vector>
3
4 #include "GVONavish.h"
5 #include "GVOWorldMap.h"
6
7
8
9 namespace {
10         const double k_scaleStep = 0.125;       // 12.5%
11         const double k_minScale = 0.125;        // 12.5%
12         const double k_maxScale = 4.00;         // 400%
13 }
14
15
16 bool GVOWorldMap::loadFromFile( const GVOConfig& config )
17 {
18         std::wstring filePath;
19
20         filePath = g_makeFullPath( config.m_mapFileName );
21         if ( !m_mapImage.loadFromFile( filePath.c_str() ) ) {
22                 filePath = g_makeFullPath( config.m_defaultMapFileName );
23                 if ( !m_mapImage.loadFromFile( filePath.c_str() ) ) {
24                         return false;
25                 }
26         }
27
28         // \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
29         const double mapHeight = m_mapImage.height();
30         m_ratioForImageCoordFromWorldCoord = mapHeight / k_worldHeight;
31         return true;
32 }
33
34
35 void GVOWorldMap::setViewSize( const SIZE& viewSize )
36 {
37         m_viewSize = viewSize;
38 }
39
40
41 void GVOWorldMap::offsetFocusInViewCoord( const POINT& offset )
42 {
43         const double dx = ((double)offset.x / m_viewScale) / m_mapImage.width();
44         const double dy = ((double)offset.y / m_viewScale) / m_mapImage.height();
45
46         LONG x = m_focusPointInWorldCoord.x + LONG( dx * k_worldWidth );
47         LONG y = m_focusPointInWorldCoord.y + LONG( dy * k_worldHeight );
48         y = max( 0, min( y, k_worldHeight ) );
49         while ( x < 0 ) {
50                 x += k_worldWidth;
51         }
52         while ( k_worldWidth < x ) {
53                 x -= k_worldWidth;
54         }
55
56         m_focusPointInWorldCoord.x = x;
57         m_focusPointInWorldCoord.y = y;
58 }
59
60
61 void GVOWorldMap::setConfig( const GVOConfig& config )
62 {
63         m_positionUpdated = config.m_traceShipPositionEnabled;
64         m_focusPointInWorldCoord = config.m_initialSurveyCoord;
65         m_shipPointInWorld = config.m_initialSurveyCoord;
66         m_previousDrawPointInWorld = m_shipPointInWorld;
67         m_shipVectorLineEnabled = config.m_shipVectorLineEnabled;
68 }
69
70
71 void GVOWorldMap::setShipPosition( const POINT& worldCoord, bool isSyncCenter )
72 {
73         if ( isSyncCenter ) {
74                 m_focusPointInWorldCoord = worldCoord;
75         }
76         if ( m_shipPointInWorld.x != worldCoord.x
77                 || m_shipPointInWorld.y != worldCoord.y ) {
78
79                 m_positionUpdated = true;
80                 m_shipPointInWorld = worldCoord;
81         }
82 }
83
84
85 bool GVOWorldMap::zoomIn()
86 {
87         double scale = m_viewScale;
88         double step = k_scaleStep;
89
90         scale = m_viewScale + step;
91         if ( k_maxScale < scale ) {
92                 scale = k_maxScale;
93         }
94         if ( m_viewScale != scale ) {
95                 m_viewScale = scale;
96                 return true;
97         }
98         return false;
99 }
100
101
102 bool GVOWorldMap::zoomOut()
103 {
104         double scale = m_viewScale;
105         double step = k_scaleStep;
106
107         scale = m_viewScale - step;
108         if ( scale < k_minScale ) {
109                 scale = k_minScale;
110         }
111         if ( m_viewScale != scale ) {
112                 m_viewScale = scale;
113                 return true;
114         }
115         return false;
116 }
117
118
119 void GVOWorldMap::drawMap( HDC hdc, const GVOShip& ship )
120 {
121         const SIZE mapSize = scaledMapSize();
122         GVOImage scaledMapImage;
123         GVOImage *mapImage = &m_mapImage;
124
125         ::SaveDC( hdc );
126         if ( m_viewScale < 1.0 ) {
127                 // \8fk\8f¬\89æ\91\9c\95`\89æ\83R\83X\83g\82ð\8cy\8c¸\82·\82é\88×\82É\83L\83\83\83b\83V\83\85\82ð\8dì\90¬\81B
128                 // \95p\94É\82É\8fk\8f¬\94ä\97¦\82ð\95Ï\8dX\82·\82é\8e\9e\82¾\82¯\83R\83X\83g\82ª\8d\82\82­\95t\82­\82ª\81A
129                 // \96w\82Ç\82Ì\8fê\8d\87\82É\82¨\82¢\82ÄCPU\95\89\89×\82ª\8cy\8c¸\82³\82ê\82é\81B
130                 if ( !m_reducedMapImageCache.isCompatible(mapSize) ) {
131                         m_reducedMapImageCache.stretchCopy( m_mapImage, mapSize );
132                 }
133                 mapImage = &m_reducedMapImageCache;
134         }
135         ::SetStretchBltMode( hdc, COLORONCOLOR );
136
137         HDC hdcMem = ::CreateCompatibleDC( hdc );
138         ::SaveDC( hdcMem );
139
140         ::SelectObject( hdcMem, mapImage->bitmapHandle() );
141
142         const POINT mapTopLeft = mapOriginInView();
143
144         int xDrawOrigin, yDrawOrigin;
145         xDrawOrigin = mapTopLeft.x;
146         yDrawOrigin = mapTopLeft.y;
147
148         if ( 0 < xDrawOrigin ) {
149                 xDrawOrigin = (xDrawOrigin % mapSize.cx) - mapSize.cx;
150         }
151         const int xInitial = xDrawOrigin;       // \8d\92[\82Ì\95`\89æ\8aJ\8enx\8dÀ\95W
152         int drawn = xInitial;                           // \95`\89æ\8dÏ\82Ý\8dÀ\95W
153
154         // \90¢\8aE\92n\90}\82ð\89¡\82É\95À\82×\82Ä\95`\89æ
155         // \81i\95`\89æ\8dÅ\93K\89»\82Í\8fÈ\97ª\81j
156         while ( drawn < m_viewSize.cx ) {
157                 ::StretchBlt( hdc,
158                         xDrawOrigin, yDrawOrigin,
159                         mapSize.cx, mapSize.cy,
160                         hdcMem,
161                         0, 0,
162                         mapImage->width(), mapImage->height(),
163                         SRCCOPY );
164
165                 xDrawOrigin += mapSize.cx;
166                 drawn += mapSize.cx;
167         }
168
169
170         const POINT shipPointOffset = drawOffsetFromWorldCoord( m_shipPointInWorld );
171
172         // \90j\98H\97\\91ª\90ü\82ð\95`\89æ
173         if ( ship.isVectorEnabled() && m_shipVectorLineEnabled ) {
174                 const int penWidth = max( 1, int(1 * m_viewScale) );
175                 HPEN courseLinePen = ::CreatePen( PS_SOLID, penWidth, RGB( 255, 0, 255 ) );
176                 HGDIOBJ oldPen = ::SelectObject( hdc, courseLinePen );
177
178                 const LONG k_lineLength = k_worldHeight;
179                 const POINT reachPointOffset = drawOffsetFromWorldCoord(
180                         ship.pointFromOriginWithLength( m_shipPointInWorld, k_lineLength )
181                         );
182
183                 // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
184                 drawn = xInitial;
185                 xDrawOrigin = xInitial;
186                 while ( drawn < m_viewSize.cx ) {
187                         const POINT shipPointInView = {
188                                 xDrawOrigin + shipPointOffset.x,
189                                 yDrawOrigin + shipPointOffset.y
190                         };
191                         const POINT reachPointInView = {
192                                 xDrawOrigin + reachPointOffset.x,
193                                 yDrawOrigin + reachPointOffset.y
194                         };
195                         ::MoveToEx( hdc, shipPointInView.x, shipPointInView.y, NULL );
196                         ::LineTo( hdc, reachPointInView.x, reachPointInView.y );
197                         xDrawOrigin += mapSize.cx;
198                         drawn += mapSize.cx;
199                 }
200
201                 ::SelectObject( hdc, oldPen );
202                 ::DeleteObject( courseLinePen );
203         }
204
205         // \8e©\91D\82Ì\88Ê\92u\82ð\95`\89æ
206         const SIZE shipMarkSize = { 6, 6 };
207         HBRUSH shipBrush = ::CreateSolidBrush( RGB( 51, 238, 153 ) );
208         HGDIOBJ prevBrush = ::SelectObject( hdc, shipBrush );
209
210         // \8c©\82¦\82Ä\82é\92n\90}\89æ\91\9c\82Ì\95ª\82¾\82¯\95`\89æ\82·\82é
211         drawn = xInitial;
212         xDrawOrigin = xInitial;
213         while ( drawn < m_viewSize.cx ) {
214                 ::Ellipse( hdc,
215                         xDrawOrigin + shipPointOffset.x - shipMarkSize.cx / 2,
216                         yDrawOrigin + shipPointOffset.y - shipMarkSize.cy / 2,
217                         xDrawOrigin + shipPointOffset.x + shipMarkSize.cx,
218                         yDrawOrigin + shipPointOffset.y + shipMarkSize.cy );
219
220                 xDrawOrigin += mapSize.cx;
221                 drawn += mapSize.cx;
222         }
223         ::SelectObject( hdc, prevBrush );
224         ::DeleteObject( shipBrush );
225
226
227         ::RestoreDC( hdcMem, -1 );
228         ::DeleteDC( hdcMem );
229         ::RestoreDC( hdc, -1 );
230 }
231
232
233 void GVOWorldMap::updateShipRouteMap( HDC hdc )
234 {
235         // \88Ú\93®\82µ\82Ä\82¢\82È\82¯\82ê\82Î\89½\82à\82µ\82È\82¢
236         if ( !m_positionUpdated ) {
237                 return;
238         }
239         // \88Ú\93®\82µ\82Ä\82¢\82Ä\82à\8dq\98H\82ð\8cq\82°\82È\82¢\82È\82ç\95`\89æ\82µ\82È\82¢
240         if ( !m_linkRoute ) {
241                 m_previousDrawPointInWorld = m_shipPointInWorld;
242                 m_linkRoute = true;
243                 return;
244         }
245
246         const POINT& latestPoint = imageCoordFromWorldCoord( m_shipPointInWorld );
247         const POINT& previousPoint = imageCoordFromWorldCoord( m_previousDrawPointInWorld );
248
249         // \95`\89æ\8dÀ\95W\82ª\88ê\8f\8f\82È\82ç\89½\82à\82µ\82È\82¢
250         if ( latestPoint.x == previousPoint.x
251                 && latestPoint.y == previousPoint.y ) {
252                 return;
253         }
254
255
256         // \8dÅ\90V\8dÀ\95W\82©\82ç\88ê\82Â\91O\82Ì\8dÀ\95W\82Ö\90ü\82ð\88ø\82­
257         // \90¢\8aE\82ð\8c×\82®\8fê\8d\87\82É\8dl\97\82ª\95K\97v\81B
258         // \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È
259
260
261         // \90¢\8aE\82ð\8c×\82¢\82¾\82Æ\82Ý\82È\82·è\87\92l
262         const int k_distanceThreshold = m_mapImage.width() / 2;
263
264         const LONG xMin = min( latestPoint.x, previousPoint.x );
265         const LONG xMax = max( latestPoint.x, previousPoint.x );
266         const int xDistance = xMax - xMin;
267
268
269         HDC hdcMem = ::CreateCompatibleDC( hdc );
270         ::SaveDC( hdcMem );
271
272         HPEN hpen = ::CreatePen( PS_SOLID, 1, RGB( 255, 255, 255 ) );
273
274         ::SelectObject( hdcMem, m_mapImage.bitmapHandle() );
275         ::SelectObject( hdcMem, hpen );
276
277         if ( k_distanceThreshold < xDistance ) {
278                 if ( previousPoint.x < latestPoint.x ) {        // \90¢\8aE\82ð\90¼\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
279                         // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
280                         LONG x3 = latestPoint.x - m_mapImage.width();
281
282                         ::MoveToEx( hdcMem, x3, latestPoint.y, NULL );
283                         ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
284
285                         // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
286                         LONG x4 = previousPoint.x + m_mapImage.width();
287                         ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
288                         ::LineTo( hdcMem, x4, previousPoint.y );
289                 }
290                 else {                          // \90¢\8aE\82ð\93\8c\82É\8cü\82©\82Á\82Ä\8c×\82¢\82¾
291                         // \8d\82Ì\89æ\96Ê\8aO\82©\82ç\8cü\82©\82¤\90ü\82ð\95`\89æ
292                         LONG x3 = previousPoint.x - m_mapImage.width();
293
294                         ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
295                         ::LineTo( hdcMem, x3, previousPoint.y );
296
297                         // \89E\82Ì\89æ\96Ê\8aO\82É\8cü\82©\82¤\90ü\82ð\95`\89æ
298                         LONG x4 = latestPoint.x + m_mapImage.width();
299                         ::MoveToEx( hdcMem, x4, latestPoint.y, NULL );
300                         ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
301                 }
302         }
303         else {
304                 // \8c×\82ª\82È\82¢\95`\89æ
305                 ::MoveToEx( hdcMem, latestPoint.x, latestPoint.y, NULL );
306                 ::LineTo( hdcMem, previousPoint.x, previousPoint.y );
307         }
308
309
310         ::RestoreDC( hdcMem, -1 );
311         ::DeleteDC( hdcMem );
312         ::DeleteObject( hpen );
313
314         m_previousDrawPointInWorld = m_shipPointInWorld;
315 }
316
317
318 void GVOWorldMap::clearShipRoute()
319 {
320         const std::wstring fileName = m_mapImage.fileName();
321         m_mapImage.loadFromFile( fileName );
322 }
323
324
325 POINT GVOWorldMap::mapOriginInView()
326 {
327         const POINT viewCenter = viewCenterPoint();
328         const SIZE mapSize = scaledMapSize();
329         const POINT worldPosInView = drawOffsetFromWorldCoord( m_focusPointInWorldCoord );
330
331         POINT mapTopLeft = {
332                 viewCenter.x - worldPosInView.x,
333                 viewCenter.y - worldPosInView.y
334         };
335         if ( m_viewSize.cx < mapSize.cx ) {
336                 while ( 0 < mapTopLeft.x ) {
337                         mapTopLeft.x -= mapSize.cx;
338                 }
339         }
340
341         return mapTopLeft;
342 }
343
344
345 POINT GVOWorldMap::imageCoordFromWorldCoord( const POINT& worldCoord ) const
346 {
347         const double xNormPos = worldCoord.x / (double)k_worldWidth;
348         const double yNormPos = worldCoord.y / (double)k_worldHeight;
349         const POINT worldPosInImage = {
350                 LONG( m_mapImage.width() * xNormPos ),
351                 LONG( m_mapImage.height() * yNormPos )
352         };
353         return worldPosInImage;
354 }
355
356 POINT GVOWorldMap::drawOffsetFromWorldCoord( const POINT&worldCoord ) const
357 {
358         const POINT worldPosInImage = imageCoordFromWorldCoord( worldCoord );
359         const POINT drawOffset = {
360                 LONG( worldPosInImage.x * m_viewScale ),
361                 LONG( worldPosInImage.y * m_viewScale )
362         };
363         return drawOffset;
364 }