diff --git a/application/editormode.h b/application/editormode.h index 7bc24ab0..b911e0e1 100644 --- a/application/editormode.h +++ b/application/editormode.h @@ -1042,6 +1042,7 @@ class editor_mode : public application_mode, private editor_track::observer std::vector rest; // parameters after the rotation bool described{false}; std::string track; + glm::dvec2 tilt{0.0}; // rot.x and rot.z of a described template, degrees }; struct standing_template { diff --git a/application/editormodelineside.cpp b/application/editormodelineside.cpp index 3fdb7131..ea686900 100644 --- a/application/editormodelineside.cpp +++ b/application/editormodelineside.cpp @@ -288,7 +288,7 @@ std::size_t editor_mode::place_templates(std::vector const &Items include_info info; if (false == editor_includes::load(item.file, info, Error)) continue; - directive = editor_includes::directive(item.file, info, editor_includes::parameter_count(item.file), local, static_cast(item.yaw), item.track); + directive = editor_includes::directive(item.file, info, editor_includes::parameter_count(item.file), local, static_cast(item.yaw), item.track, item.tilt); } else { diff --git a/application/editormodeselftest.cpp b/application/editormodeselftest.cpp index b84287c2..bf281bf1 100644 --- a/application/editormodeselftest.cpp +++ b/application/editormodeselftest.cpp @@ -350,6 +350,38 @@ void editor_mode::selftest_step() WriteLog(format("SELFTEST frame %s %.2f: %.3f %.3f %.3f widening %.4f cant %.4f setback %.4f", name.c_str(), station, at.position.x, at.position.y, at.position.z, at.widening[side], at.cant[side], sweep->setback(at))); } } + else if (command == "switchdrive") + { + int index{-1}, side{0}; + words >> index >> side; + scan_switch_drives(); + m_switch.drive = index; + m_switch.drive_side = side; + WriteLog("SELFTEST switchdrive: " + (index >= 0 && index < static_cast(m_switch.drives.size()) ? m_switch.drives[index].name : std::string{"none"})); + } + else if (command == "drivetilt") + { + std::string name; + words >> name; + auto *model{simulation::Instances.find(name)}; + auto *track{simulation::Paths.find(name.substr(0, name.find('_')))}; + if (model == nullptr || track == nullptr) + { + WriteLog("SELFTEST drivetilt: no " + name); + continue; + } + auto const &path{track->m_paths[0]}; + auto const transform{glm::dmat3(model->rotation_scale())}; + auto const across{transform * glm::dvec3{1.0, 0.0, 0.0}}; + auto const forward{transform * glm::dvec3{0.0, 0.0, 1.0}}; + auto const direction{glm::normalize(glm::dvec2{forward.x, forward.z})}; + auto const lateral{across.x * -direction.y + across.z * direction.x}; + auto const cone{-lateral * std::tan(glm::radians(static_cast(path.rolls[0])))}; + auto const control{path.points[segment_data::point::control1]}; + auto const grade{control.y / std::hypot(control.x, control.z)}; + WriteLog(format("SELFTEST drivetilt %s: angles %.3f %.3f %.3f, +x rises %.4f (cone %.4f), +z rises %.4f (grade %.4f), origin %.3f %.3f %.3f, points %.3f", name.c_str(), model->Angles().x, model->Angles().y, model->Angles().z, across.y, cone, forward.y, grade, + model->location().x, model->location().y, model->location().z, path.points[segment_data::point::start].y)); + } else if (command == "fouling") { std::map values; diff --git a/application/editormodetrack.cpp b/application/editormodetrack.cpp index 9ea3a14e..5cd4fdcc 100644 --- a/application/editormodetrack.cpp +++ b/application/editormodetrack.cpp @@ -4747,6 +4747,14 @@ bool editor_mode::place_switch_drive(TTrack &Switch) item.yaw = yaw < 0.0 ? yaw + 360.0 : yaw; item.described = true; item.track = Switch.name(); + { + auto const along{control != glm::dvec3{} ? control : main.points[segment_data::point::end] - start}; + auto const run{glm::length(plan_of(along))}; + auto const roll{static_cast(main.rolls[0])}; + item.tilt = {run > 1e-9 ? -glm::degrees(std::atan(along.y / run)) : 0.0, roll}; + if (Global.bRollFix) + item.location.y += 0.75 * std::abs(std::sin(glm::radians(roll))); + } std::string error; if (place_templates({item}, error) == 0) { diff --git a/editor/editorIncludeInfo.cpp b/editor/editorIncludeInfo.cpp index 8f7fe005..d57da257 100644 --- a/editor/editorIncludeInfo.cpp +++ b/editor/editorIncludeInfo.cpp @@ -644,7 +644,7 @@ std::string compose_directive(std::string const &File, std::vector return text + " end"; } -std::string directive(std::string const &File, include_info const &Info, int const Parameters, glm::dvec3 const &Location, std::optional Yaw, std::string const &Track) +std::string directive(std::string const &File, include_info const &Info, int const Parameters, glm::dvec3 const &Location, std::optional Yaw, std::string const &Track, glm::dvec2 const &Tilt) { std::vector values; for (auto id = 1; id <= Parameters; ++id) @@ -668,6 +668,14 @@ std::string directive(std::string const &File, include_info const &Info, int con { value = number(*Yaw); } + else if (role == "rot.x" && Tilt.x != 0.0) + { + value = number(Tilt.x); + } + else if (role == "rot.z" && Tilt.y != 0.0) + { + value = number(Tilt.y); + } else if (role.starts_with("rot.") && value.empty()) { value = "0"; diff --git a/editor/editorIncludeInfo.hpp b/editor/editorIncludeInfo.hpp index 9f8ef370..d22a9606 100644 --- a/editor/editorIncludeInfo.hpp +++ b/editor/editorIncludeInfo.hpp @@ -71,7 +71,7 @@ bool complete(include_info const &Info, int Parameters, std::string *Issue = nul // builds include directive which places specified template. Location and Yaw are the placement to pass through // the parameters with matching roles, in the coordinates of the place the directive goes to; without Yaw the // rotation parameters receive their default values -std::string directive(std::string const &File, include_info const &Info, int Parameters, glm::dvec3 const &Location, std::optional Yaw, std::string const &Track = {}); +std::string directive(std::string const &File, include_info const &Info, int Parameters, glm::dvec3 const &Location, std::optional Yaw, std::string const &Track = {}, glm::dvec2 const &Tilt = glm::dvec2{0.0}); // splits include directive into the name of the included file and the values of the parameters, both as they're // written. returns: false if the text isn't a complete include directive