#include "GVOGameProcess.h"
#include "GVOWorldMap.h"
#include "GVOShip.h"
+#include "GVOSpeedMeter.h"
//#define GVO_PERF_CHECK
// \93K\93\96\82É\93®\82«\89ñ\82é\83f\83o\83b\83O\83R\81[\83h
-#define DEBUG_AUTO_CRUISE
+//#define DEBUG_AUTO_CRUISE
static GVOGameProcess s_gvoGameProcess;
static GVOWorldMap s_worldMap;
static GVOShip s_ship;
+static GVOSpeedMeter s_speedMeter;
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 double yAutoCruise = s_config.m_initialSurveyCoord.y;
static const double k_AutoCruiseLength = 3.3;
static const int k_autoCruiseAngle = 12;
-static const DWORD k_refreshIntervalForAutoCruise = 1000;
+static const DWORD k_refreshIntervalForAutoCruise = 1;
static const DWORD k_autoCruiseTurnInterval = 7 * 1000;
static double s_autoCruiseAngle = 0;
-
-
-
-
int APIENTRY _tWinMain( _In_ HINSTANCE hInstance,
_In_opt_ HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine,
LONG( yAutoCruise )
};
s_ship.updateWithSurveyCoord( p );
+ s_speedMeter.updateVelocity( s_ship.velocity(), ::timeGetTime() );
s_gvoGameProcess.setSurveyCoord( p );
s_worldMap.setShipPosition( p, s_config.m_traceShipPositionEnabled );
HDC hdc = ::GetDC( hwnd );
if ( s_isUpdated ) {
s_config.m_initialSurveyCoord = s_gvoGameProcess.surveyCoord();
s_ship.updateWithSurveyCoord( s_gvoGameProcess.surveyCoord() );
+ s_speedMeter.updateVelocity( s_ship.velocity(), s_gvoGameProcess.timeStamp() );
s_worldMap.setShipPosition( s_gvoGameProcess.surveyCoord(), s_config.m_traceShipPositionEnabled );
HDC hdc = ::GetDC( hwnd );
s_worldMap.updateShipRouteMap( hdc );
// \95`\89æ\82ðhdcBackbuffer\82É\91Î\82µ\82Ä\8ds\82¤\81B
s_worldMap.drawMap( hdcBackbuffer, s_ship );
+ // \91¬\93x\8cv\82ð\95`\89æ
+ {
+ 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();
<ClInclude Include="GVOGameProcess.h" />
<ClInclude Include="GVOImage.h" />
<ClInclude Include="GVONavish.h" />
+ <ClInclude Include="GVOSpeedMeter.h" />
<ClInclude Include="GVOSurveyCoordExtractor.h" />
<ClInclude Include="GVOVector.h" />
<ClInclude Include="GVOWorldMap.h" />
<ClInclude Include="GVOVector.h">
<Filter>ヘッダー ファイル</Filter>
</ClInclude>
+ <ClInclude Include="GVOSpeedMeter.h">
+ <Filter>ヘッダー ファイル</Filter>
+ </ClInclude>
</ItemGroup>
<ItemGroup>
<ClCompile Include="stdafx.cpp">
#include "stdafx.h"
-#include "GVOShip.h"
#include <string>
-
+#include "GVOShip.h"
namespace {
// \83Q\81[\83\80\93à\8ap\93x\81i\82Q\93x\90¸\93x\81j\82É\8aÛ\82ß\82é
}
-void GVOShip::updateWithSurveyCoord( const POINT& surveyCoord)
+void GVOShip::updateWithSurveyCoord( const POINT& surveyCoord )
{
const GVOVector v( m_surveyCoord, surveyCoord );
- const double velocity = v.length();
+
+ m_velocity = v.length();
// \88Ú\93®\82µ\82Ä\82È\82¯\82ê\82Î\96³\8e\8b\82·\82é\81B
- if ( velocity == 0.0 ) {
+ if ( v.length() == 0.0 ) {
return;
}
m_surveyCoord = surveyCoord;
}
m_vector = s_roundInGameVector( headVector.normalizedVector() );
- // \8dÅ\90V\82Ì\83x\83N\83g\83\8b\82ª\83p\83\89\83\81\81[\83^\82Æ\93¯\88ê\95û\8cü\82È\82ç\8d\87\90¬\82·\82é\81B\95Ê\95û\8cü\82È\82ç\83\8a\83X\83g\82É\89Á\82¦\82é\81B
- if ( !m_vectorArray.empty() && (m_vectorArray.back().angleTo( v ) == 0.0) ) {
- m_vectorArray.back().composite( v );
- }
- else {
- m_vectorArray.push_back( v );
- }
+ // \8dÅ\90V\82Ì\83x\83N\83g\83\8b\82ð\83\8a\83X\83g\82É\89Á\82¦\82é\81B
+ m_vectorArray.push_back( v );
// \8cv\8eZ\8fã\82Í90\82 \82ê\82Î\8ap\93x\82ª\8eZ\8fo\82Å\82«\82é\82Í\82¸\82¾\82ª\8cë\8d·\82ð\93¥\82Ü\82¦\82Ä2\94{\82Ì\8b\97\97£\82ð\95Û\8e\9d\82µ\82Ä\82¨\82
- if ( 180 < headVector.length() ) {
+ if ( 180 < (headVector.length() - m_vectorArray.front().length()) ) {
m_vectorArray.pop_front();
}
}
GVOVector m_vector; //!<@brief \8e©\91D\82Ì\83x\83N\83g\83\8b
VectorArray m_vectorArray;
+ double m_velocity;
public:
GVOShip() :
- m_surveyCoord()
+ m_surveyCoord(),
+ m_velocity()
{
}
};
return p;
}
+
+ inline double velocity() const
+ {
+ return m_velocity;
+ }
};
--- /dev/null
+#pragma once
+#include <deque>
+#include <algorithm>
+
+
+class GVOSpeedMeter {
+private:
+ typedef std::deque<double> Array;
+ struct VelocityLogItem {
+ uint32_t timeStamp;
+ double velocity;
+
+ VelocityLogItem() :
+ timeStamp(),
+ velocity()
+ {
+ }
+ VelocityLogItem( const uint32_t timeStamp, const double velocity ) :
+ timeStamp( timeStamp ),
+ velocity( velocity )
+ {
+ }
+ };
+ typedef std::deque<VelocityLogItem> VelocityyArray;
+ typedef std::deque<double> VelocityLog;
+ const uint32_t k_velocityMeasuringDistance = 5000;
+
+private:
+ VelocityyArray m_velocityArray;
+ VelocityLog m_velocityLog;
+ double m_velocity;
+
+public:
+
+ GVOSpeedMeter() :
+ m_velocity()
+ {
+ }
+
+ ~GVOSpeedMeter()
+ {
+ }
+
+ inline void updateVelocity( const double velocity, const uint32_t timeStamp )
+ {
+ m_velocityArray.push_back( VelocityLogItem( timeStamp, velocity ) );
+
+ removeOldItem( timeStamp );
+ updateVelocityLog();
+
+ 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
+ {
+ //
+ //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;
+ }
+
+private:
+ inline double calcVelocity()
+ {
+ double velocity = 0.0;
+
+ if ( m_velocityArray.size() < 2 ) {
+ return 0.0;
+ }
+
+ // \88Ú\93®\95½\8bÏ\92l
+ for ( VelocityyArray::const_iterator it = m_velocityArray.begin(); it != m_velocityArray.end(); ++it ) {
+ velocity += it->velocity;
+ }
+ velocity /= m_velocityArray.size();
+
+ // 1\95b\93\96\82½\82è\82Ì\88Ú\93®\97Ê\82ð\8eZ\8fo\82·\82é
+ const uint32_t dt = m_velocityArray.back().timeStamp - m_velocityArray.front().timeStamp;
+ const double normalizedDeltaTime = (double)k_velocityMeasuringDistance / (double)dt;
+ return velocity / normalizedDeltaTime;
+ }
+
+ inline void removeOldItem(const uint32_t timeStamp)
+ {
+ VelocityyArray::const_iterator removeMark = m_velocityArray.end();
+ for ( VelocityyArray::const_iterator it = m_velocityArray.begin(); it != m_velocityArray.end(); ++it ) {
+ const uint32_t dt = timeStamp - it->timeStamp;
+ if ( dt <= k_velocityMeasuringDistance ) {
+ break;
+ }
+ removeMark = it;
+ }
+ if ( removeMark != m_velocityArray.end() ) {
+ m_velocityArray.erase( m_velocityArray.begin(), removeMark );
+ }
+ }
+
+ inline void updateVelocityLog()
+ {
+ m_velocityLog.push_back( calcVelocity() );
+ if ( 3 < m_velocityLog.size() ) {
+ m_velocityLog.pop_front();
+ }
+ }
+
+ inline double fastestVelocity() const
+ {
+ double fastest = 0.0;
+
+ VelocityLog::const_iterator it = std::min_element( m_velocityLog.begin(), m_velocityLog.end() );
+ if ( it != m_velocityLog.end() ) {
+ fastest = *it;
+ }
+ return fastest;
+ }
+};
// \90j\98H\97\\91ª\90ü\82ð\95`\89æ
if ( ship.isVectorEnabled() ) {
const int penWidth = max( 1, int(1 * m_viewScale) );
- HPEN courseLinePen = ::CreatePen( PS_SOLID, penWidth, RGB( 255, 0, 0 ) );
+ HPEN courseLinePen = ::CreatePen( PS_SOLID, penWidth, RGB( 255, 0, 255 ) );
HGDIOBJ oldPen = ::SelectObject( hdc, courseLinePen );
const LONG k_lineLength = k_worldHeight;