mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-23 14:55:15 +02:00
feat(editor): W17 placed and checked one by one: a list of the switches without one or with one standing wrong, a click on its point in the view puts or moves that one
- the panel shows the scope and what's found first, then Place and Move for all, then each missing or wrong marker with its own button and a click flying the camera there; the spacing, tolerances, height, model and turn are under Settings - a click on a green point (no marker) or a red one (marker standing wrong) in the view puts or moves that marker alone - the check starts around the camera - one camera flight, fly_to, instead of four copies of it - self-test fouling show=1 and place=one
This commit is contained in:
@@ -675,23 +675,19 @@ void editor_mode::start_focus(scene::basic_node *node, double duration)
|
||||
double const len = glm::length(dir);
|
||||
dir = len > 1e-3 ? dir / len : glm::normalize(glm::dvec3(1.0, 0.5, 1.0));
|
||||
|
||||
// looking from there straight at the object
|
||||
fly_to(center, -dir, distance, duration);
|
||||
}
|
||||
|
||||
void editor_mode::fly_to(glm::dvec3 const &Target, glm::dvec3 const &Look, double const Distance, double const Duration)
|
||||
{
|
||||
m_focus_start_pos = Camera.Pos;
|
||||
m_focus_start_angle = Camera.Angle;
|
||||
m_focus_target_pos = center + dir * distance;
|
||||
|
||||
// target orientation looks from the target position straight at the object
|
||||
glm::dvec3 look = center - m_focus_target_pos;
|
||||
double const looklen = glm::length(look);
|
||||
if (looklen > 1e-6)
|
||||
look /= looklen;
|
||||
m_focus_target_angle = glm::vec3(
|
||||
static_cast<float>(std::asin(glm::clamp(look.y, -1.0, 1.0))), // pitch
|
||||
static_cast<float>(std::atan2(-look.x, -look.z)), // yaw
|
||||
0.0f); // roll
|
||||
|
||||
m_focus_target_pos = Target - Look * Distance;
|
||||
m_focus_target_angle = glm::vec3(static_cast<float>(std::asin(std::clamp(Look.y, -1.0, 1.0))), static_cast<float>(std::atan2(-Look.x, -Look.z)), 0.0f);
|
||||
m_focus_active = true;
|
||||
m_focus_time = 0.0;
|
||||
m_focus_duration = duration;
|
||||
m_focus_duration = Duration;
|
||||
}
|
||||
|
||||
void editor_mode::snap_to_ground(scene::basic_node *node, bool const Alone, bool const Joined)
|
||||
|
||||
@@ -1374,7 +1374,7 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
void draw_track_hover() const;
|
||||
struct track_intent
|
||||
{
|
||||
enum class kind { none, gizmo, select, point, box, straight_set, straight_break, detour, straight_tool, straight_handle, route_vertex, route_grip, switch_at, lay, lay_end, sweep, turntable_place, turntable_exit, turntable_fit, spread, extend_end, signal_place, signal_move, switch_slide, pick_style };
|
||||
enum class kind { none, gizmo, select, point, box, straight_set, straight_break, detour, straight_tool, straight_handle, route_vertex, route_grip, switch_at, lay, lay_end, sweep, turntable_place, turntable_exit, turntable_fit, spread, extend_end, signal_place, signal_move, switch_slide, pick_style, fouling };
|
||||
kind what{kind::none};
|
||||
TTrack *track{nullptr};
|
||||
int path{0};
|
||||
@@ -1447,6 +1447,8 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
void render_track_context();
|
||||
void split_track_at(TTrack &Track, glm::dvec3 const &Point);
|
||||
void focus_track(TTrack &Track, int const Path, glm::dvec3 const &Point);
|
||||
// the camera flies over Duration seconds to the place Distance back from the target along Look, looking along it
|
||||
void fly_to(glm::dvec3 const &Target, glm::dvec3 const &Look, double const Distance, double const Duration = 0.6);
|
||||
// paths found by their names
|
||||
struct track_search
|
||||
{
|
||||
@@ -1737,7 +1739,7 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
{
|
||||
bool open{false};
|
||||
bool expand{false};
|
||||
int scope{0}; // 0: the selected switch, 1: around the camera, 2: the whole scenery
|
||||
int scope{1}; // 0: the selected switch, 1: around the camera, 2: the whole scenery
|
||||
double reach{1500.0};
|
||||
double spacing{3.5}; // m between the axes
|
||||
double tolerance{2.0}; // m, a marker farther than this from its point is wrong
|
||||
@@ -1756,8 +1758,13 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
};
|
||||
fouling_tool m_fouling;
|
||||
void fouling_update();
|
||||
void fouling_place();
|
||||
void fouling_fix();
|
||||
// the markers put at the fouling points without one, or at the one only
|
||||
void fouling_place(int const Only = -1);
|
||||
// the markers standing wrong moved to their places, or the one found only
|
||||
void fouling_fix(int const Only = -1);
|
||||
// the fouling point without a marker or the marker standing wrong under the cursor: the index of the point or of the
|
||||
// marker found, and whether it's a marker
|
||||
std::optional<std::pair<int, bool>> fouling_hit() const;
|
||||
void render_fouling_ui();
|
||||
void draw_fouling_overlay() const;
|
||||
|
||||
|
||||
@@ -677,13 +677,7 @@ void editor_mode::gauge_focus(int const Index)
|
||||
look = glm::length(look) > 1e-6 ? glm::normalize(look) : glm::dvec3(0.0, 0.0, 1.0);
|
||||
if (glm::dot(Camera.Pos - hit.point, look) > 0.0)
|
||||
look = -look;
|
||||
m_focus_start_pos = Camera.Pos;
|
||||
m_focus_start_angle = Camera.Angle;
|
||||
m_focus_target_pos = hit.point - look * gauge_view_distance;
|
||||
m_focus_target_angle = glm::vec3(static_cast<float>(std::asin(std::clamp(look.y, -1.0, 1.0))), static_cast<float>(std::atan2(-look.x, -look.z)), 0.0f);
|
||||
m_focus_active = true;
|
||||
m_focus_time = 0.0;
|
||||
m_focus_duration = 0.6;
|
||||
fly_to(hit.point, look, gauge_view_distance);
|
||||
}
|
||||
|
||||
void editor_mode::gauge_spot(gauge_hit const &Hit)
|
||||
|
||||
@@ -38,6 +38,7 @@ using geometry::plan_of;
|
||||
double const kSampleStep{2.0}; // m between the samples of the line
|
||||
double const kLineCorridor{15.0}; // m from the axis, posts farther away belong to another line
|
||||
double const kOverlayRange{3000.0};
|
||||
float const kFoulingPick{12.0f}; // px from a point of the fouling overlay a click takes it
|
||||
double const kFoulingTrace{250.0}; // m from the start of a switch the tracks are followed for
|
||||
double const kRailHeight{0.18}; // m from the points of a track to the heads of its rails
|
||||
int const kRailCategory{1};
|
||||
@@ -666,14 +667,7 @@ void editor_mode::render_hekto_ui()
|
||||
auto const label{post.duplicate ? format(STR_C("km %s duplicate##%u"), km_text(post.hectometres).c_str(), post.post.instance) : format(STR_C("km %s stands %.1f m from its place##%u"), km_text(post.hectometres).c_str(), post.distance, post.post.instance)};
|
||||
if (ImGui::Selectable(label.c_str()))
|
||||
{
|
||||
glm::dvec3 const look{glm::normalize(glm::dvec3{-1.0, -0.8, -1.0})};
|
||||
m_focus_start_pos = Camera.Pos;
|
||||
m_focus_start_angle = Camera.Angle;
|
||||
m_focus_target_pos = post.post.location - look * 20.0;
|
||||
m_focus_target_angle = glm::vec3(static_cast<float>(std::asin(look.y)), static_cast<float>(std::atan2(-look.x, -look.z)), 0.0f);
|
||||
m_focus_active = true;
|
||||
m_focus_time = 0.0;
|
||||
m_focus_duration = 0.6;
|
||||
fly_to(post.post.location, glm::normalize(glm::dvec3{-1.0, -0.8, -1.0}), 20.0);
|
||||
}
|
||||
}
|
||||
ImGui::EndChild();
|
||||
@@ -832,14 +826,15 @@ void editor_mode::fouling_update()
|
||||
tool.status = format(STR_C("%zu switches, %zu fouling points: %d without a marker, %d markers standing wrong"), switches.size(), tool.points.size(), static_cast<int>(missing), static_cast<int>(wrong));
|
||||
}
|
||||
|
||||
void editor_mode::fouling_place()
|
||||
void editor_mode::fouling_place(int const Only)
|
||||
{
|
||||
auto &tool{m_fouling};
|
||||
fouling_update();
|
||||
std::vector<template_item> items;
|
||||
for (auto const &point : tool.points)
|
||||
for (int i = 0; i < static_cast<int>(tool.points.size()); ++i)
|
||||
{
|
||||
if (point.standing >= 0)
|
||||
auto const &point{tool.points[i]};
|
||||
if (point.standing >= 0 || (Only >= 0 && i != Only))
|
||||
continue;
|
||||
template_item item;
|
||||
item.file = kFoulingModels[std::clamp(tool.model, 0, 3)];
|
||||
@@ -856,14 +851,15 @@ void editor_mode::fouling_place()
|
||||
tool.dirty = true;
|
||||
}
|
||||
|
||||
void editor_mode::fouling_fix()
|
||||
void editor_mode::fouling_fix(int const Only)
|
||||
{
|
||||
auto &tool{m_fouling};
|
||||
fouling_update();
|
||||
std::vector<std::pair<standing_template, template_item>> moves;
|
||||
for (auto const &found : tool.found)
|
||||
for (int i = 0; i < static_cast<int>(tool.found.size()); ++i)
|
||||
{
|
||||
if (false == found.wrong)
|
||||
auto const &found{tool.found[i]};
|
||||
if (false == found.wrong || (Only >= 0 && i != Only))
|
||||
continue;
|
||||
template_item item;
|
||||
auto const &point{tool.points[found.point]};
|
||||
@@ -886,10 +882,37 @@ void editor_mode::fouling_fix()
|
||||
tool.dirty = true;
|
||||
}
|
||||
|
||||
std::optional<std::pair<int, bool>> editor_mode::fouling_hit() const
|
||||
{
|
||||
auto const &tool{m_fouling};
|
||||
screen_projection const projection;
|
||||
auto const &mouse{ImGui::GetIO().MousePos};
|
||||
glm::dvec3 const camera{Global.pCamera.Pos};
|
||||
std::optional<std::pair<int, bool>> result;
|
||||
auto best{kFoulingPick * kFoulingPick};
|
||||
auto const consider = [&](glm::dvec3 const &Position, int const Index, bool const Marker) {
|
||||
ImVec2 screen;
|
||||
if (glm::distance(Position, camera) > kOverlayRange || false == projection.project(Position, screen))
|
||||
return;
|
||||
auto const distance2{(screen.x - mouse.x) * (screen.x - mouse.x) + (screen.y - mouse.y) * (screen.y - mouse.y)};
|
||||
if (distance2 < best)
|
||||
{
|
||||
best = distance2;
|
||||
result = std::make_pair(Index, Marker);
|
||||
}
|
||||
};
|
||||
for (int i = 0; i < static_cast<int>(tool.points.size()); ++i)
|
||||
if (tool.points[i].standing < 0)
|
||||
consider(tool.points[i].position, i, false);
|
||||
for (int i = 0; i < static_cast<int>(tool.found.size()); ++i)
|
||||
if (tool.found[i].wrong)
|
||||
consider(tool.found[i].marker.location, i, true);
|
||||
return result;
|
||||
}
|
||||
|
||||
void editor_mode::render_fouling_ui()
|
||||
{
|
||||
auto &tool{m_fouling};
|
||||
ImGui::TextDisabled("%s", STR_C("W17 where the axes of the tracks meeting at a switch are the spacing apart,\nhalf way between them"));
|
||||
auto const changed = [&](bool const Changed) {
|
||||
if (Changed)
|
||||
tool.dirty = true;
|
||||
@@ -899,54 +922,92 @@ void editor_mode::render_fouling_ui()
|
||||
changed(ImGui::RadioButton(STR_C("Around the camera"), &tool.scope, 1));
|
||||
ImGui::SameLine();
|
||||
changed(ImGui::RadioButton(STR_C("Whole scenery"), &tool.scope, 2));
|
||||
ImGui::PushItemWidth(110.0f);
|
||||
if (tool.scope == 1)
|
||||
changed(ImGui::InputDouble(STR_C("Around the camera (m)"), &tool.reach, 0.0, 0.0, "%.0f"));
|
||||
changed(ImGui::InputDouble(STR_C("Spacing of the axes (m)"), &tool.spacing, 0.0, 0.0, "%.2f"));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", STR_C("3.5 m on the straight track; more on the curves, where the cars lean out"));
|
||||
changed(ImGui::InputDouble(STR_C("A marker off by more (m) is wrong"), &tool.tolerance, 0.0, 0.0, "%.1f"));
|
||||
changed(ImGui::RadioButton(STR_C("On the ground"), &tool.height_mode, 0));
|
||||
ImGui::SameLine();
|
||||
changed(ImGui::RadioButton(STR_C("Over the rail heads"), &tool.height_mode, 1));
|
||||
if (tool.height_mode == 1)
|
||||
fouling_update();
|
||||
if (false == tool.error.empty())
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.8f, 0.3f, 1.0f), "%s", tool.error.c_str());
|
||||
else
|
||||
{
|
||||
ImGui::TextWrapped("%s", tool.status.c_str());
|
||||
auto const missing{std::count_if(tool.points.begin(), tool.points.end(), [](fouling_point const &Point) { return Point.standing < 0; })};
|
||||
auto const wrong{std::count_if(tool.found.begin(), tool.found.end(), [](fouling_found const &Found) { return Found.wrong; })};
|
||||
if (missing > 0 && ImGui::Button(format(STR_C("Place %d markers"), static_cast<int>(missing)).c_str()))
|
||||
fouling_place();
|
||||
if (wrong > 0)
|
||||
{
|
||||
if (missing > 0)
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(format(STR_C("Move %d wrong markers to their places"), static_cast<int>(wrong)).c_str()))
|
||||
fouling_fix();
|
||||
}
|
||||
if (missing + wrong > 0)
|
||||
{
|
||||
// each one: a click flies there, the button fixes it alone; in the view a click on its point does the same
|
||||
ImGui::TextDisabled("%s", STR_C("Click the green or red point in the view: that one only"));
|
||||
ImGui::BeginChild("##foulinglist", ImVec2(0.0f, std::min(8, static_cast<int>(missing + wrong)) * ImGui::GetFrameHeightWithSpacing() + 6.0f), true);
|
||||
auto const look{glm::normalize(glm::dvec3{-1.0, -0.8, -1.0})};
|
||||
for (int i = 0; i < static_cast<int>(tool.points.size()); ++i)
|
||||
{
|
||||
auto const &point{tool.points[i]};
|
||||
if (point.standing >= 0)
|
||||
continue;
|
||||
ImGui::PushID(i);
|
||||
if (ImGui::SmallButton(STR_C("Place")))
|
||||
fouling_place(i);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable(format(STR_C("%s: no W17"), point.track->name().c_str()).c_str()))
|
||||
fly_to(point.position, look, 25.0);
|
||||
ImGui::PopID();
|
||||
}
|
||||
for (int i = 0; i < static_cast<int>(tool.found.size()); ++i)
|
||||
{
|
||||
auto const &found{tool.found[i]};
|
||||
if (false == found.wrong)
|
||||
continue;
|
||||
ImGui::PushID(1000000 + i);
|
||||
if (ImGui::SmallButton(STR_C("Fix")))
|
||||
fouling_fix(i);
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Selectable(format(STR_C("%s: W17 %.1f m off, %+.2f m high"), tool.points[found.point].track->name().c_str(), found.error, found.height_error).c_str()))
|
||||
fly_to(found.marker.location, look, 25.0);
|
||||
ImGui::PopID();
|
||||
}
|
||||
ImGui::EndChild();
|
||||
}
|
||||
}
|
||||
if (ImGui::CollapsingHeader(STR_C("Settings##fouling")))
|
||||
{
|
||||
ImGui::TextDisabled("%s", STR_C("W17 where the axes of the tracks meeting at a switch are the spacing apart,\nhalf way between them"));
|
||||
ImGui::PushItemWidth(110.0f);
|
||||
if (tool.scope == 1)
|
||||
changed(ImGui::InputDouble(STR_C("Around the camera (m)"), &tool.reach, 0.0, 0.0, "%.0f"));
|
||||
changed(ImGui::InputDouble(STR_C("Spacing of the axes (m)"), &tool.spacing, 0.0, 0.0, "%.2f"));
|
||||
if (ImGui::IsItemHovered())
|
||||
ImGui::SetTooltip("%s", STR_C("3.5 m on the straight track; more on the curves, where the cars lean out"));
|
||||
changed(ImGui::InputDouble(STR_C("A marker off by more (m) is wrong"), &tool.tolerance, 0.0, 0.0, "%.1f"));
|
||||
changed(ImGui::RadioButton(STR_C("On the ground"), &tool.height_mode, 0));
|
||||
ImGui::SameLine();
|
||||
changed(ImGui::InputDouble(STR_C("by (m)"), &tool.height, 0.0, 0.0, "%.3f"));
|
||||
changed(ImGui::InputDouble(STR_C("A marker higher or lower by more (m) is wrong"), &tool.height_tolerance, 0.0, 0.0, "%.3f"));
|
||||
tool.height_tolerance = std::clamp(tool.height_tolerance, 0.005, 1.0);
|
||||
changed(ImGui::RadioButton(STR_C("Over the rail heads"), &tool.height_mode, 1));
|
||||
if (tool.height_mode == 1)
|
||||
{
|
||||
ImGui::SameLine();
|
||||
changed(ImGui::InputDouble(STR_C("by (m)"), &tool.height, 0.0, 0.0, "%.3f"));
|
||||
changed(ImGui::InputDouble(STR_C("A marker higher or lower by more (m) is wrong"), &tool.height_tolerance, 0.0, 0.0, "%.3f"));
|
||||
tool.height_tolerance = std::clamp(tool.height_tolerance, 0.005, 1.0);
|
||||
}
|
||||
char const *models[] = {"w17", "w17_new", "w17_old", "w17_dwuteo"};
|
||||
ImGui::Combo(STR_C("Marker"), &tool.model, models, IM_ARRAYSIZE(models));
|
||||
char const *turns[] = {"0", "90", "180", "270"};
|
||||
int turn{tool.turn / 90};
|
||||
if (ImGui::Combo(STR_C("Turned by (deg)"), &turn, turns, IM_ARRAYSIZE(turns)))
|
||||
{
|
||||
tool.turn = turn * 90;
|
||||
tool.dirty = true;
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
}
|
||||
char const *models[] = {"w17", "w17_new", "w17_old", "w17_dwuteo"};
|
||||
ImGui::Combo(STR_C("Marker"), &tool.model, models, IM_ARRAYSIZE(models));
|
||||
char const *turns[] = {"0", "90", "180", "270"};
|
||||
int turn{tool.turn / 90};
|
||||
if (ImGui::Combo(STR_C("Turned by (deg)"), &turn, turns, IM_ARRAYSIZE(turns)))
|
||||
{
|
||||
tool.turn = turn * 90;
|
||||
tool.dirty = true;
|
||||
}
|
||||
ImGui::PopItemWidth();
|
||||
tool.reach = std::clamp(tool.reach, 50.0, 50000.0);
|
||||
tool.spacing = std::clamp(tool.spacing, 2.0, 10.0);
|
||||
tool.tolerance = std::clamp(tool.tolerance, 0.2, 20.0);
|
||||
fouling_update();
|
||||
if (false == tool.error.empty())
|
||||
{
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.5f, 0.3f, 1.0f), "%s", tool.error.c_str());
|
||||
return;
|
||||
}
|
||||
ImGui::TextWrapped("%s", tool.status.c_str());
|
||||
auto const missing{std::count_if(tool.points.begin(), tool.points.end(), [](fouling_point const &Point) { return Point.standing < 0; })};
|
||||
auto const wrong{std::count_if(tool.found.begin(), tool.found.end(), [](fouling_found const &Found) { return Found.wrong; })};
|
||||
if (missing > 0 && ImGui::Button(format(STR_C("Place %d markers"), static_cast<int>(missing)).c_str()))
|
||||
fouling_place();
|
||||
if (wrong > 0)
|
||||
{
|
||||
if (missing > 0)
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button(format(STR_C("Move %d wrong markers to their places"), static_cast<int>(wrong)).c_str()))
|
||||
fouling_fix();
|
||||
}
|
||||
}
|
||||
|
||||
void editor_mode::draw_fouling_overlay() const
|
||||
|
||||
@@ -783,6 +783,11 @@ editor_mode::selftest_result editor_mode::selftest_sweep(std::string const &comm
|
||||
values[pair.substr(0, equals)] = pair.substr(equals + 1);
|
||||
}
|
||||
auto &tool{m_fouling};
|
||||
if (values["show"] == "1")
|
||||
{
|
||||
show_track_tab(track_tab::lineside);
|
||||
tool.expand = true;
|
||||
}
|
||||
tool.scope = std::stoi(values.count("scope") ? values["scope"] : "2");
|
||||
tool.height_mode = values["mode"] == "ground" ? 0 : 1;
|
||||
tool.height = std::stod(values.count("height") ? values["height"] : "0");
|
||||
@@ -793,6 +798,19 @@ editor_mode::selftest_result editor_mode::selftest_sweep(std::string const &comm
|
||||
for (auto const &found : tool.found)
|
||||
if (found.wrong && shown++ < 5)
|
||||
WriteLog(format("SELFTEST fouling: %s %.2f m off, %+.3f m high", found.marker.file.c_str(), found.error, found.height_error));
|
||||
if (values["place"] == "one")
|
||||
{
|
||||
// the first fouling point without a marker alone, as a click on it in the view
|
||||
for (int index = 0; index < static_cast<int>(tool.points.size()); ++index)
|
||||
if (tool.points[index].standing < 0)
|
||||
{
|
||||
fouling_place(index);
|
||||
break;
|
||||
}
|
||||
tool.dirty = true;
|
||||
fouling_update();
|
||||
WriteLog("SELFTEST fouling one: " + tool.status);
|
||||
}
|
||||
if (values["place"] == "1")
|
||||
{
|
||||
fouling_place();
|
||||
|
||||
@@ -437,6 +437,14 @@ editor_mode::track_intent editor_mode::track_intent_at(int const Mods)
|
||||
return make(kind::switch_at, "Press and drag: a switch here, along sets its direction, sideways its side");
|
||||
}
|
||||
}
|
||||
if (m_track_tab == track_tab::lineside && m_fouling.open)
|
||||
if (auto const hit{fouling_hit()}; hit.has_value())
|
||||
{
|
||||
intent.index = hit->first;
|
||||
intent.path = hit->second ? 1 : 0;
|
||||
intent.position = hit->second ? m_fouling.found[hit->first].marker.location : m_fouling.points[hit->first].position;
|
||||
return make(kind::fouling, hit->second ? "Click: move this W17 to its place" : "Click: put the W17 here");
|
||||
}
|
||||
if (m_hover.track != nullptr)
|
||||
{
|
||||
intent.track = m_hover.track;
|
||||
@@ -552,6 +560,12 @@ void editor_mode::track_press(track_intent const &Intent)
|
||||
handle_drag_start(Intent.position);
|
||||
return;
|
||||
case kind::switch_at: start_switch_placement(); return;
|
||||
case kind::fouling:
|
||||
if (Intent.path == 1)
|
||||
fouling_fix(Intent.index);
|
||||
else
|
||||
fouling_place(Intent.index);
|
||||
return;
|
||||
case kind::extend_end:
|
||||
if (false == start_extend(Intent.snap))
|
||||
point_drag_start(Intent.snap.point);
|
||||
@@ -700,13 +714,7 @@ void editor_mode::focus_track(TTrack &Track, int const Path, glm::dvec3 const &P
|
||||
if (glm::dot(Camera.Pos - Point, look) > 0.0)
|
||||
look = -look;
|
||||
look = glm::normalize(look - glm::dvec3(0.0, 0.25, 0.0));
|
||||
m_focus_start_pos = Camera.Pos;
|
||||
m_focus_start_angle = Camera.Angle;
|
||||
m_focus_target_pos = Point - look * kFocusDistance;
|
||||
m_focus_target_angle = glm::vec3(static_cast<float>(std::asin(std::clamp(look.y, -1.0, 1.0))), static_cast<float>(std::atan2(-look.x, -look.z)), 0.0f);
|
||||
m_focus_active = true;
|
||||
m_focus_time = 0.0;
|
||||
m_focus_duration = 0.6;
|
||||
fly_to(Point, look, kFocusDistance);
|
||||
}
|
||||
|
||||
void editor_mode::render_track_context()
|
||||
|
||||
@@ -7034,3 +7034,21 @@ msgstr "Wciśnij na nim i przeciągnij: wzdłuż kierunek, w bok strona"
|
||||
|
||||
msgid "Connection: drag from a straight onto the other track"
|
||||
msgstr "Połączenie: przeciągnij z prostej na drugi tor"
|
||||
|
||||
msgid "Click the green or red point in the view: that one only"
|
||||
msgstr "Kliknij zielony albo czerwony punkt w widoku: tylko ten"
|
||||
|
||||
msgid "%s: no W17"
|
||||
msgstr "%s: brak W17"
|
||||
|
||||
msgid "%s: W17 %.1f m off, %+.2f m high"
|
||||
msgstr "%s: W17 %.1f m od miejsca, %+.2f m w pionie"
|
||||
|
||||
msgid "Settings##fouling"
|
||||
msgstr "Ustawienia##fouling"
|
||||
|
||||
msgid "Click: move this W17 to its place"
|
||||
msgstr "Klik: przesuń ten W17 na miejsce"
|
||||
|
||||
msgid "Click: put the W17 here"
|
||||
msgstr "Klik: postaw tu W17"
|
||||
|
||||
Reference in New Issue
Block a user