From 5e616a61ba40f53767b015ad2e2d6500080ccc40 Mon Sep 17 00:00:00 2001 From: Tobias Weber Date: Sun, 26 Jan 2025 03:50:51 +0100 Subject: [PATCH] a) curve smoothing, b) track distances --- src/gui/docks/track_infos.cpp | 56 +++++++++++- src/gui/docks/track_infos.h | 7 +- src/gui/globals.cpp | 5 +- src/gui/globals.h | 5 +- src/gui/gui.cpp | 16 +++- src/gui/gui.h | 3 +- src/lib/calc.h | 52 +++++++++++ src/lib/map.h | 9 +- src/lib/timepoint.h | 3 + src/lib/track.h | 165 +++++++++++++++++++++++++--------- src/lib/trackdb.h | 23 ++++- 11 files changed, 284 insertions(+), 60 deletions(-) diff --git a/src/gui/docks/track_infos.cpp b/src/gui/docks/track_infos.cpp index ef6063f..5e4b27c 100644 --- a/src/gui/docks/track_infos.cpp +++ b/src/gui/docks/track_infos.cpp @@ -131,6 +131,12 @@ TrackInfos::TrackInfos(QWidget* parent) : QWidget{parent} m_time_check->setChecked(false); connect(m_time_check.get(), &QCheckBox::toggled, this, &TrackInfos::PlotAlt); + m_smooth_check = std::make_shared(alt_panel); + m_smooth_check->setText("Smooth Curve"); + m_smooth_check->setToolTip("Smooth altitude curve."); + m_smooth_check->setChecked(true); + connect(m_smooth_check.get(), &QCheckBox::toggled, this, &TrackInfos::PlotAlt); + QPushButton *btn_replot_alt = new QPushButton(alt_panel); btn_replot_alt->setText("Reset Plot"); btn_replot_alt->setToolTip("Reset the plotting range."); @@ -142,6 +148,7 @@ TrackInfos::TrackInfos(QWidget* parent) : QWidget{parent} alt_panel_layout->setHorizontalSpacing(4); alt_panel_layout->addWidget(m_alt_plot.get(), 0, 0, 1, 4); alt_panel_layout->addWidget(m_time_check.get(), 1, 0, 1, 1); + alt_panel_layout->addWidget(m_smooth_check.get(), 1, 1, 1, 1); alt_panel_layout->addWidget(btn_replot_alt, 1, 3, 1, 1); // pace plot panel @@ -485,7 +492,6 @@ void TrackInfos::PlotAlt() m_alt_plot->xAxis->setLabel("Distance (km)"); QVector dist, alt; - dist.reserve(m_track->GetPoints().size()); alt.reserve(m_track->GetPoints().size()); @@ -495,15 +501,27 @@ void TrackInfos::PlotAlt() for(const t_track_pt& pt : m_track->GetPoints()) { if(plot_time) + { + if(std::abs(pt.elapsed) < g_eps) + continue; dist.push_back(pt.elapsed_total / 60.); + } else + { + if(std::abs(pt.distance) < g_eps) + continue; dist.push_back(pt.distance_total / 1000.); + } alt.push_back(pt.elevation); m_min_alt = std::min(m_min_alt, pt.elevation); m_max_alt = std::max(m_max_alt, pt.elevation); } + // smooth curve + if(m_smooth_check->isChecked() && g_smooth_rad > 0) + alt = smooth_data(alt, g_smooth_rad); + if(!alt.size()) return; @@ -521,7 +539,7 @@ void TrackInfos::PlotAlt() QCPGraph *curve = new QCPGraph(m_alt_plot->xAxis, m_alt_plot->yAxis); curve->setData(dist, alt); - //curve->setLineStyle(QCPCurve::lsLine); + curve->setLineStyle(QCPGraph::lsLine); QPen pen = curve->pen(); pen.setWidthF(2.); @@ -960,7 +978,20 @@ void TrackInfos::TrackPlotMouseMove(QMouseEvent *evt) return; auto [ longitude, latitude ] = GetTrackPlotCoords(evt, true); - emit PlotCoordsChanged(longitude, latitude); + t_real dist = -1., time = -1.; + + if(m_track) + { + auto [ lon_rad, lat_rad ] = GetTrackPlotCoords(evt, false); + + if(const t_track_pt* pt = m_track->GetClosestPoint(lon_rad, lat_rad); pt) + { + dist = pt->distance_total; + time = pt->elapsed_total; + } + } + + emit PlotCoordsChanged(longitude, latitude, dist, time); } @@ -1088,7 +1119,20 @@ void TrackInfos::MapMouseMove(QMouseEvent *evt) return; auto [ lon, lat ] = GetMapCoords(evt, true); - emit PlotCoordsChanged(lon, lat); + t_real dist = -1., time = -1.; + + if(m_track) + { + auto [ lon_rad, lat_rad ] = GetMapCoords(evt, false); + + if(const t_track_pt* pt = m_track->GetClosestPoint(lon_rad, lat_rad); pt) + { + dist = pt->distance_total; + time = pt->elapsed_total; + } + } + + emit PlotCoordsChanged(lon, lat, dist, time); } @@ -1153,6 +1197,7 @@ void TrackInfos::SaveSettings(QSettings& settings) settings.setValue("track_info/distance_bin", m_dist_binlen->value()); settings.setValue("track_info/speed_check", m_speed_check->isChecked()); settings.setValue("track_info/time_check", m_time_check->isChecked()); + settings.setValue("track_info/smooth_check", m_smooth_check->isChecked()); } @@ -1189,4 +1234,7 @@ void TrackInfos::RestoreSettings(QSettings& settings) if(settings.contains("track_info/time_check")) m_time_check->setChecked(settings.value("track_info/time_check").toBool()); + + if(settings.contains("track_info/smooth_check")) + m_smooth_check->setChecked(settings.value("track_info/smooth_check").toBool()); } diff --git a/src/gui/docks/track_infos.h b/src/gui/docks/track_infos.h index 014a4e3..65e871f 100644 --- a/src/gui/docks/track_infos.h +++ b/src/gui/docks/track_infos.h @@ -107,6 +107,7 @@ class TrackInfos : public QWidget // altitude tab std::shared_ptr m_alt_plot{}; std::shared_ptr m_time_check{}; + std::shared_ptr m_smooth_check{}; std::shared_ptr m_alt_context{}; // pace tab @@ -145,10 +146,10 @@ class TrackInfos : public QWidget signals: - void PlotCoordsChanged(t_real, t_real); // mouse moved in track or map - void PlotCoordsSelected(t_real, t_real); // mouse clicked in track or map + void PlotCoordsChanged(t_real, t_real, t_real, t_real); // mouse moved in track or map + void PlotCoordsSelected(t_real, t_real); // mouse clicked in track or map void StatusMessageChanged(const QString&); - void TrackChanged(); // the track has been modified + void TrackChanged(); // the track has been modified }; diff --git a/src/gui/globals.cpp b/src/gui/globals.cpp index c2e5c07..ac58222 100644 --- a/src/gui/globals.cpp +++ b/src/gui/globals.cpp @@ -19,6 +19,9 @@ t_real g_eps = 1e-6; // precision for showing numbers in the gui t_int g_prec_gui = 4; +// radius for data smoothing +t_int g_smooth_rad = 10; + bool g_use_recent_dir = true; bool g_reload_last = true; @@ -28,7 +31,7 @@ bool g_reload_last = true; int g_dist_func = 0; // minimum height difference [m] before being counted as climb -t_real g_asc_eps = 10.; +t_real g_asc_eps = 5.; // assumed time interval if non is given (for import) diff --git a/src/gui/globals.h b/src/gui/globals.h index b513ed2..836bbb8 100644 --- a/src/gui/globals.h +++ b/src/gui/globals.h @@ -19,7 +19,7 @@ // track types using t_tracks = MultipleTracks; using t_track = typename t_tracks::t_track; -using t_track_pt = typename t_track::t_TrackPoint; +using t_track_pt = typename t_track::t_trackpt; // map types @@ -30,6 +30,9 @@ using t_map = Map; extern t_real g_eps; extern t_int g_prec_gui; +// radius for data smoothing +extern t_int g_smooth_rad; + // use recently used directory or a special document folder extern bool g_use_recent_dir; diff --git a/src/gui/gui.cpp b/src/gui/gui.cpp index 177590e..a88fa92 100644 --- a/src/gui/gui.cpp +++ b/src/gui/gui.cpp @@ -602,11 +602,17 @@ void TracksWnd::TrackDeleted(t_size idx) } -void TracksWnd::PlotCoordsChanged(t_real longitude, t_real latitude) +void TracksWnd::PlotCoordsChanged(t_real longitude, t_real latitude, + t_real distance, t_real time) { std::ostringstream ostr; ostr.precision(g_prec_gui); - ostr << "Longitude: " << longitude << "°, Latitude: " << latitude << "°."; + ostr << "Cursor longitude: " << longitude << "°, latitude: " << latitude << "°."; + if(distance >= 0. && time >= 0.) + { + ostr << " Closest track point: " << (distance/1000.) + << " km and " << (time/60.) << " min from start."; + } SetStatusMessage(ostr.str().c_str()); } @@ -840,6 +846,7 @@ bool TracksWnd::ImportFiles(const QStringList& filenames) t_track track{}; track.SetDistanceFunction(g_dist_func); track.SetAscentEpsilon(g_asc_eps); + track.SetSmoothRadius(g_smooth_rad); if(!track.Import(filename.toStdString(), g_assume_dt)) { @@ -876,6 +883,8 @@ void TracksWnd::ShowSettings(bool only_create) "Calculation epsilon:", g_eps, 0., 1., 1e-6, 8); m_settings->AddDoubleSpinbox("settings/epsilon_ascent", "Ascent epsilon:", g_asc_eps, 0.1, 999., 1., 1, " m"); + m_settings->AddSpinbox("settings/smooth_radius", + "Data smooth radius:", g_smooth_rad, 0, 999, 1); m_settings->AddCombobox("settings/distance_function", "Distance calculation:", { "Haversine Formula", "Thomas Formula", @@ -919,6 +928,8 @@ void TracksWnd::ApplySettings() value(); g_asc_eps = m_settings->GetValue("settings/epsilon_ascent"). value(); + g_smooth_rad = m_settings->GetValue("settings/smooth_radius"). + value(); g_dist_func = m_settings->GetValue("settings/distance_function"). value(); g_assume_dt = m_settings->GetValue("settings/assume_dt"). @@ -938,6 +949,7 @@ void TracksWnd::ApplySettings() CreateTempDir(); m_trackdb.SetDistanceFunction(g_dist_func); m_trackdb.SetAscentEpsilon(g_asc_eps); + m_trackdb.SetSmoothRadius(g_smooth_rad); update(); } diff --git a/src/gui/gui.h b/src/gui/gui.h index 3a3d017..28e2a34 100644 --- a/src/gui/gui.h +++ b/src/gui/gui.h @@ -171,7 +171,8 @@ protected slots: void TrackNameChanged(t_size idx, const std::string& name); void TrackDeleted(t_size idx); - void PlotCoordsChanged(t_real longitude, t_real latitude); + void PlotCoordsChanged(t_real longitude, t_real latitude, + t_real distance, t_real time); void SetStatusMessage(const QString& msg, int display_ms = 0) const; void PopulateTrackList(bool resort = true); }; diff --git a/src/lib/calc.h b/src/lib/calc.h index 092f44c..471cfc8 100644 --- a/src/lib/calc.h +++ b/src/lib/calc.h @@ -10,6 +10,8 @@ #include #include +#include +#include #include @@ -21,6 +23,7 @@ */ template t_real havsin(t_real th) +requires std::floating_point { return t_real(0.5) - t_real(0.5)*std::cos(th); } @@ -33,6 +36,7 @@ t_real havsin(t_real th) */ template t_real arcaversin(t_real x) +requires std::floating_point { return std::acos(t_real(1) - t_real(2)*x); } @@ -45,6 +49,7 @@ t_real arcaversin(t_real x) */ template t_real earth_radius(t_real lat) +requires std::floating_point { t_real rad_pol = 6.3567523e6; t_real rad_equ = 6.3781370e6; @@ -73,6 +78,7 @@ std::tuple // [ planar distance, distance including heights ] geo_dist(t_real lat1, t_real lat2, t_real lon1, t_real lon2, t_real elev1, t_real elev2) +requires std::floating_point { t_real rad = earth_radius((lat1 + lat2) / t_real(2)); rad += (elev1 + elev2) * t_real(0.5); @@ -98,6 +104,7 @@ std::tuple // [ planar distance, distance including heights ] geo_dist_2(t_real lat1, t_real lat2, t_real lon1, t_real lon2, [[__maybe_unused__]] t_real elev1, [[__maybe_unused__]] t_real elev2) +requires std::floating_point { namespace geo = boost::geometry; using t_pt = geo::model::point>; @@ -144,9 +151,54 @@ geo_dist_2(t_real lat1, t_real lat2, */ template t_real speed_to_pace(t_real speed) +requires std::floating_point { return t_real(60.) / speed; } + +/** + * smoothing of data points + * see: https://en.wikipedia.org/wiki/Laplacian_smoothing + */ +template +t_cont smooth_data(const t_cont& vec, int N = 1) +requires requires(t_cont cont) +{ + typename t_cont::value_type; + cont.size(); + cont[0] = cont[1]; +} +{ + if(N <= 0) + return vec; + + using t_val = typename t_cont::value_type; + const int SIZE = static_cast(vec.size()); + + t_cont smoothed = vec; + + for(int i = 0; i < SIZE; ++i) + { + t_val elem{}; + t_val num{}; + + for(int j = -N; j <= N; ++j) + { + int idx = i + j; + if(idx >= SIZE || idx < 0) + continue; + + elem += vec[i + j]; + num += 1; + } + + smoothed[i] = elem / num; + } + + return smoothed; +} + + #endif diff --git a/src/lib/map.h b/src/lib/map.h index d214ffa..6f31d0a 100644 --- a/src/lib/map.h +++ b/src/lib/map.h @@ -18,10 +18,11 @@ #include #include #include -#include -#include #include #include +#include +#include +#include #include #include @@ -52,6 +53,7 @@ template +requires std::floating_point struct MapVertex { t_real longitude{}; @@ -64,6 +66,7 @@ struct MapVertex template +requires std::integral struct MapSegment { std::list vertex_ids{}; @@ -76,6 +79,7 @@ struct MapSegment template +requires std::integral struct MapMultiSegment { std::list vertex_ids{}; @@ -97,6 +101,7 @@ enum class MapObjType template +requires std::floating_point && std::integral class Map { public: diff --git a/src/lib/timepoint.h b/src/lib/timepoint.h index a84f067..dbea921 100644 --- a/src/lib/timepoint.h +++ b/src/lib/timepoint.h @@ -12,6 +12,7 @@ #include #include #include +#include #include @@ -162,6 +163,7 @@ std::tuple date_time_from_epoch(t_epoch epoch) template std::string get_time_str(t_real secs) +requires std::floating_point && std::integral { t_int h = std::floor(secs / 60. / 60.); secs = std::fmod(secs, 60. * 60.); @@ -198,6 +200,7 @@ std::string get_time_str(t_real secs) template std::string get_pace_str(t_real min) +requires std::floating_point && std::integral { t_int secs = std::round(std::fmod(min*60., 60.)); t_int mins = std::floor(min); diff --git a/src/lib/track.h b/src/lib/track.h index ff3a58a..c91d629 100644 --- a/src/lib/track.h +++ b/src/lib/track.h @@ -16,6 +16,7 @@ #include #include #include +#include #if __has_include() #include @@ -35,6 +36,7 @@ template +requires std::floating_point struct TrackPoint { t_real latitude{}; // [deg] @@ -43,13 +45,13 @@ struct TrackPoint t_timept timept{}; - t_real elapsed{}; // time elapsed since last point [s] + t_real elapsed{}; // time elapsed since previous point [s] t_real elapsed_total{}; // time elapsed since first point [s] - t_real distance_planar{}; // planar distance to last point [m] + t_real distance_planar{}; // planar distance to previous point [m] t_real distance_planar_total{}; // planar distance to first point [m] - t_real distance{}; // full distance to last point [m] + t_real distance{}; // full distance to previous point [m] t_real distance_total{}; // full distance to first point [m] }; @@ -59,6 +61,7 @@ struct TrackPoint * represents a single running track */ template +requires std::floating_point && std::integral class SingleTrack { public: @@ -67,7 +70,7 @@ class SingleTrack using t_timept = typename t_clk::time_point; using t_dur = typename t_clk::duration; using t_sec = std::chrono::duration>; - using t_TrackPoint = TrackPoint; + using t_trackpt = TrackPoint; using t_char = typename std::string::value_type; @@ -78,6 +81,35 @@ class SingleTrack + /** + * get the function used for distance calculations + */ + std::tuple + (*GetDistanceFunction() const) ( + t_real lat1, t_real lat2, + t_real lon1, t_real lon2, + t_real elev1, t_real elev2) + + { + // default distance function + std::tuple (*dist_func)(t_real lat1, t_real lat2, + t_real lon1, t_real lon2, + t_real elev1, t_real elev2) = &geo_dist; + + //std::cout << "distance function: " << m_distance_function << std::endl; + switch(m_distance_function) + { + case 0: dist_func = &geo_dist; break; + case 1: dist_func = &geo_dist_2; break; + case 2: dist_func = &geo_dist_2; break; + case 3: dist_func = &geo_dist_2; break; + } + + return dist_func; + } + + + /** * calculate track properties */ @@ -99,25 +131,20 @@ class SingleTrack m_max_long = -m_min_long; std::optional latitude_last, longitude_last, elevation_last; - std::optional elevation_last_asc; std::optional time_pt_last; - // get distance function + // distance function std::tuple (*dist_func)(t_real lat1, t_real lat2, t_real lon1, t_real lon2, - t_real elev1, t_real elev2) = &geo_dist; + t_real elev1, t_real elev2) = GetDistanceFunction(); - //std::cout << "distance function: " << m_distance_function << std::endl; - switch(m_distance_function) - { - case 0: dist_func = &geo_dist; break; - case 1: dist_func = &geo_dist_2; break; - case 2: dist_func = &geo_dist_2; break; - case 3: dist_func = &geo_dist_2; break; - } + std::vector elevations; + elevations.reserve(m_points.size()); - for(t_TrackPoint& trackpt : m_points) + for(t_trackpt& trackpt : m_points) { + elevations.push_back(trackpt.elevation); + // elapsed seconds since last track point if(time_pt_last) trackpt.elapsed = t_sec{trackpt.timept - *time_pt_last}.count(); @@ -131,22 +158,6 @@ class SingleTrack *elevation_last, trackpt.elevation); } - // ascent and descent - if(elevation_last_asc) - { - t_real elev_diff = trackpt.elevation - *elevation_last_asc; - if(elev_diff > m_asc_eps) - { - m_ascent += elev_diff; - elevation_last_asc = trackpt.elevation; - } - else if(elev_diff < -m_asc_eps) - { - m_descent += -elev_diff; - elevation_last_asc = trackpt.elevation; - } - } - // cumulative values m_total_time += trackpt.elapsed; m_total_dist += trackpt.distance; @@ -169,9 +180,34 @@ class SingleTrack longitude_last = trackpt.longitude; elevation_last = trackpt.elevation; time_pt_last = trackpt.timept; - if(!elevation_last_asc) // only first point - elevation_last_asc = trackpt.elevation; - } + } // loop over track points + + if(m_smooth_rad > 0) + elevations = smooth_data(elevations, static_cast(m_smooth_rad)); + + // calulate ascent & descent + std::optional elevation_last_asc; + for(t_real elevation : elevations) + { + // ascent and descent + if(elevation_last_asc) + { + t_real elev_diff = elevation - *elevation_last_asc; + if(elev_diff > m_asc_eps) + { + m_ascent += elev_diff; + elevation_last_asc = elevation; + } + else if(elev_diff < -m_asc_eps) + { + m_descent += -elev_diff; + elevation_last_asc = elevation; + } + } + + if(!elevation_last_asc) // only assign at first point + elevation_last_asc = elevation; + } // loop over elevations } @@ -191,7 +227,7 @@ class SingleTrack t_real time = 0., dist = 0.; t_size bin_idx = 0; - for(const t_TrackPoint& pt : m_points) + for(const t_trackpt& pt : m_points) { time += pt.elapsed; dist += planar ? pt.distance_planar : pt.distance; @@ -278,7 +314,7 @@ class SingleTrack if(pt.first != "trkpt") continue; - t_TrackPoint trackpt + t_trackpt trackpt { .latitude = pt.second.get(".lat") / t_real(180) * num::pi_v, .longitude = pt.second.get(".lon") / t_real(180) * num::pi_v, @@ -311,13 +347,43 @@ class SingleTrack - const std::vector& GetPoints() const + const std::vector& GetPoints() const { return m_points; } + /** + * find the track point that is closest to the given coordinates + */ + const t_trackpt* GetClosestPoint(t_real lon, t_real lat) const + { + if(m_points.size() == 0) + return nullptr; + + // distance function + std::tuple (*dist_func)(t_real lat1, t_real lat2, + t_real lon1, t_real lon2, + t_real elev1, t_real elev2) = GetDistanceFunction(); + + auto iter = std::min_element(m_points.begin(), m_points.end(), + [dist_func, lon, lat](const t_trackpt& pt1, const t_trackpt& pt2) + { + auto [ dist1_pl, dist1 ] = (*dist_func)(pt1.latitude, lat, pt1.longitude, lon, 0., 0.); + auto [ dist2_pl, dist2 ] = (*dist_func)(pt2.latitude, lat, pt2.longitude, lon, 0., 0.); + + return dist1_pl < dist2_pl; + }); + + if(iter == m_points.end()) + return nullptr; + + return &*iter; + } + + + std::optional GetStartTime() const { if(m_points.size() == 0) @@ -447,6 +513,16 @@ class SingleTrack + /** + * number of neighbouring point to include in data smoothing + */ + void SetSmoothRadius(t_size rad) + { + m_smooth_rad = rad; + } + + + bool Save(std::ofstream& ofstr) const { if(!ofstr) @@ -460,7 +536,7 @@ class SingleTrack // write track points for(t_size ptidx = 0; ptidx < num_points; ++ptidx) { - const t_TrackPoint& pt = GetPoints()[ptidx]; + const t_trackpt& pt = GetPoints()[ptidx]; ofstr.write(reinterpret_cast(&pt.latitude), sizeof(pt.latitude)); ofstr.write(reinterpret_cast(&pt.longitude), sizeof(pt.longitude)); @@ -524,7 +600,7 @@ class SingleTrack // read track points for(t_size ptidx = 0; ptidx < num_points; ++ptidx) { - t_TrackPoint pt{}; + t_trackpt pt{}; ifstr.read(reinterpret_cast(&pt.latitude), sizeof(pt.latitude)); ifstr.read(reinterpret_cast(&pt.longitude), sizeof(pt.longitude)); @@ -647,7 +723,7 @@ class SingleTrack << std::left << std::setw(field_width) << "t" << " " << std::left << std::setw(field_width) << "s" << "\n"; - for(const t_TrackPoint& pt : track.GetPoints()) + for(const t_trackpt& pt : track.GetPoints()) { t_real latitude_deg = pt.latitude * t_real(180) / num::pi_v; t_real longitude_deg = pt.longitude * t_real(180) / num::pi_v; @@ -698,7 +774,7 @@ class SingleTrack { m_hash = 0; - for(const t_TrackPoint& pt : m_points) + for(const t_trackpt& pt : m_points) { t_size lat_hash = std::hash{}(pt.latitude); t_size lon_hash = std::hash{}(pt.longitude); @@ -715,7 +791,7 @@ class SingleTrack private: - std::vector m_points{}; + std::vector m_points{}; std::string m_filename{}; std::string m_version{}, m_creator{}; @@ -730,7 +806,8 @@ class SingleTrack t_real m_min_elev{}, m_max_elev{}; // minimum height difference [m] before being counted as climb - t_real m_asc_eps{10.}; + t_real m_asc_eps{5.}; + t_size m_smooth_rad{10}; t_real m_ascent{}, m_descent{}; int m_distance_function{0}; diff --git a/src/lib/trackdb.h b/src/lib/trackdb.h index 4baeb97..9524d22 100644 --- a/src/lib/trackdb.h +++ b/src/lib/trackdb.h @@ -14,6 +14,7 @@ #include #include #include +#include #define TRACKDB_MAGIC "TRACKDB" @@ -24,11 +25,11 @@ * represents a collection of running tracks */ template +requires std::floating_point && std::integral class MultipleTracks { public: using t_track = SingleTrack; - using t_TrackPoint = typename t_track::t_TrackPoint; using t_clk = typename t_track::t_clk; using t_timept = typename t_track::t_timept; using t_timept_map = std::map(track)); m_tracks.rbegin()->SetDistanceFunction(m_distance_function); m_tracks.rbegin()->SetAscentEpsilon(m_asc_eps); + m_tracks.rbegin()->SetSmoothRadius(m_smooth_rad); } @@ -76,6 +78,7 @@ class MultipleTracks m_tracks.push_back(track); m_tracks.rbegin()->SetDistanceFunction(m_distance_function); m_tracks.rbegin()->SetAscentEpsilon(m_asc_eps); + m_tracks.rbegin()->SetSmoothRadius(m_smooth_rad); } @@ -206,6 +209,8 @@ class MultipleTracks t_track track{}; track.SetDistanceFunction(m_distance_function); track.SetAscentEpsilon(m_asc_eps); + track.SetSmoothRadius(m_smooth_rad); + if(!track.Load(ifstr)) return false; @@ -241,6 +246,19 @@ class MultipleTracks + /** + * number of neighbouring point to include in data smoothing + */ + void SetSmoothRadius(t_size rad) + { + m_smooth_rad = rad; + + for(t_track& track : m_tracks) + track.SetSmoothRadius(m_smooth_rad); + } + + + /** * total distance of all tracks */ @@ -295,7 +313,8 @@ class MultipleTracks int m_distance_function{0}; - t_real m_asc_eps{10.}; + t_real m_asc_eps{5.}; + t_size m_smooth_rad{10}; };