mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-23 14:55:15 +02:00
feat(editor): Alt while laying turns the straight to 1:9, 1:6.6... of a straight track
Besides the steps of 15 degrees, Alt snaps the laid straight to the ratios of the switches (1:4.8 to 1:26.5) of the straight track the straight crosses, or of one within 30 m of where it starts; the label at the cursor says which ratio to which track. The first click with Alt within 2 m of a straight track puts the start on its axis, so a branch or a crossing at 1:9 starts exactly on the track.
This commit is contained in:
@@ -5079,7 +5079,7 @@ void editor_mode::render_first_steps()
|
||||
"curves",
|
||||
{{"Ctrl + wheel", "The radius of the curve, while laying on from an end"},
|
||||
{"Shift + wheel", "The shape: arc then straight, straight then arc, arc, straight"},
|
||||
{"Alt", "Held: the direction turns in steps of 15 degrees"},
|
||||
{"Alt", "Held: the direction turns in steps of 15 degrees, or to 1:9, 1:6.6... to a straight track it starts by or crosses"},
|
||||
{"0 1 2 3", "Type the length of the next piece, Enter lays it"},
|
||||
{"wheel", "Alone it zooms, as everywhere"}},
|
||||
{{first_step::wheel, "Change the radius with Ctrl+wheel"}, {first_step::shape, "Change the shape with Shift+wheel"}},
|
||||
@@ -5425,7 +5425,7 @@ void editor_mode::render_shortcuts_window()
|
||||
{"Ctrl+wheel", "Laying: radius"},
|
||||
{"Shift+wheel", "Laying: arc then straight, straight then arc, arc, straight; at a free end: another way to join"},
|
||||
{"0-9 while laying", "Type the length (or the radius) of the next piece, Enter lays it"},
|
||||
{"Alt held while laying", "The direction in steps of 15 degrees"},
|
||||
{"Alt held while laying", "The direction in steps of 15 degrees, or 1:9, 1:6.6... to a straight track; the first click with Alt goes onto the axis of the track"},
|
||||
{"N", "Parallel track: LMB on a track, then the side and the spacing"},
|
||||
{"Shift+LMB on a track in L", "The Parallel tool (N) with it"},
|
||||
{"Enter / Backspace", "Laying track: lay up to the cursor / take the last piece back"},
|
||||
|
||||
@@ -1343,6 +1343,7 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
editor_track::path_style style;
|
||||
std::string status;
|
||||
double status_time{-100.0};
|
||||
std::string skew; // what Alt turned the straight to
|
||||
struct step
|
||||
{
|
||||
editor_track::snap_target start;
|
||||
@@ -1354,7 +1355,9 @@ class editor_mode : public application_mode, private editor_track::observer
|
||||
bool m_norms_free{true};
|
||||
alignment::limits laying_norms() const;
|
||||
bool render_norms_choice();
|
||||
glm::dvec3 lay_angle_snap(glm::dvec3 const &Mouse) const;
|
||||
glm::dvec3 lay_angle_snap(glm::dvec3 const &Mouse, std::string &Label) const;
|
||||
// the straight track Alt turns the laid straight to: the nearest one the straight from From to To crosses, or one along From
|
||||
TTrack const *skew_reference(glm::dvec3 const &From, glm::dvec3 const &To, double const Reach) const;
|
||||
void lay_after_history(bool const Undo);
|
||||
void lay_back();
|
||||
void render_lay_ui();
|
||||
|
||||
@@ -80,8 +80,9 @@ void editor_mode::update_build_tools()
|
||||
double const height{false == lay.points.empty() ? lay.points.back().y : lay.start.track != nullptr ? lay.start.position.y : ground.y};
|
||||
if (pointing)
|
||||
lay.mouse = typed_lay(cursor_terrain(height));
|
||||
lay.skew.clear();
|
||||
if (pointing && ImGui::GetIO().KeyAlt && m_typed.empty() && false == lay.points.empty())
|
||||
lay.mouse = lay_angle_snap(lay.mouse);
|
||||
lay.mouse = lay_angle_snap(lay.mouse, lay.skew);
|
||||
lay.mouse_snap = m_typed.empty() ? snap_free_end(lay.mouse, cursor, nullptr, kRailCategory, {}) : editor_track::find_free_end(nullptr, kRailCategory, lay.mouse, kLaySnapRadius, {});
|
||||
if (lay.mouse_snap.track == nullptr)
|
||||
lay.mouse = lay_bed(lay.mouse);
|
||||
@@ -926,7 +927,16 @@ void editor_mode::lay_click()
|
||||
{
|
||||
tool.chain.clear();
|
||||
tool.start = snap;
|
||||
tool.points.push_back(snap.track != nullptr ? snap.position : tool.mouse);
|
||||
auto point{snap.track != nullptr ? snap.position : tool.mouse};
|
||||
if (auto const *axis{snap.track == nullptr && ImGui::GetIO().KeyAlt ? skew_reference(point, point, kSkewSeat) : nullptr})
|
||||
{
|
||||
auto const &path{axis->m_paths.front()};
|
||||
auto const &from{path.points[segment_data::point::start]};
|
||||
auto const &to{path.points[segment_data::point::end]};
|
||||
auto const along{std::clamp(glm::dot(plan_of(point - from), plan_of(to - from)) / glm::dot(plan_of(to - from), plan_of(to - from)), 0.0, 1.0)};
|
||||
point = from + (to - from) * along;
|
||||
}
|
||||
tool.points.push_back(point);
|
||||
return;
|
||||
}
|
||||
auto const &point{tool.mouse};
|
||||
@@ -1093,20 +1103,84 @@ void editor_mode::lay_back()
|
||||
tool.start = {};
|
||||
}
|
||||
|
||||
glm::dvec3 editor_mode::lay_angle_snap(glm::dvec3 const &Mouse) const
|
||||
glm::dvec3 editor_mode::lay_angle_snap(glm::dvec3 const &Mouse, std::string &Label) const
|
||||
{
|
||||
Label.clear();
|
||||
auto const &last{m_lay.points.back()};
|
||||
glm::dvec2 const offset{Mouse.x - last.x, Mouse.z - last.z};
|
||||
auto const length{glm::length(offset)};
|
||||
if (length < 1.0)
|
||||
return Mouse;
|
||||
auto const leaving{m_lay.start.track != nullptr ? -plan_of(m_lay.start.direction) : glm::dvec2{0.0}};
|
||||
glm::dvec2 const reference{glm::length(leaving) > 1e-6 ? glm::normalize(leaving) : glm::dvec2{0.0, 1.0}};
|
||||
glm::dvec2 reference{glm::length(leaving) > 1e-6 ? glm::normalize(leaving) : glm::dvec2{0.0, 1.0}};
|
||||
auto const step{glm::radians(15.0)};
|
||||
auto const heading{turned(reference, std::round(signed_angle(reference, offset / length) / step) * step) * length};
|
||||
auto angle{std::round(signed_angle(reference, offset / length) / step) * step};
|
||||
if (auto const *axis{skew_reference(last, Mouse, kSkewReach)})
|
||||
{
|
||||
auto const &path{axis->m_paths.front()};
|
||||
reference = glm::normalize(plan_of(path.points[segment_data::point::end] - path.points[segment_data::point::start]));
|
||||
auto const relative{signed_angle(reference, offset / length)};
|
||||
angle = std::round(relative / step) * step;
|
||||
double ratio{0.0};
|
||||
for (auto const n : kSkewRatios)
|
||||
for (auto const base : {0.0, glm::pi<double>()})
|
||||
for (auto const side : {-1.0, 1.0})
|
||||
{
|
||||
auto const candidate{base + side * std::atan(1.0 / n)};
|
||||
if (std::abs(std::remainder(relative - candidate, 2.0 * glm::pi<double>())) < std::abs(std::remainder(relative - angle, 2.0 * glm::pi<double>())))
|
||||
{
|
||||
angle = candidate;
|
||||
ratio = n;
|
||||
}
|
||||
}
|
||||
auto const name{axis->name().empty() ? std::string{STR_C("(no name)")} : axis->name()};
|
||||
auto const degrees{glm::degrees(std::abs(std::remainder(angle, glm::pi<double>())))};
|
||||
Label = ratio > 0.0 ? format(STR_C("1:%g to %s"), ratio, name.c_str()) : degrees < 0.1 ? format(STR_C("parallel to %s"), name.c_str()) : format(STR_C("%.0f deg to %s"), degrees, name.c_str());
|
||||
}
|
||||
auto const heading{turned(reference, angle) * length};
|
||||
return {last.x + heading.x, Mouse.y, last.z + heading.y};
|
||||
}
|
||||
|
||||
TTrack const *editor_mode::skew_reference(glm::dvec3 const &From, glm::dvec3 const &To, double const Reach) const
|
||||
{
|
||||
auto const from{plan_of(From)};
|
||||
auto const leg{plan_of(To) - from};
|
||||
auto const length{glm::length(leg)};
|
||||
TTrack const *nearest{nullptr};
|
||||
auto best{std::numeric_limits<double>::max()};
|
||||
for (auto *section : simulation::Region->sections((From + To) * 0.5, static_cast<float>(std::min(0.5 * length + Reach, 2000.0))))
|
||||
for (auto const &cell : section->m_cells)
|
||||
for (auto *track : cell.m_directories.paths)
|
||||
{
|
||||
if (track == nullptr || track->eType != tt_Normal || track->m_editorremoved || (track->iCategoryFlag & 15) != kRailCategory || track->m_paths.size() != 1 ||
|
||||
false == editor_track::is_straight(*track, m_straights.tolerance))
|
||||
continue;
|
||||
auto const &path{track->m_paths.front()};
|
||||
auto const a{plan_of(path.points[segment_data::point::start])};
|
||||
auto const axis{plan_of(path.points[segment_data::point::end]) - a};
|
||||
auto const span{glm::length(axis)};
|
||||
if (span < 1.0)
|
||||
continue;
|
||||
auto const along{std::clamp(glm::dot(from - a, axis) / span, 0.0, span)};
|
||||
auto distance{glm::distance(from, a + axis / span * along)};
|
||||
if (distance > Reach)
|
||||
distance = std::numeric_limits<double>::max();
|
||||
if (auto const determinant{cross(leg, axis)}; length > 1.0 && std::abs(determinant) > 1e-9)
|
||||
{
|
||||
auto const t{cross(a - from, axis) / determinant};
|
||||
auto const u{cross(a - from, leg) / determinant};
|
||||
if (t >= 0.0 && t <= 1.0 && u >= 0.0 && u <= 1.0)
|
||||
distance = std::min(distance, t * length);
|
||||
}
|
||||
if (distance < best)
|
||||
{
|
||||
best = distance;
|
||||
nearest = track;
|
||||
}
|
||||
}
|
||||
return nearest;
|
||||
}
|
||||
|
||||
editor_mode::track_hover editor_mode::split_spot() const
|
||||
{
|
||||
auto const *selected{selected_track()};
|
||||
|
||||
@@ -1521,14 +1521,14 @@ std::vector<editor_mode::key_hint> editor_mode::track_key_hints(bool const All)
|
||||
if (m_lay.active)
|
||||
{
|
||||
if (m_lay.points.empty())
|
||||
hints = {{"LMB", "start, on the ground or at a free end"}, {"Drag from a free end", "lays the shape to the cursor"}, {"Shift+click a straight", "a parallel track (N)"}, {"Alt", "the direction in steps of 15 degrees"}, {"Esc", "stop laying"}};
|
||||
hints = {{"LMB", "start, on the ground or at a free end"}, {"Drag from a free end", "lays the shape to the cursor"}, {"Shift+click a straight", "a parallel track (N)"}, {"Alt", "the direction in steps of 15 degrees, or 1:9, 1:6.6... to a straight track"}, {"Esc", "stop laying"}};
|
||||
else
|
||||
{
|
||||
bool const chained{m_lay.start.track != nullptr};
|
||||
hints = {{"LMB", chained ? "lay the shape up to here" : "lay the straight up to here"}, {"LMB on a free end", "join and lay"}};
|
||||
if (chained || m_lay.mouse_snap.track != nullptr)
|
||||
hints.insert(hints.end(), {{"Ctrl+wheel", "radius"}, {"Shift+wheel", "another shape"}});
|
||||
hints.insert(hints.end(), {{"Alt", "the direction in steps of 15 degrees"}, {"Enter", "lay up to the cursor"}, {"Backspace", m_lay.chain.empty() ? "take back the point" : "take back the last piece"}, {"Esc", "cancel"}});
|
||||
hints.insert(hints.end(), {{"Alt", "the direction in steps of 15 degrees, or 1:9, 1:6.6... to a straight track"}, {"Enter", "lay up to the cursor"}, {"Backspace", m_lay.chain.empty() ? "take back the point" : "take back the last piece"}, {"Esc", "cancel"}});
|
||||
}
|
||||
}
|
||||
else if (false == m_straights.detour.empty())
|
||||
@@ -1738,7 +1738,7 @@ std::string editor_mode::track_readout() const
|
||||
if (lay.points.size() == 1 && (lay.start.track != nullptr || lay.mouse_snap.track != nullptr))
|
||||
text += "\n" + track_shaping(lay.mouse_snap.track != nullptr && lay.start.track != nullptr);
|
||||
if (ImGui::GetIO().KeyAlt && false == lay.points.empty())
|
||||
text += "\n" + format(STR_C("azimuth %.0f deg"), azimuth(lay.points.back(), lay.mouse));
|
||||
text += "\n" + (lay.skew.empty() ? format(STR_C("azimuth %.0f deg"), azimuth(lay.points.back(), lay.mouse)) : lay.skew);
|
||||
if (false == lay.status.empty() && ImGui::GetTime() - lay.status_time < 4.0)
|
||||
text += "\n" + lay.status;
|
||||
return text;
|
||||
|
||||
@@ -104,6 +104,9 @@ double const kLaySnapRadius{5.0};
|
||||
float const kSnapPixels{16.0f};
|
||||
double const kSwitchReach{6.0}; // a press farther from the selected track selects another one instead of placing the switch
|
||||
double const kSlipReach{12.0}; // m; a slip goes to the crossing nearest the cursor, if it's this close
|
||||
double const kSkewSeat{2.0}; // m; the first point laid with Alt this close to a straight track goes onto its axis
|
||||
double const kSkewReach{30.0}; // m; Alt while laying turns the straight to a straight track this close to its start, or one it crosses
|
||||
double const kSkewRatios[]{4.8, 6.6, 7.5, 9.0, 12.0, 14.0, 18.5, 26.5};
|
||||
double const kLineReach{2000.0}; // how far each way the Curve tab follows the line
|
||||
double const kDigitizeFit{0.5};
|
||||
double const kDrawnReach{100000.0};
|
||||
|
||||
@@ -4590,14 +4590,14 @@ msgstr "K: podziel tutaj"
|
||||
msgid "Shift+click a straight"
|
||||
msgstr "Shift+LPM na prostej"
|
||||
|
||||
msgid "the direction in steps of 15 degrees"
|
||||
msgstr "kierunek co 15 stopni"
|
||||
msgid "the direction in steps of 15 degrees, or 1:9, 1:6.6... to a straight track"
|
||||
msgstr "kierunek co 15 stopni albo 1:9, 1:6,6… do prostego toru"
|
||||
|
||||
msgid "Alt held while laying"
|
||||
msgstr "Wciśnięty Alt w trakcie układania"
|
||||
|
||||
msgid "The direction in steps of 15 degrees"
|
||||
msgstr "Kierunek co 15 stopni"
|
||||
msgid "The direction in steps of 15 degrees, or 1:9, 1:6.6... to a straight track; the first click with Alt goes onto the axis of the track"
|
||||
msgstr "Kierunek co 15 stopni albo 1:9, 1:6,6… do prostego toru; pierwszy klik z Altem trafia w oś toru"
|
||||
|
||||
msgid "Shift+LMB on a track in L"
|
||||
msgstr "Shift+LPM na torze przy układaniu (L)"
|
||||
@@ -5376,8 +5376,8 @@ msgstr "Promień łuku, gdy układasz dalej od końca toru"
|
||||
msgid "The shape: arc then straight, straight then arc, arc, straight"
|
||||
msgstr "Kształt: łuk i prosta, prosta i łuk, sam łuk, prosta"
|
||||
|
||||
msgid "Held: the direction turns in steps of 15 degrees"
|
||||
msgstr "Trzymany: kierunek zmienia się co 15 stopni"
|
||||
msgid "Held: the direction turns in steps of 15 degrees, or to 1:9, 1:6.6... to a straight track it starts by or crosses"
|
||||
msgstr "Trzymany: kierunek zmienia się co 15 stopni albo na 1:9, 1:6,6… do prostego toru, przy którym się zaczyna albo który przecina"
|
||||
|
||||
msgid "Type the length of the next piece, Enter lays it"
|
||||
msgstr "Wpisz długość następnego kawałka, Enter go układa"
|
||||
@@ -9934,3 +9934,12 @@ msgstr "Klik: ten rozjazd krzyżowy zostanie zastąpiony wybranym"
|
||||
|
||||
msgid "Click: it goes here"
|
||||
msgstr "Klik: stanie tutaj"
|
||||
|
||||
msgid "1:%g to %s"
|
||||
msgstr "1:%g do %s"
|
||||
|
||||
msgid "parallel to %s"
|
||||
msgstr "równolegle do %s"
|
||||
|
||||
msgid "%.0f deg to %s"
|
||||
msgstr "%.0f° do %s"
|
||||
|
||||
Reference in New Issue
Block a user