/* This Source Code Form is subject to the terms of the Mozilla Public License, v. 2.0. If a copy of the MPL was not distributed with this file, You can obtain one at http://mozilla.org/MPL/2.0/. */ #include "stdafx.h" #include "application/editormode.h" #include "editor/editorGeometry.hpp" #include "editor/editorGroundFollow.hpp" #include "editor/editorSettings.hpp" #include "utilities/Globals.h" #include "world/Track.h" #include #include #include namespace { using geometry::plan_distance; using geometry::plan_of; double constexpr kSteepest{1000.0}; // per mille: the grades of the user's own track aren't capped double constexpr kModel{0.15}; // m, how closely the line keeps to the ground, as to the terrain model } // namespace glm::dvec3 editor_mode::cursor_terrain(double const Height) { auto point{cursor_level(Height)}; for (int i = 0; i < 5 && std::isfinite(point.x) && std::isfinite(point.z); ++i) { std::vector found; auto ground{ground_heights({glm::dvec3{point.x, std::max(point.y, static_cast(Global.pCamera.Pos.y)), point.z}}, true, &found).front()}; if (found[0] == 0 && false == nmt_ground(point.x, point.z, ground)) ground = nearby_ground(point); if (std::isnan(ground)) return point; auto const next{cursor_level(ground)}; auto const settled{plan_distance(next, point) < 0.01}; point = {next.x, ground, next.z}; if (settled) break; } return point; } double editor_mode::nearby_ground(glm::dvec3 const &Point) const { auto const ceiling{std::max(Point.y, static_cast(Global.pCamera.Pos.y))}; for (double const radius : {10.0, 25.0, 50.0, 100.0, 200.0, 400.0}) { std::vector probes; for (int i = 0; i < 12; ++i) { auto const angle{glm::two_pi() * i / 12.0}; probes.push_back({Point.x + radius * std::cos(angle), ceiling, Point.z + radius * std::sin(angle)}); } std::vector found; auto const heights{ground_heights(probes, true, &found)}; double sum{0.0}; int count{0}; for (std::size_t i = 0; i < probes.size(); ++i) if (found[i] != 0) { sum += heights[i]; ++count; } if (count > 0) return sum / count; } return std::numeric_limits::quiet_NaN(); } std::pair editor_mode::trackbed(TTrack const *Style) { return Style != nullptr ? profile_ballast(*Style) : std::pair{0.2, 0.5 * 1.435 + 0.5 + 1.1}; } std::vector editor_mode::bed_heights(std::vector const &Points, std::vector const &Directions, TTrack const *Style) const { auto const [depth, half]{trackbed(Style)}; std::vector probes; probes.reserve(Points.size() * 3); for (std::size_t i = 0; i < Points.size(); ++i) { auto const &direction{Directions[i]}; auto const side{glm::length(direction) > 1e-6 ? glm::normalize(glm::dvec2{-direction.y, direction.x}) * half : glm::dvec2{0.0}}; probes.push_back(Points[i]); probes.push_back(Points[i] + glm::dvec3{side.x, 0.0, side.y}); probes.push_back(Points[i] - glm::dvec3{side.x, 0.0, side.y}); } std::vector found; auto heights{ground_heights(probes, true, &found)}; for (std::size_t i = 0; i < probes.size(); ++i) if (found[i] == 0 && nmt_ground(probes[i].x, probes[i].z, heights[i])) found[i] = 1; std::vector result(Points.size(), std::numeric_limits::quiet_NaN()); for (std::size_t i = 0; i < Points.size(); ++i) { auto const axis{found[3 * i] != 0 ? heights[3 * i] : std::numeric_limits::quiet_NaN()}; auto toe{std::numeric_limits::infinity()}; for (std::size_t j = 3 * i + 1; j < 3 * i + 3; ++j) if (found[j] != 0) toe = std::min(toe, heights[j]); if (std::isfinite(toe)) result[i] = std::isnan(axis) ? toe + depth : std::max(toe + depth, axis); else if (false == std::isnan(axis)) result[i] = axis + depth; } return result; } double editor_mode::bed_height(glm::dvec3 const &Point, glm::dvec2 const &Direction, TTrack const *Style) const { auto const height{bed_heights({Point}, {Direction}, Style).front()}; if (false == std::isnan(height)) return height; auto const nearby{nearby_ground(Point)}; return std::isnan(nearby) ? Point.y : nearby + trackbed(Style).first; } void editor_mode::follow_ground(std::vector &Pieces, double const Height0, double const Grade0, double const Height1, double const Grade1, TTrack const *Style) const { // from point to point or level the heights lead from one end to the other, whatever the ground between if (EditorSettings.heights() != editorSettings::lay_heights::fitted) { ground_follow::follow(Pieces, {Height0, Grade0, Height1, Grade1}, [](std::vector const &Points, std::vector const &) { return std::vector(Points.size(), std::numeric_limits::quiet_NaN()); }); return; } auto const grading{laying_grading(Style)}; auto const bed = [&](std::vector const &Points, std::vector const &Directions) { return bed_heights(Points, Directions, Style); }; if (m_follow_preview) ground_follow::follow_meanwhile(Pieces, {Height0, Grade0, Height1, Grade1}, bed, grading, m_fitter); else ground_follow::follow(Pieces, {Height0, Grade0, Height1, Grade1}, bed, grading, &m_fitter); } ground_follow::grading editor_mode::laying_grading(TTrack const *Style) const { // the line goes over the ground as the tracks go onto the terrain: the vertical curves of the norms, the grades as steep as the // ground has them, a rise or a hollow longer than a structure followed closely, the bumps and what parts from it gone past ground_follow::grading grading; grading.settings.norms = m_profile.line.norms; grading.settings.norms.grade_max = kSteepest; grading.settings.norms.curve_threshold = 0.0; grading.settings.speed = design_speed(Style, nullptr); grading.fit.grade_max = kSteepest; grading.fit.tolerance = kModel; grading.fit.follow_length = 60.0; grading.fit.follow_height = 4.0; grading.fit.follow_steepest = 100.0; grading.fit.follow_tolerance = 0.05; grading.reach = kModel; return grading; } glm::dvec3 editor_mode::lay_bed(glm::dvec3 const &Point) { auto const &tool{m_lay}; // level: on at the height the laying goes on from, the first point on the ground if (EditorSettings.heights() == editorSettings::lay_heights::level && false == tool.points.empty()) return {Point.x, tool.points.back().y, Point.z}; TTrack const *style{tool.start.track != nullptr ? tool.start.track : tool.copy_selected ? selected_track() : nullptr}; return {Point.x, bed_height(Point, tool.points.empty() ? glm::dvec2{0.0} : plan_of(Point - tool.points.back()), style), Point.z}; }