OSDN Git Service

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