Skip to content
Merged
44 changes: 44 additions & 0 deletions .github/workflows/bldwin32-rdeb.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
name: bldwin32-rdeb

on:
workflow_dispatch:
push:
pull_request:

jobs:
build-win32:
runs-on: windows-2022

steps:
- uses: actions/checkout@v4

- name: MSVC dev cmd (x86)
uses: ilammy/msvc-dev-cmd@v1
with:
arch: x86

- name: Install wxWidgets via vcpkg (x86-windows)
uses: lukka/run-vcpkg@v11
with:
vcpkgTriplet: x86-windows
runVcpkgInstall: true
vcpkgJsonGlob: vcpkg.json

- name: Configure (CMake, Win32, RelWithDebInfo)
shell: pwsh
run: |
cmake -S . -B build `
-G "Visual Studio 17 2022" -A Win32 -T v143 `
-DOCPN_TARGET=MSVC `
-DwxWidgets_ROOT_DIR="${env:VCPKG_INSTALLED_DIR}\x86-windows" `
-DwxWidgets_LIB_DIR="${env:VCPKG_INSTALLED_DIR}\x86-windows\lib" `
-DCMAKE_TOOLCHAIN_FILE="${env:VCPKG_ROOT}\scripts\buildsystems\vcpkg.cmake"

- name: Build (RelWithDebInfo)
run: cmake --build build --target package --config RelWithDebInfo

- name: Upload artifact
uses: actions/upload-artifact@v4
with:
name: windows-win32-rdeb-package
path: build/**
1 change: 1 addition & 0 deletions include/ConfigurationDialog.h
Original file line number Diff line number Diff line change
Expand Up @@ -130,6 +130,7 @@ class ConfigurationDialog : public ConfigurationDialogBase {
void OnEndAtWaypoint(wxCommandEvent& event);
void OnAvoidCyclones(wxCommandEvent& event);
void OnUseMotor(wxCommandEvent& event);
void OnUseOptimalAngles(wxCommandEvent& event);
void OnAddDegreeStep(wxCommandEvent& event);
void OnRemoveDegreeStep(wxCommandEvent& event);
void OnClearDegreeSteps(wxCommandEvent& event);
Expand Down
5 changes: 5 additions & 0 deletions include/RouteMap.h
Original file line number Diff line number Diff line change
Expand Up @@ -528,6 +528,11 @@ struct RouteMapConfiguration {
* calculation. The default value is 180 degrees.
*/
double ToDegree;
/**
* Use the optimal angles calculated from the boats's polar instead of
* FromDegree and ToDegree
*/
bool UseOptimalAngles;
/**
* The angular resolution at each step of the route calculation, in degrees.
* Lower values provide finer resolution but increase computation time.
Expand Down
4 changes: 4 additions & 0 deletions include/WeatherRoutingUI.h
Original file line number Diff line number Diff line change
Expand Up @@ -493,6 +493,7 @@ class ConfigurationDialogBase : public wxDialog {
virtual void EnableSpin(wxMouseEvent& event) { event.Skip(); }
virtual void EnableSpinDouble(wxMouseEvent& event) { event.Skip(); }
virtual void OnUpdateSpin(wxSpinEvent& event) { event.Skip(); }
virtual void OnUseOptimalAngles(wxCommandEvent& event) { event.Skip(); }
virtual void OnClose(wxCommandEvent& event) { event.Skip(); }
virtual void OnAvoidCyclones(wxCommandEvent& event) { event.Skip(); }
virtual void OnUseMotor(wxCommandEvent& event) { event.Skip(); }
Expand All @@ -512,6 +513,9 @@ class ConfigurationDialogBase : public wxDialog {
//!< night sailing
wxSpinCtrl* m_sFromDegree; //!< Minimum course relative to true wind.
wxSpinCtrl* m_sToDegree; //!< Maximum course relative to true wind.
wxCheckBox* m_cbUseOptimalAngles; //!< Use polar optimal angles for minimum
//!< and maximum course relative to true
//<! wind
/** The increment course angle when calculating a isochrone route. */
wxSpinCtrlDouble* m_sByDegrees;

Expand Down
21 changes: 21 additions & 0 deletions src/ConfigurationDialog.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,19 @@ void ConfigurationDialog::OnUseMotor(wxCommandEvent& event) {
Update();
}

void ConfigurationDialog::OnUseOptimalAngles(wxCommandEvent& event) {
bool use_optimal_angles = m_cbUseOptimalAngles->IsChecked();
if (use_optimal_angles) {
m_sFromDegree->SetValue(0.0);
m_sToDegree->SetValue(180.0);
m_edited_controls.push_back(m_sFromDegree);
m_edited_controls.push_back(m_sToDegree);
}
m_sFromDegree->Enable(!use_optimal_angles);
m_sToDegree->Enable(!use_optimal_angles);
Update();
}

void ConfigurationDialog::OnBoatFilename(wxCommandEvent& event) {
wxFileDialog openDialog(
this, _("Select Boat File"), wxFileName(m_tBoat->GetValue()).GetPath(),
Expand Down Expand Up @@ -379,6 +392,10 @@ void ConfigurationDialog::SetConfigurations(

SET_SPIN(FromDegree);
SET_SPIN(ToDegree);
SET_CHECKBOX(UseOptimalAngles);
// Enable/disable degree controls based on checkbox state
m_sFromDegree->Enable(!m_cbUseOptimalAngles->IsChecked());
m_sToDegree->Enable(!m_cbUseOptimalAngles->IsChecked());
SET_SPIN_DOUBLE(ByDegrees);

// Motor settings
Expand Down Expand Up @@ -503,6 +520,9 @@ void ConfigurationDialog::OnResetAdvanced(wxCommandEvent& event) {

m_sFromDegree->SetValue(0);
m_sToDegree->SetValue(180);
m_cbUseOptimalAngles->SetValue(false);
m_sFromDegree->Enable(true);
m_sToDegree->Enable(true);
m_sByDegrees->SetValue(5.0);

// Motor settings
Expand Down Expand Up @@ -725,6 +745,7 @@ void ConfigurationDialog::Update() {

GET_SPIN(FromDegree);
GET_SPIN(ToDegree);
GET_CHECKBOX(UseOptimalAngles);
GET_SPIN(ByDegrees);

// Motor settings
Expand Down
81 changes: 78 additions & 3 deletions src/Position.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. *
**************************************************************************/

#include <algorithm>

#include <wx/wx.h>

#include "Position.h"
Expand Down Expand Up @@ -242,12 +244,85 @@ bool Position::Propagate(IsoRouteList& routelist,
bearing2 = heading_resolve(parent_bearing + configuration.MaxSearchAngle);
}

for (const double& twa : configuration.DegreeSteps) {
std::vector<double> degree_steps;
degree_steps.reserve(configuration.DegreeSteps.size());

// Do not waste time exploring directions outside the configured optimal
// angles
if (configuration.UseOptimalAngles) {
int polar_idx = polar;
if (polar_idx < 0) {
// Find a reasonable polar for the first propagation
PolarSpeedStatus status;
polar_idx = configuration.boat.FindBestPolarForCondition(
polar, weather_data.twsOverWater, 90.0, weather_data.swell,
configuration.OptimizeTacking, &status);
if (polar_idx < 0 || status != POLAR_SPEED_SUCCESS) polar_idx = 0;
}
Polar& the_polar = configuration.boat.Polars[polar_idx];

// Note: Optimal angles are in the range of 0 to 360, where values
// greater than 180 are for the port tack
SailingVMG opt_angles = the_polar.GetVMGTrueWind(weather_data.twsOverWater);
opt_angles.values[SailingVMG::PORT_DOWNWIND] -= 360.0;
opt_angles.values[SailingVMG::PORT_UPWIND] -= 360.0;

// If wind is too light etc. there may not be any optimal angles
// Check sanity to avoid indexing beyond limits of DegreeSteps
if (opt_angles.values[SailingVMG::PORT_DOWNWIND] != NAN &&
configuration.DegreeSteps.front() <
opt_angles.values[SailingVMG::PORT_DOWNWIND] &&
configuration.DegreeSteps.back() >
*std::max_element(opt_angles.values, opt_angles.values + 4)) {
size_t step_idx = 0;
while (configuration.DegreeSteps[step_idx] <
opt_angles.values[SailingVMG::PORT_DOWNWIND])
++step_idx;
degree_steps.emplace_back(opt_angles.values[SailingVMG::PORT_DOWNWIND]);
while (configuration.DegreeSteps[step_idx] <
opt_angles.values[SailingVMG::PORT_UPWIND]) {
degree_steps.emplace_back(configuration.DegreeSteps[step_idx]);
++step_idx;
}
degree_steps.emplace_back(opt_angles.values[SailingVMG::PORT_UPWIND]);
while (configuration.DegreeSteps[step_idx] <
opt_angles.values[SailingVMG::STARBOARD_UPWIND])
++step_idx;
degree_steps.emplace_back(
opt_angles.values[SailingVMG::STARBOARD_UPWIND]);
while (configuration.DegreeSteps[step_idx] <
opt_angles.values[SailingVMG::STARBOARD_DOWNWIND]) {
degree_steps.emplace_back(configuration.DegreeSteps[step_idx]);
++step_idx;
}
degree_steps.emplace_back(
opt_angles.values[SailingVMG::STARBOARD_DOWNWIND]);

if (parent != nullptr) {
/* add a position behind the lines to ensure our route intersects
with the previous one to nicely merge the resulting graph */
first_avoid = false;
rp = new Position(this);
double dp = .95;
rp->lat = (1 - dp) * lat + dp * parent->lat;
rp->lon = (1 - dp) * lon + dp * parent->lon;
rp->propagated = true;
rp->prev = rp->next = rp;
points = rp;
++count;
}
} else {
degree_steps = configuration.DegreeSteps;
}
} else
degree_steps = configuration.DegreeSteps;

for (const double& twa : degree_steps) {
double timeseconds = configuration.UsedDeltaTime;
double ctw =
weather_data.twdOverWater + twa; /* rotated relative to true wind */

// Do no waste time exploring directions outside the configured search
// Do not waste time exploring directions outside the configured search
// angle.
if (!std::isnan(bearing1)) {
double bearing3 = heading_resolve(ctw);
Expand Down Expand Up @@ -531,4 +606,4 @@ SkipPosition* SkipPosition::Copy() {
fp->prev = np;
np->next = fp;
return fs;
}
}
3 changes: 3 additions & 0 deletions src/WeatherRouting.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2365,6 +2365,7 @@ bool WeatherRouting::OpenXML(wxString filename, bool reportfailure) {

configuration.FromDegree = AttributeDouble(e, "FromDegree", 0);
configuration.ToDegree = AttributeDouble(e, "ToDegree", 180);
configuration.UseOptimalAngles = AttributeBool(e, "UseOptimalAngles", false);
configuration.ByDegrees = AttributeDouble(e, "ByDegrees", 5.);

// Motor configuration loading
Expand Down Expand Up @@ -2495,6 +2496,7 @@ void WeatherRouting::SaveXML(wxString filename) {

c->SetDoubleAttribute("FromDegree", configuration.FromDegree);
c->SetDoubleAttribute("ToDegree", configuration.ToDegree);
c->SetAttribute("UseOptimalAngles", configuration.UseOptimalAngles);
c->SetDoubleAttribute("ByDegrees", configuration.ByDegrees);

// Motor configuration saving
Expand Down Expand Up @@ -3605,6 +3607,7 @@ RouteMapConfiguration WeatherRouting::DefaultConfiguration() {

configuration.FromDegree = 0;
configuration.ToDegree = 180;
configuration.UseOptimalAngles = false;
configuration.ByDegrees = 5;

return configuration;
Expand Down
16 changes: 16 additions & 0 deletions src/WeatherRoutingUI.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2257,6 +2257,15 @@ ConfigurationDialogBase::ConfigurationDialogBase(wxWindow* parent,
wxSize(140, -1), wxSP_ARROW_KEYS, 0, 180, 180);
bSizer4->Add(m_sToDegree, 0, wxALL | wxALIGN_CENTER_VERTICAL, 5);

m_cbUseOptimalAngles =
new wxCheckBox(sbCourses->GetStaticBox(), wxID_ANY,
_("Use optimal angles"), wxDefaultPosition,
wxDefaultSize, wxCHK_2STATE);
m_cbUseOptimalAngles->SetToolTip(
_("When enabled, uses the optimal upwind and downwind angles "
"calculated from the boat's polar instead of fixed values."));
bSizer4->Add(m_cbUseOptimalAngles, 0, wxALL, 5);

sbCourses->Add(bSizer4, 1, wxEXPAND, 5);

wxBoxSizer* bSizer3;
Expand Down Expand Up @@ -2913,6 +2922,9 @@ ConfigurationDialogBase::ConfigurationDialogBase(wxWindow* parent,
m_sToDegree->Connect(
wxEVT_COMMAND_SPINCTRL_UPDATED,
wxSpinEventHandler(ConfigurationDialogBase::OnUpdateSpin), NULL, this);
m_cbUseOptimalAngles->Connect(
wxEVT_COMMAND_CHECKBOX_CLICKED,
wxCommandEventHandler(ConfigurationDialogBase::OnUseOptimalAngles), NULL, this);
m_sByDegrees->Connect(
wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED,
wxSpinEventHandler(ConfigurationDialogBase::OnUpdateSpin), NULL, this);
Expand Down Expand Up @@ -3545,6 +3557,10 @@ ConfigurationDialogBase::~ConfigurationDialogBase() {
m_sToDegree->Disconnect(
wxEVT_COMMAND_SPINCTRL_UPDATED,
wxSpinEventHandler(ConfigurationDialogBase::OnUpdateSpin), NULL, this);
m_cbUseOptimalAngles->Disconnect(
wxEVT_COMMAND_CHECKBOX_CLICKED,
wxCommandEventHandler(ConfigurationDialogBase::OnUseOptimalAngles), NULL,
this);
m_sByDegrees->Disconnect(
wxEVT_COMMAND_SPINCTRLDOUBLE_UPDATED,
wxCommandEventHandler(ConfigurationDialogBase::OnUpdate), NULL, this);
Expand Down
6 changes: 6 additions & 0 deletions vcpkg.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
{
"name": "weather-routing-pi",
"version-string": "0.0.0",
"builtin-baseline":"26283ac5e8a068561a718ce18b169bfad84c7dab",
"dependencies": ["wxwidgets"]
}
Loading