mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 07:29:20 +02:00
feat(editor): switch templates, auto distances with snapping
This commit is contained in:
@@ -332,8 +332,8 @@ class editor_mode : public application_mode
|
||||
int listed{-1};
|
||||
TTrack *current_for{nullptr};
|
||||
editor_track::straight current;
|
||||
editor_track::straight a;
|
||||
editor_track::straight b;
|
||||
std::vector<editor_track::straight> neighbours;
|
||||
std::vector<double> spacings{3.5, 4.0, 4.5, 4.75, 5.0, 5.5, 6.0};
|
||||
char filter[32]{};
|
||||
int handle{-1};
|
||||
bool dragging{false};
|
||||
@@ -378,6 +378,9 @@ class editor_mode : public application_mode
|
||||
double straight_tool_radius(editor_track::straight const &Line) const;
|
||||
void straight_reshape(editor_track::straight const &Line, double const From, double const To, std::function<glm::dvec3(glm::dvec3 const &)> const &Tail, double const Radius);
|
||||
void straight_refresh();
|
||||
void find_neighbour_straights();
|
||||
glm::dvec3 snap_straight_offset(editor_track::straight const &Line, glm::dvec3 const &Offset) const;
|
||||
glm::dvec3 snap_straight_direction(glm::dvec3 const &Pivot, glm::dvec3 const &Moved) const;
|
||||
straights_state m_straights;
|
||||
enum class track_tab { straights, route, path };
|
||||
track_tab m_track_tab{track_tab::straights};
|
||||
|
||||
@@ -15,6 +15,8 @@ http:
|
||||
#include "utilities/Globals.h"
|
||||
#include "rendering/renderer.h"
|
||||
#include "world/Track.h"
|
||||
#include "scene/scene.h"
|
||||
#include "simulation/simulation.h"
|
||||
#include "utilities/Logs.h"
|
||||
|
||||
#include "imgui/imgui.h"
|
||||
@@ -1232,6 +1234,7 @@ editor_track::straight const &editor_mode::current_straight()
|
||||
m_straights.current_for = track;
|
||||
m_straights.handle = -1;
|
||||
m_straights.current = track != nullptr ? editor_track::find_straight(*track, m_straights.tolerance) : editor_track::straight{};
|
||||
find_neighbour_straights();
|
||||
}
|
||||
return m_straights.current;
|
||||
}
|
||||
@@ -1326,20 +1329,119 @@ void editor_mode::draw_straights_overlay() const
|
||||
drawlist->AddCircle(screen, 12.0f, IM_COL32(255, 255, 255, 255), 16, 2.5f);
|
||||
}
|
||||
}
|
||||
draw(m_straights.a, IM_COL32(60, 230, 90, 255), 5.0f);
|
||||
draw(m_straights.b, IM_COL32(255, 60, 255, 255), 5.0f);
|
||||
if (false == m_straights.a.tracks.empty() && false == m_straights.b.tracks.empty())
|
||||
auto const &line{m_straights.current};
|
||||
if (line.tracks.empty() || m_straights.dragging)
|
||||
return;
|
||||
glm::dvec2 const normal{-line.direction.y, line.direction.x};
|
||||
for (auto const &other : m_straights.neighbours)
|
||||
{
|
||||
auto const &a{m_straights.a};
|
||||
for (auto const &point : {m_straights.b.start, m_straights.b.end})
|
||||
auto const along = [&](glm::dvec3 const &Point) { return glm::dot(glm::dvec2{Point.x - line.start.x, Point.z - line.start.z}, line.direction); };
|
||||
auto const across = [&](glm::dvec3 const &Point) { return glm::dot(glm::dvec2{Point.x - line.start.x, Point.z - line.start.z}, normal); };
|
||||
auto const a0{along(other.start)}, a1{along(other.end)};
|
||||
auto const from{std::max(0.0, std::min(a0, a1))};
|
||||
auto const to{std::min(line.length, std::max(a0, a1))};
|
||||
if (to <= from)
|
||||
continue;
|
||||
auto const middle{(from + to) * 0.5};
|
||||
auto const fraction{std::abs(a1 - a0) > 1e-6 ? (middle - a0) / (a1 - a0) : 0.0};
|
||||
auto const lateral{across(other.start) + (across(other.end) - across(other.start)) * fraction};
|
||||
glm::dvec3 const foot{line.start.x + line.direction.x * middle, line.start.y + line.grade * middle, line.start.z + line.direction.y * middle};
|
||||
glm::dvec3 const target{foot.x + normal.x * lateral, foot.y, foot.z + normal.y * lateral};
|
||||
projection.line(drawlist, foot, target, IM_COL32(255, 230, 120, 230), 1.5f);
|
||||
ImVec2 screen;
|
||||
if (projection.project((foot + target) * 0.5, screen))
|
||||
{
|
||||
auto const along{glm::dot(glm::dvec2{point.x - a.start.x, point.z - a.start.z}, a.direction)};
|
||||
auto const foot{a.start + glm::dvec3{a.direction.x, 0.0, a.direction.y} * along + glm::dvec3{0.0, a.grade * along, 0.0}};
|
||||
projection.line(drawlist, foot, point, IM_COL32(255, 210, 60, 220), 1.5f);
|
||||
auto const angle{glm::degrees(std::abs(std::asin(std::clamp(line.direction.x * other.direction.y - line.direction.y * other.direction.x, -1.0, 1.0))))};
|
||||
char label[48];
|
||||
if (angle > 0.001)
|
||||
std::snprintf(label, sizeof(label), "%.3f m %.3f deg", std::abs(lateral), angle);
|
||||
else
|
||||
std::snprintf(label, sizeof(label), "%.3f m", std::abs(lateral));
|
||||
auto const size{ImGui::CalcTextSize(label)};
|
||||
drawlist->AddRectFilled(ImVec2(screen.x - 3.0f, screen.y - 2.0f), ImVec2(screen.x + size.x + 3.0f, screen.y + size.y + 2.0f), IM_COL32(0, 0, 0, 170), 3.0f);
|
||||
drawlist->AddText(screen, IM_COL32(255, 230, 120, 255), label);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void editor_mode::find_neighbour_straights()
|
||||
{
|
||||
auto &state{m_straights};
|
||||
state.neighbours.clear();
|
||||
auto const &line{state.current};
|
||||
if (line.tracks.empty())
|
||||
return;
|
||||
glm::dvec2 const normal{-line.direction.y, line.direction.x};
|
||||
auto const middle{(line.start + line.end) * 0.5};
|
||||
std::vector<TTrack const *> visited(line.tracks.begin(), line.tracks.end());
|
||||
auto const sections{simulation::Region->sections(middle, static_cast<float>(line.length * 0.5 + 60.0))};
|
||||
for (auto *section : sections)
|
||||
{
|
||||
for (auto const &cell : section->m_cells)
|
||||
{
|
||||
for (auto *track : cell.m_paths)
|
||||
{
|
||||
if (std::find(visited.begin(), visited.end(), track) != visited.end() || false == editor_track::is_straight(*track, state.tolerance))
|
||||
continue;
|
||||
auto other{editor_track::find_straight(*track, state.tolerance)};
|
||||
visited.insert(visited.end(), other.tracks.begin(), other.tracks.end());
|
||||
if (other.tracks.empty() || std::abs(line.direction.x * other.direction.y - line.direction.y * other.direction.x) > std::sin(glm::radians(10.0)))
|
||||
continue;
|
||||
auto const offset{glm::dvec2{(other.start + other.end).x * 0.5 - line.start.x, (other.start + other.end).z * 0.5 - line.start.z}};
|
||||
if (std::abs(glm::dot(offset, normal)) > 60.0)
|
||||
continue;
|
||||
auto const a0{glm::dot(glm::dvec2{other.start.x - line.start.x, other.start.z - line.start.z}, line.direction)};
|
||||
auto const a1{glm::dot(glm::dvec2{other.end.x - line.start.x, other.end.z - line.start.z}, line.direction)};
|
||||
if (std::max(a0, a1) <= 0.0 || std::min(a0, a1) >= line.length)
|
||||
continue;
|
||||
state.neighbours.push_back(std::move(other));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
glm::dvec3 editor_mode::snap_straight_offset(editor_track::straight const &Line, glm::dvec3 const &Offset) const
|
||||
{
|
||||
glm::dvec2 const normal{-Line.direction.y, Line.direction.x};
|
||||
auto const middle{(Line.start + Line.end) * 0.5 + Offset};
|
||||
double best{0.2};
|
||||
double correction{0.0};
|
||||
for (auto const &other : m_straights.neighbours)
|
||||
{
|
||||
if (std::abs(Line.direction.x * other.direction.y - Line.direction.y * other.direction.x) > std::sin(glm::radians(0.5)))
|
||||
continue;
|
||||
auto const distance{glm::dot(glm::dvec2{other.start.x - middle.x, other.start.z - middle.z}, normal)};
|
||||
for (auto const spacing : m_straights.spacings)
|
||||
{
|
||||
auto const error{std::abs(distance) - spacing};
|
||||
if (std::abs(error) < best)
|
||||
{
|
||||
best = std::abs(error);
|
||||
correction = distance > 0.0 ? error : -error;
|
||||
}
|
||||
}
|
||||
}
|
||||
return Offset + glm::dvec3{normal.x, 0.0, normal.y} * correction;
|
||||
}
|
||||
|
||||
glm::dvec3 editor_mode::snap_straight_direction(glm::dvec3 const &Pivot, glm::dvec3 const &Moved) const
|
||||
{
|
||||
glm::dvec2 const offset{Moved.x - Pivot.x, Moved.z - Pivot.z};
|
||||
auto const length{glm::length(offset)};
|
||||
if (length < 1e-3)
|
||||
return Moved;
|
||||
auto const direction{offset / length};
|
||||
for (auto const &other : m_straights.neighbours)
|
||||
{
|
||||
auto const sine{direction.x * other.direction.y - direction.y * other.direction.x};
|
||||
if (std::abs(sine) > std::sin(glm::radians(0.5)))
|
||||
continue;
|
||||
auto const aligned{other.direction * (glm::dot(direction, other.direction) < 0.0 ? -1.0 : 1.0)};
|
||||
return {Pivot.x + aligned.x * length, Moved.y, Pivot.z + aligned.y * length};
|
||||
}
|
||||
return Moved;
|
||||
}
|
||||
|
||||
void editor_mode::render_straights_ui()
|
||||
{
|
||||
auto &state{m_straights};
|
||||
@@ -1403,12 +1505,6 @@ void editor_mode::render_straights_ui()
|
||||
ImGui::Text("To (%.3f, %.3f, %.3f)", line.end.x, line.end.y, line.end.z);
|
||||
ImGui::Text("Paths: %s ... %s", line.tracks.front()->name().c_str(), line.tracks.back()->name().c_str());
|
||||
ImGui::Unindent();
|
||||
if (ImGui::Button("Set as A"))
|
||||
state.a = line;
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Set as B"))
|
||||
state.b = line;
|
||||
ImGui::SameLine();
|
||||
if (in_straight_set(line))
|
||||
{
|
||||
if (ImGui::Button("Remove from the set"))
|
||||
@@ -1496,48 +1592,11 @@ void editor_mode::render_straights_ui()
|
||||
state.set.clear();
|
||||
}
|
||||
|
||||
ImGui::Separator();
|
||||
auto const &a{state.a};
|
||||
auto const &b{state.b};
|
||||
ImGui::TextColored(ImVec4(0.25f, 0.9f, 0.35f, 1.0f), "A: %s", a.tracks.empty() ? "-" : describe(a).c_str());
|
||||
ImGui::TextColored(ImVec4(1.0f, 0.25f, 1.0f, 1.0f), "B: %s", b.tracks.empty() ? "-" : describe(b).c_str());
|
||||
if (a.tracks.empty() || b.tracks.empty())
|
||||
return;
|
||||
|
||||
auto const cross{a.direction.x * b.direction.y - a.direction.y * b.direction.x};
|
||||
auto const dot{glm::dot(a.direction, b.direction)};
|
||||
auto const between{glm::degrees(std::atan2(std::abs(cross), std::abs(dot)))};
|
||||
auto const offset = [&](glm::dvec3 const &Point) {
|
||||
glm::dvec2 const relative{Point.x - a.start.x, Point.z - a.start.z};
|
||||
return a.direction.x * relative.y - a.direction.y * relative.x;
|
||||
};
|
||||
auto const along = [&](glm::dvec3 const &Point) { return glm::dot(glm::dvec2{Point.x - a.start.x, Point.z - a.start.z}, a.direction); };
|
||||
auto const height = [&](glm::dvec3 const &Point) { return Point.y - (a.start.y + a.grade * along(Point)); };
|
||||
auto const startoffset{offset(b.start)};
|
||||
auto const endoffset{offset(b.end)};
|
||||
auto const from{std::max(0.0, std::min(along(b.start), along(b.end)))};
|
||||
auto const to{std::min(a.length, std::max(along(b.start), along(b.end)))};
|
||||
|
||||
ImGui::Text("Angle between A and B: %.5f deg%s", between, between < 0.001 ? " (parallel)" : "");
|
||||
ImGui::Text("Distance of B from A: start %.3f m, end %.3f m, change %.3f m", std::abs(startoffset), std::abs(endoffset), std::abs(endoffset - startoffset));
|
||||
ImGui::Text("B lies on the %s of A", (startoffset + endoffset) > 0.0 ? "left" : "right");
|
||||
ImGui::Text("Height of B above A: start %.3f m, end %.3f m", height(b.start), height(b.end));
|
||||
ImGui::Text("Grade difference: %.2f per mille", (b.grade * (dot < 0.0 ? -1.0 : 1.0) - a.grade) * 1000.0);
|
||||
ImGui::Text("Overlap along A: %.2f m", std::max(0.0, to - from));
|
||||
|
||||
ImGui::PushItemWidth(120.0f);
|
||||
ImGui::InputDouble("Distance (m)", &state.distance, 0.0, 0.0, "%.3f");
|
||||
ImGui::PopItemWidth();
|
||||
ImGui::SameLine();
|
||||
if (ImGui::Button("Make B parallel to A at this distance"))
|
||||
if (false == state.neighbours.empty())
|
||||
{
|
||||
auto const side{(startoffset + endoffset) >= 0.0 ? 1.0 : -1.0};
|
||||
glm::dvec2 const direction{a.direction * (dot < 0.0 ? -1.0 : 1.0)};
|
||||
glm::dvec2 const normal{-a.direction.y, a.direction.x};
|
||||
auto const startalong{along(b.start)};
|
||||
auto const start2d{glm::dvec2{a.start.x, a.start.z} + a.direction * startalong + normal * (side * std::abs(state.distance))};
|
||||
auto const end2d{start2d + direction * b.length};
|
||||
straight_apply(b, {start2d.x, b.start.y, start2d.y}, {end2d.x, b.end.y, end2d.y});
|
||||
ImGui::Separator();
|
||||
ImGui::Text("Neighbouring straights: %zu (distances shown in the view)", state.neighbours.size());
|
||||
ImGui::TextDisabled("Dragging the middle snaps the distance to: 3.5, 4.0, 4.5, 4.75, 5.0, 5.5, 6.0 m; dragging an end snaps parallel");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1580,9 +1639,7 @@ void editor_mode::straight_refresh()
|
||||
auto &state{m_straights};
|
||||
state.current_for = selected_track();
|
||||
state.current = state.current_for != nullptr ? editor_track::find_straight(*state.current_for, state.tolerance) : editor_track::straight{};
|
||||
for (auto *line : {&state.a, &state.b})
|
||||
if (false == line->tracks.empty())
|
||||
*line = editor_track::find_straight(*line->tracks.front(), state.tolerance);
|
||||
find_neighbour_straights();
|
||||
for (auto &line : state.found)
|
||||
if (false == line.tracks.empty())
|
||||
line = editor_track::find_straight(*line.tracks.front(), state.tolerance);
|
||||
@@ -1696,17 +1753,18 @@ void editor_mode::render_straight_gizmo()
|
||||
m_track_drag = tracks;
|
||||
}
|
||||
auto const &grabbed{state.drag_line};
|
||||
glm::dvec3 const moved{camerapos + glm::dvec3(state.gizmo[3])};
|
||||
glm::dvec3 moved{camerapos + glm::dvec3(state.gizmo[3])};
|
||||
std::function<glm::dvec3(glm::dvec3 const &)> transform;
|
||||
if (state.handle == 2)
|
||||
{
|
||||
auto const offset{moved - (grabbed.start + grabbed.end) * 0.5};
|
||||
auto const offset{snap_straight_offset(grabbed, moved - (grabbed.start + grabbed.end) * 0.5)};
|
||||
transform = [offset](glm::dvec3 const &Point) { return Point + offset; };
|
||||
}
|
||||
else
|
||||
{
|
||||
auto const pivot{state.handle == 0 ? grabbed.end : grabbed.start};
|
||||
auto const original{state.handle == 0 ? grabbed.start : grabbed.end};
|
||||
moved = snap_straight_direction(pivot, moved);
|
||||
glm::dvec2 const before{original.x - pivot.x, original.z - pivot.z};
|
||||
glm::dvec2 const after{moved.x - pivot.x, moved.z - pivot.z};
|
||||
auto const lengthbefore{glm::length(before)};
|
||||
|
||||
@@ -22,6 +22,7 @@ http:
|
||||
#include <cmath>
|
||||
#include <limits>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <numeric>
|
||||
#include <functional>
|
||||
#include <sstream>
|
||||
@@ -1043,6 +1044,176 @@ bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<editor_track::switch_template> editor_track::find_switch_templates()
|
||||
{
|
||||
std::vector<switch_template> result;
|
||||
std::map<std::string, std::size_t> lookup;
|
||||
for (auto *track : simulation::Paths.sequence())
|
||||
{
|
||||
if (track == nullptr || track->m_editorremoved || track->eType != tt_Switch || track->m_paths.size() < 2 || (track->iCategoryFlag & 0x80) != 0)
|
||||
continue;
|
||||
auto const &main{track->m_paths[0]};
|
||||
auto const origin{main.points[segment_data::point::start]};
|
||||
auto const chord{plan_of(main.points[segment_data::point::end] - origin)};
|
||||
if (glm::length(chord) < 1.0)
|
||||
continue;
|
||||
auto const axis{glm::normalize(chord)};
|
||||
glm::dvec2 const normal{-axis.y, axis.x};
|
||||
auto const tolocal = [&](glm::dvec3 const &Vector) {
|
||||
auto const planar{plan_of(Vector)};
|
||||
return glm::dvec3{glm::dot(planar, normal), Vector.y, glm::dot(planar, axis)};
|
||||
};
|
||||
switch_template entry;
|
||||
entry.source = track;
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
auto const &path{track->m_paths[i]};
|
||||
auto &local{entry.local[i]};
|
||||
local = path;
|
||||
local.points[segment_data::point::start] = tolocal(path.points[segment_data::point::start] - origin);
|
||||
local.points[segment_data::point::end] = tolocal(path.points[segment_data::point::end] - origin);
|
||||
local.points[segment_data::point::control1] = tolocal(path.points[segment_data::point::control1]);
|
||||
local.points[segment_data::point::control2] = tolocal(path.points[segment_data::point::control2]);
|
||||
}
|
||||
if (glm::length(glm::dvec2{entry.local[1].points[segment_data::point::start].x, entry.local[1].points[segment_data::point::start].z}) > kSamePoint)
|
||||
continue;
|
||||
if (entry.local[1].points[segment_data::point::end].x < 0.0)
|
||||
{
|
||||
for (auto &path : entry.local)
|
||||
{
|
||||
for (auto &point : path.points)
|
||||
point.x = -point.x;
|
||||
path.rolls = {-path.rolls[0], -path.rolls[1]};
|
||||
}
|
||||
}
|
||||
entry.length = entry.local[0].points[segment_data::point::end].z;
|
||||
auto const tangent{path_tangent(entry.local[1], true)};
|
||||
auto const angle{std::atan2(std::abs(tangent.x), tangent.z)};
|
||||
entry.ratio = angle > 1e-6 ? 1.0 / std::tan(angle) : 0.0;
|
||||
entry.radius = path_radius(entry.local[1], 0.25);
|
||||
char key[256];
|
||||
std::snprintf(key, sizeof(key), "%.2f|%.2f|%.2f|%s|%s", entry.length, entry.local[1].points[segment_data::point::end].x, entry.local[1].points[segment_data::point::end].z, material_name(track->m_material1).c_str(), material_name(track->m_material2).c_str());
|
||||
auto const found{lookup.find(key)};
|
||||
if (found != lookup.end())
|
||||
{
|
||||
++result[found->second].count;
|
||||
continue;
|
||||
}
|
||||
entry.count = 1;
|
||||
char label[256];
|
||||
std::snprintf(label, sizeof(label), "L %.2f m 1:%.1f R %.0f m %s", entry.length, entry.ratio, entry.radius, material_name(track->m_material1).c_str());
|
||||
entry.label = label;
|
||||
lookup.emplace(key, result.size());
|
||||
result.push_back(entry);
|
||||
}
|
||||
std::sort(result.begin(), result.end(), [](switch_template const &A, switch_template const &B) { return A.count > B.count; });
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<segment_data> editor_track::place_switch(switch_template const &Template, glm::dvec3 const &Origin, glm::dvec2 const &Direction, int const Side, double const Grade)
|
||||
{
|
||||
std::vector<segment_data> result;
|
||||
glm::dvec2 const normal{-Direction.y, Direction.x};
|
||||
auto const side{static_cast<double>(Side)};
|
||||
auto const vector = [&](glm::dvec3 const &Local) {
|
||||
auto const planar{Direction * Local.z + normal * (Local.x * side)};
|
||||
return glm::dvec3{planar.x, Local.y + Grade * Local.z, planar.y};
|
||||
};
|
||||
for (auto const &local : Template.local)
|
||||
{
|
||||
segment_data path{local};
|
||||
path.points[segment_data::point::start] = Origin + vector(local.points[segment_data::point::start]);
|
||||
path.points[segment_data::point::end] = Origin + vector(local.points[segment_data::point::end]);
|
||||
path.points[segment_data::point::control1] = vector(local.points[segment_data::point::control1]);
|
||||
path.points[segment_data::point::control2] = vector(local.points[segment_data::point::control2]);
|
||||
if (Side < 0)
|
||||
path.rolls = {-local.rolls[0], -local.rolls[1]};
|
||||
result.push_back(path);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
std::vector<editor_track::switch_template> editor_track::standard_switch_templates()
|
||||
{
|
||||
struct definition
|
||||
{
|
||||
char const *name;
|
||||
double radius;
|
||||
double ratio;
|
||||
double a;
|
||||
double b;
|
||||
};
|
||||
definition const definitions[] = {
|
||||
{"Rz 1:9 R190", 190.0, 9.0, 10.523, 16.615},
|
||||
{"Rz 1:9 R300", 300.0, 9.0, 16.615, 16.615},
|
||||
{"Rz 1:12 R500", 500.0, 12.0, 20.797, 0.0},
|
||||
{"Rz 1:14 R760", 760.0, 14.0, 0.0, 0.0},
|
||||
{"Rz 1:18,5 R1200", 1200.0, 18.5, 32.409, 34.943},
|
||||
{"Rz 1:26,5 R2500", 2500.0, 26.5, 0.0, 0.0},
|
||||
};
|
||||
std::vector<switch_template> result;
|
||||
for (auto const &definition : definitions)
|
||||
{
|
||||
auto const angle{std::atan(1.0 / definition.ratio)};
|
||||
auto const a{definition.a > 0.0 ? definition.a : definition.radius * std::tan(angle * 0.5)};
|
||||
auto const estimated{definition.b <= 0.0};
|
||||
auto const b{estimated ? 1.87 * definition.ratio : definition.b};
|
||||
auto const offset{b * std::tan(angle)};
|
||||
switch_template entry;
|
||||
entry.a = a;
|
||||
entry.b = b;
|
||||
entry.length = a + b;
|
||||
entry.ratio = definition.ratio;
|
||||
entry.radius = definition.radius;
|
||||
auto &main{entry.local[0]};
|
||||
main.points[segment_data::point::end] = {0.0, 0.0, a + b};
|
||||
auto &diverging{entry.local[1]};
|
||||
diverging.points[segment_data::point::end] = {offset, 0.0, a + b};
|
||||
diverging.points[segment_data::point::control1] = {0.0, 0.0, a * 2.0 / 3.0};
|
||||
diverging.points[segment_data::point::control2] = {-offset * 2.0 / 3.0, 0.0, -b * 2.0 / 3.0};
|
||||
diverging.radius = static_cast<float>(definition.radius);
|
||||
entry.label = std::string{definition.name} + " a " + std::to_string(a).substr(0, std::to_string(a).find('.') + 4) + " b " + std::to_string(b).substr(0, std::to_string(b).find('.') + 4) + (estimated ? " (b est.)" : "");
|
||||
result.push_back(entry);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
TTrack *editor_track::create_switch(switch_template const &Template, std::vector<segment_data> const &Paths, TTrack const &Style)
|
||||
{
|
||||
if (Paths.size() < 2)
|
||||
return nullptr;
|
||||
TTrack *track{nullptr};
|
||||
if (Template.source != nullptr)
|
||||
{
|
||||
track = clone(*Template.source);
|
||||
}
|
||||
else
|
||||
{
|
||||
char const *environments[] = {"flat", "mountains", "canyon", "tunnel", "bridge", "bank"};
|
||||
auto const environment{Style.eEnvironment >= e_flat && Style.eEnvironment <= e_bank ? environments[Style.eEnvironment] : "flat"};
|
||||
std::ostringstream text;
|
||||
text.precision(std::numeric_limits<double>::digits10);
|
||||
text << "switch " << Template.length << ' ' << Style.fTrackWidth << ' ' << Style.fFriction << ' ' << 10.0 << ' ' << Style.iQualityFlag << ' ' << 0 << ' ' << environment << ' ';
|
||||
if (Style.m_visible)
|
||||
text << "vis " << material_name(Style.m_material1) << ' ' << Style.fTexLength << ' ' << material_name(Style.m_material2) << ' ' << texture_height(Style) << ' ' << Style.fTexWidth << ' ' << Style.fTexSlope << ' ';
|
||||
else
|
||||
text << "unvis ";
|
||||
for (int i = 0; i < 2; ++i)
|
||||
{
|
||||
auto const &path{Paths[i]};
|
||||
auto const &start{path.points[segment_data::point::start]};
|
||||
auto const &control1{path.points[segment_data::point::control1]};
|
||||
auto const &control2{path.points[segment_data::point::control2]};
|
||||
auto const &end{path.points[segment_data::point::end]};
|
||||
text << start.x << ' ' << start.y << ' ' << start.z << ' ' << path.rolls[0] << ' ' << control1.x << ' ' << control1.y << ' ' << control1.z << ' ' << control2.x << ' ' << control2.y << ' ' << control2.z << ' ' << end.x << ' ' << end.y << ' ' << end.z << ' ' << path.rolls[1] << ' ' << path.radius << ' ';
|
||||
}
|
||||
text << "endtrack\n";
|
||||
track = load_path(text.str(), Style);
|
||||
}
|
||||
track->m_paths.assign(Paths.begin(), Paths.begin() + 2);
|
||||
return track;
|
||||
}
|
||||
|
||||
std::vector<TTrack *> editor_track::straight_affected(std::vector<straight> const &Lines)
|
||||
{
|
||||
std::vector<TTrack *> result;
|
||||
@@ -1252,18 +1423,23 @@ TTrack *editor_track::clone(TTrack const &Template)
|
||||
{
|
||||
std::ostringstream text;
|
||||
Template.export_as_text_(text);
|
||||
cParser parser(text.str(), cParser::buffer_TEXT);
|
||||
parser.getTokens();
|
||||
std::string token;
|
||||
parser >> token;
|
||||
auto content{text.str()};
|
||||
auto const type{content.find("track ")};
|
||||
if (type == 0)
|
||||
content.erase(0, 6);
|
||||
auto *track{load_path(content, Template)};
|
||||
track->m_friction = Template.m_friction;
|
||||
return track;
|
||||
}
|
||||
|
||||
TTrack *editor_track::load_path(std::string const &Text, TTrack const &Template)
|
||||
{
|
||||
cParser parser(Text, cParser::buffer_TEXT);
|
||||
scene::node_data data;
|
||||
data.type = "track";
|
||||
if (false == Template.name().empty() && Template.name() != "none")
|
||||
{
|
||||
for (int i = 1; data.name.empty() || simulation::Paths.find(data.name) != nullptr; ++i)
|
||||
data.name = Template.name() + "_" + std::to_string(i);
|
||||
}
|
||||
auto const base{Template.name().empty() || Template.name() == "none" ? std::string{"editor_track"} : Template.name()};
|
||||
for (int i = 1; data.name.empty() || simulation::Paths.find(data.name) != nullptr; ++i)
|
||||
data.name = base + "_" + std::to_string(i);
|
||||
auto *track = new TTrack(data);
|
||||
track->m_rangesquaredmin = Template.m_rangesquaredmin;
|
||||
track->m_rangesquaredmax = Template.m_rangesquaredmax;
|
||||
@@ -1271,7 +1447,6 @@ TTrack *editor_track::clone(TTrack const &Template)
|
||||
for (auto *events : {&track->m_events0, &track->m_events1, &track->m_events2, &track->m_events0all, &track->m_events1all, &track->m_events2all})
|
||||
events->clear();
|
||||
track->m_events = false;
|
||||
track->m_friction = Template.m_friction;
|
||||
simulation::Paths.insert(track);
|
||||
return track;
|
||||
}
|
||||
|
||||
@@ -9,6 +9,7 @@ http:
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <array>
|
||||
#include <functional>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
@@ -129,6 +130,22 @@ class editor_track
|
||||
double cant{0.0};
|
||||
};
|
||||
static bool find_curve(TTrack &Track, straight_tolerance const &Tolerance, double const Gauge, curve &Curve);
|
||||
struct switch_template
|
||||
{
|
||||
TTrack *source{nullptr};
|
||||
std::array<segment_data, 2> local;
|
||||
double length{0.0};
|
||||
double ratio{0.0};
|
||||
double radius{0.0};
|
||||
int count{0};
|
||||
std::string label;
|
||||
double a{0.0};
|
||||
double b{0.0};
|
||||
};
|
||||
static std::vector<switch_template> standard_switch_templates();
|
||||
static std::vector<switch_template> find_switch_templates();
|
||||
static std::vector<segment_data> place_switch(switch_template const &Template, glm::dvec3 const &Origin, glm::dvec2 const &Direction, int const Side, double const Grade);
|
||||
static TTrack *create_switch(switch_template const &Template, std::vector<segment_data> const &Paths, TTrack const &Style);
|
||||
static void move_straights(std::vector<straight> const &Lines, std::vector<std::pair<glm::dvec3, glm::dvec3>> const &Ends);
|
||||
static std::vector<TTrack *> relay(chain const &Chain, std::vector<segment_data> const &Pieces);
|
||||
static void retire(TTrack &Track);
|
||||
@@ -152,5 +169,6 @@ class editor_track
|
||||
static void store_switch_path(TTrack &Switch, int const Path);
|
||||
static void rebuild_geometry(TTrack &Track);
|
||||
static TTrack *clone(TTrack const &Template);
|
||||
static TTrack *load_path(std::string const &Text, TTrack const &Template);
|
||||
static void move_straight(straight const &Line, glm::dvec3 const &Start, glm::dvec3 const &End, std::function<bool(TTrack const *)> const &Member);
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user