mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-20 17:24:53 +02:00
feat(track): 3D sleepers and switch timbers, blade heel and throw from templates, one rail carving pass
- sleepers3d / sleepers3dplain / sleepers3dbed in eu07.ini: timbers put together from the begin, main and end parts of a model and stretched to their length, plain sleepers of their own model; tracks whose plain sleepers would touch are laid as one cluster, lines of timbers led by priority and turned to the bisectors of frogs and crossings at a limited rate, whole timbers under crossings, split ones between tracks with room to part them, gaps filled per rail and overlaps shortened or dropped; clusters worked out in a background thread while driving, on demand in the editor - the ballast can be left out under the beams (sleepers3dbed no) - blades get their heel and throw from the switch template or blades <heel> <throw> in the scenery; blades of double switches run on into the track linking them to the next switch - rail cutting at frogs, wing rails, movable noses and switch crossings done by one carving pass (RailCarve) - tint of the rail treads applied to the colour only, without the matte band at the frog - a switch trailed through with a movable nose set the other way derails the vehicle - self-tests: sleepercheck, sharedzones, beamsnear, throw, sleepersbg
This commit is contained in:
@@ -31,6 +31,9 @@ namespace
|
||||
|
||||
using geometry::bezier;
|
||||
using geometry::has_controls;
|
||||
using geometry::cross;
|
||||
using geometry::grade_of;
|
||||
using geometry::plan_distance;
|
||||
using geometry::plan_of;
|
||||
|
||||
struct switch_end_move
|
||||
@@ -90,6 +93,80 @@ std::string switch_rejoin(TTrack &Track, std::vector<editor_track::joint> const
|
||||
return loose;
|
||||
}
|
||||
|
||||
double constexpr kBranchStraight{500.0}; // how far the straight past the curve next to a switch is followed
|
||||
|
||||
// direction out of the path at the end
|
||||
glm::dvec3 end_outward(segment_data const &Path, bool const Atend)
|
||||
{
|
||||
bezier const curve{Path};
|
||||
auto direction{Atend ? curve.first(1.0) : -curve.first(0.0)};
|
||||
if (glm::length(plan_of(direction)) < 1e-9)
|
||||
direction = Atend ? curve.p3 - curve.p0 : curve.p0 - curve.p3;
|
||||
return direction;
|
||||
}
|
||||
|
||||
// straight from the start of the line to its first curve
|
||||
double opening(alignment::result const &Result)
|
||||
{
|
||||
return Result.elements.empty() || Result.elements.front().kind != alignment::element_kind::straight ? 0.0 : Result.elements.front().length;
|
||||
}
|
||||
|
||||
// straight from the last curve of the line to its end
|
||||
double closing(alignment::result const &Result)
|
||||
{
|
||||
return Result.elements.empty() || Result.elements.back().kind != alignment::element_kind::straight ? 0.0 : Result.elements.back().length;
|
||||
}
|
||||
|
||||
alignment::vertex scaled(alignment::vertex Vertex, double const Factor)
|
||||
{
|
||||
Vertex.radius *= Factor;
|
||||
Vertex.transition_in *= Factor;
|
||||
Vertex.transition_out *= Factor;
|
||||
for (auto &arc : Vertex.arcs)
|
||||
{
|
||||
arc.radius *= Factor;
|
||||
arc.transition *= Factor;
|
||||
}
|
||||
return Vertex;
|
||||
}
|
||||
|
||||
// where the path of the index leaves the chain
|
||||
glm::dvec3 exit_of(editor_track::chain const &Chain, std::size_t const Index)
|
||||
{
|
||||
auto const &path{Chain.tracks[Index]->m_paths.front()};
|
||||
return path.points[Chain.forward[Index] ? segment_data::point::end : segment_data::point::start];
|
||||
}
|
||||
|
||||
// raises the pieces by Rise at their start, less and less up to nothing at their end
|
||||
void lift(std::vector<segment_data> &Pieces, double const Rise)
|
||||
{
|
||||
std::vector<double> lengths;
|
||||
double total{0.0};
|
||||
for (auto const &piece : Pieces)
|
||||
{
|
||||
lengths.push_back(bezier{piece}.plan_length());
|
||||
total += lengths.back();
|
||||
}
|
||||
if (total <= 1e-6 || std::abs(Rise) <= 1e-6)
|
||||
return;
|
||||
auto const slope{Rise / total};
|
||||
double along{0.0};
|
||||
for (std::size_t i = 0; i < Pieces.size(); ++i)
|
||||
{
|
||||
auto &points{Pieces[i].points};
|
||||
auto const controls{has_controls(Pieces[i])};
|
||||
points[segment_data::point::start].y += Rise - slope * along;
|
||||
points[segment_data::point::end].y += Rise - slope * (along + lengths[i]);
|
||||
// the controls are kept from their ends
|
||||
if (controls)
|
||||
{
|
||||
points[segment_data::point::control1].y -= slope * glm::length(plan_of(points[segment_data::point::control1]));
|
||||
points[segment_data::point::control2].y += slope * glm::length(plan_of(points[segment_data::point::control2]));
|
||||
}
|
||||
along += lengths[i];
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
bool editor_mode::switch_room(TTrack &Track, double &Low, double &High, std::string &Reason)
|
||||
@@ -178,6 +255,258 @@ bool editor_mode::switch_vacate(TTrack &Track, glm::dvec3 const &From, glm::dvec
|
||||
return true;
|
||||
}
|
||||
|
||||
std::optional<editor_mode::switch_branch> editor_mode::take_switch_branch(TTrack const &Track, editor_track::joint const &Joint, std::size_t const Index) const
|
||||
{
|
||||
auto *first{Joint.other};
|
||||
if (first == nullptr || first->eType != tt_Normal || first->m_paths.empty())
|
||||
return std::nullopt;
|
||||
auto const category{first->iCategoryFlag & 15};
|
||||
auto const regular = [&](TTrack const *Other) { return Other != nullptr && Other->eType == tt_Normal && false == Other->m_editorremoved && editor_track::is_supported(*Other) && (Other->iCategoryFlag & 15) == category; };
|
||||
// from the switch on: a straight up to the curve maybe, the curve, the straight past it
|
||||
std::vector<TTrack *> run;
|
||||
auto *current{first};
|
||||
auto atstart{Joint.theirs.kind == editor_track::point_kind::start};
|
||||
int phase{0};
|
||||
std::size_t straight{0};
|
||||
double past{0.0};
|
||||
while (regular(current) && std::find(run.begin(), run.end(), current) == run.end() && past < kBranchStraight)
|
||||
{
|
||||
auto const plain{editor_track::is_straight(*current, m_straights.tolerance)};
|
||||
if (false == plain && phase == 2)
|
||||
break;
|
||||
if (false == plain)
|
||||
phase = 1;
|
||||
else if (phase == 1)
|
||||
{
|
||||
phase = 2;
|
||||
straight = run.size();
|
||||
}
|
||||
if (phase == 2)
|
||||
past += current->Length();
|
||||
run.push_back(current);
|
||||
auto *next{atstart ? current->trNext : current->trPrev};
|
||||
if (next == nullptr || (next->trPrev != current && next->trNext != current))
|
||||
break;
|
||||
atstart = next->trPrev == current;
|
||||
current = next;
|
||||
}
|
||||
if (phase != 2)
|
||||
return std::nullopt;
|
||||
switch_branch branch;
|
||||
branch.joint = Index;
|
||||
branch.straight = straight;
|
||||
std::string error;
|
||||
auto &chain{branch.chain};
|
||||
if (false == editor_track::find_chain(run.front(), run.back(), chain, error) || chain.tracks != run || plan_distance(chain.start, editor_track::point_position(Track, Joint.own)) > geometry::same_point)
|
||||
return std::nullopt;
|
||||
auto &design{branch.design};
|
||||
design.norms = m_route.design.norms;
|
||||
design.shape = m_route.design.shape;
|
||||
design.transition_pieces = m_route.design.transition_pieces;
|
||||
design.speed = chain.velocity > 0.0 ? chain.velocity : 100.0;
|
||||
if (auto const hit{alignment_store::on_track(*run[straight - 1])})
|
||||
{
|
||||
auto const &stored{alignment_store::records()[hit->record].design};
|
||||
design.norms = stored.norms;
|
||||
design.shape = stored.shape;
|
||||
design.transition_pieces = stored.transition_pieces;
|
||||
design.arc_piece_angle = stored.arc_piece_angle;
|
||||
design.speed = stored.speed;
|
||||
}
|
||||
design.norms.checked = false;
|
||||
auto const found{editor_track::recognize_line(chain, m_straights.tolerance, design.norms.gauge, design.shape)};
|
||||
if (found.size() != 1 || found.front().kink)
|
||||
return std::nullopt;
|
||||
design.start = chain.start;
|
||||
design.end = chain.end;
|
||||
design.start_direction = glm::normalize(plan_of(chain.start_direction));
|
||||
design.end_direction = glm::normalize(plan_of(chain.end_direction));
|
||||
design.start_grade = grade_of(chain.start_direction);
|
||||
design.end_grade = grade_of(chain.end_direction);
|
||||
auto vertex{route_vertex_of(found.front().shape)};
|
||||
vertex.position = found.front().position;
|
||||
design.vertices = {vertex};
|
||||
branch.result = alignment::compute(design);
|
||||
if (false == branch.result.valid)
|
||||
return std::nullopt;
|
||||
return branch;
|
||||
}
|
||||
|
||||
bool editor_mode::plan_switch_branch(segment_data const &Path, editor_track::joint const &Joint, switch_branch &Branch) const
|
||||
{
|
||||
auto const atend{Joint.own.kind == editor_track::point_kind::end};
|
||||
auto const start{Path.points[atend ? segment_data::point::end : segment_data::point::start]};
|
||||
auto const outward{end_outward(Path, atend)};
|
||||
auto laid{Branch.design};
|
||||
laid.start = start;
|
||||
laid.start_direction = glm::normalize(plan_of(outward));
|
||||
laid.start_grade = grade_of(outward);
|
||||
auto const original{Branch.design.vertices.front()};
|
||||
auto const make = [&](double const Factor) {
|
||||
laid.vertices = {scaled(original, Factor)};
|
||||
return alignment::compute(laid);
|
||||
};
|
||||
// compute() makes the curve tighter when it doesn't fit between the ends
|
||||
auto const fits = [&](alignment::result const &Result) { return Result.valid && false == Result.curves.empty() && Result.curves.front().radius >= laid.vertices.front().radius * (1.0 - 1e-6); };
|
||||
double factor{1.0};
|
||||
auto result{make(factor)};
|
||||
if (false == result.valid || result.curves.empty())
|
||||
return false;
|
||||
if (false == fits(result))
|
||||
{
|
||||
// the design keeps the curve the way it was made to fit
|
||||
auto &vertex{laid.vertices.front()};
|
||||
auto const &report{result.curves.front()};
|
||||
auto const reduced{report.radius / vertex.radius};
|
||||
if (vertex.compound)
|
||||
vertex = scaled(vertex, reduced);
|
||||
else
|
||||
{
|
||||
vertex.radius = report.radius;
|
||||
vertex.transition_in = report.transition_in;
|
||||
vertex.transition_out = report.transition_out;
|
||||
}
|
||||
factor = reduced;
|
||||
result = alignment::compute(laid);
|
||||
if (false == result.valid)
|
||||
return false;
|
||||
}
|
||||
else if (opening(Branch.result) < 0.1 && opening(result) >= 0.1)
|
||||
{
|
||||
// the curve which adjoined the switch keeps to it: the largest one which fits
|
||||
double low{1.0};
|
||||
double high{1.5};
|
||||
while (fits(make(high)) && high < 5.0)
|
||||
high *= 1.5;
|
||||
if (fits(make(high)))
|
||||
return false;
|
||||
for (int i = 0; i < 40; ++i)
|
||||
{
|
||||
auto const middle{0.5 * (low + high)};
|
||||
(fits(make(middle)) ? low : high) = middle;
|
||||
}
|
||||
factor = low;
|
||||
result = make(factor);
|
||||
if (false == result.valid)
|
||||
return false;
|
||||
}
|
||||
// the paths are laid up to the first joint on the straight past the old and the new curve
|
||||
auto const &chain{Branch.chain};
|
||||
auto const end{plan_of(chain.end)};
|
||||
auto const behind = [&](glm::dvec3 const &Point) { return glm::dot(end - plan_of(Point), laid.end_direction); };
|
||||
auto const curveend{closing(result)};
|
||||
auto last{chain.tracks.size()};
|
||||
for (auto k{Branch.straight - 1}; k < chain.tracks.size(); ++k)
|
||||
if (behind(exit_of(chain, k)) <= curveend - 0.5)
|
||||
{
|
||||
last = k;
|
||||
break;
|
||||
}
|
||||
if (last == chain.tracks.size())
|
||||
return false;
|
||||
auto part{laid};
|
||||
auto const joint{exit_of(chain, last)};
|
||||
part.end = joint;
|
||||
part.end_grade = grade_of(end_outward(chain.tracks[last]->m_paths.front(), chain.forward[last]));
|
||||
auto const partresult{alignment::compute(part)};
|
||||
if (false == partresult.valid)
|
||||
return false;
|
||||
Branch.part = {};
|
||||
Branch.part.tracks.assign(chain.tracks.begin(), chain.tracks.begin() + last + 1);
|
||||
Branch.part.forward.assign(chain.forward.begin(), chain.forward.begin() + last + 1);
|
||||
Branch.part.start = chain.start;
|
||||
Branch.part.end = joint;
|
||||
Branch.pieces = alignment::pieces(partresult, part, last + 1);
|
||||
if (Branch.pieces.size() < last + 1)
|
||||
return false;
|
||||
editor_track::keep_heights(Branch.part, Branch.pieces);
|
||||
lift(Branch.pieces, start.y - chain.start.y);
|
||||
Branch.pieces.front().points[segment_data::point::start] = start;
|
||||
Branch.pieces.back().points[segment_data::point::end] = joint;
|
||||
Branch.laid = laid;
|
||||
Branch.factor = factor;
|
||||
return true;
|
||||
}
|
||||
|
||||
void editor_mode::lay_switch_branch(switch_branch const &Branch, editor_track::edit &Edit)
|
||||
{
|
||||
for (auto *track : Branch.part.tracks)
|
||||
Edit.capture(*track);
|
||||
auto const created{editor_track::relay(Branch.part, Branch.pieces)};
|
||||
Edit.created.insert(Edit.created.end(), created.begin(), created.end());
|
||||
// the design kept for the line starting or ending at the switch follows it
|
||||
auto const &records{alignment_store::records()};
|
||||
auto const from{Branch.chain.start};
|
||||
auto const joint{Branch.part.end};
|
||||
for (std::size_t i = 0; i < records.size(); ++i)
|
||||
{
|
||||
auto const &record{records[i]};
|
||||
auto const atstart{plan_distance(record.design.start, from) < geometry::same_point};
|
||||
if (false == atstart && plan_distance(record.design.end, from) >= geometry::same_point)
|
||||
continue;
|
||||
auto design{record.design};
|
||||
auto &vertices{design.vertices};
|
||||
if (vertices.empty())
|
||||
return;
|
||||
auto const outward{Branch.laid.start_direction};
|
||||
auto const k{atstart ? std::size_t{0} : vertices.size() - 1};
|
||||
if (atstart)
|
||||
{
|
||||
design.start = Branch.laid.start;
|
||||
design.start_direction = outward;
|
||||
design.start_grade = Branch.laid.start_grade;
|
||||
}
|
||||
else
|
||||
{
|
||||
design.end = Branch.laid.start;
|
||||
design.end_direction = -outward;
|
||||
design.end_grade = -Branch.laid.start_grade;
|
||||
}
|
||||
auto const &corners{record.result.vertices};
|
||||
if (vertices.size() >= 2 && corners.size() == vertices.size())
|
||||
{
|
||||
// the corner moves along its other leg to the line from the switch
|
||||
auto const corner{corners[k]};
|
||||
auto const leg{corners[atstart ? 1 : k - 1] - corner};
|
||||
auto const determinant{cross(outward, leg)};
|
||||
if (std::abs(determinant) < 1e-9)
|
||||
return;
|
||||
auto const offset{cross(corner - plan_of(Branch.laid.start), leg) / determinant};
|
||||
if (offset <= 0.0)
|
||||
return;
|
||||
vertices[k].offset = offset;
|
||||
}
|
||||
// the curve next to the switch takes the shape it was laid with
|
||||
auto const &shape{Branch.laid.vertices.front()};
|
||||
if (shape.compound || vertices[k].compound)
|
||||
vertices[k] = scaled(vertices[k], Branch.factor);
|
||||
else
|
||||
{
|
||||
vertices[k].radius = shape.radius;
|
||||
vertices[k].transition_in = atstart ? shape.transition_in : shape.transition_out;
|
||||
vertices[k].transition_out = atstart ? shape.transition_out : shape.transition_in;
|
||||
}
|
||||
auto const result{alignment::compute(design)};
|
||||
if (false == result.valid || false == alignment_store::follows(result, Branch.part.tracks))
|
||||
return;
|
||||
// the km of the joint the paths are laid up to stays as it was
|
||||
auto origin{record.origin};
|
||||
if (atstart)
|
||||
{
|
||||
auto const before{alignment_store::chainage_on(record.result, joint)};
|
||||
auto const after{alignment_store::chainage_on(result, joint)};
|
||||
if (before.has_value() && after.has_value())
|
||||
origin += *before - *after;
|
||||
}
|
||||
auto const layer{record.layer};
|
||||
auto const source{record.source};
|
||||
auto const fit{record.fit};
|
||||
alignment_store::forget(static_cast<int>(i));
|
||||
alignment_store::store(design, result, origin, layer, source, fit);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
bool editor_mode::move_switch_paths(TTrack &Track, std::vector<segment_data> const &Paths, std::string &Status)
|
||||
{
|
||||
if (Paths.size() < 2 || Track.m_paths.size() < 2 || false == editor_track::main_axis(Track).has_value())
|
||||
@@ -199,6 +528,7 @@ bool editor_mode::move_switch_paths(TTrack &Track, std::vector<segment_data> con
|
||||
for (auto const &joint : joints)
|
||||
positions.push_back(editor_track::point_position(Track, joint.own));
|
||||
std::vector<bool> handled(joints.size(), false);
|
||||
auto branches{switch_branches(Track, joints)};
|
||||
editor_track::edit edit;
|
||||
edit.capture(Track);
|
||||
auto const line{online ? editor_track::find_straight(Track, m_straights.tolerance) : editor_track::straight{}};
|
||||
@@ -211,14 +541,56 @@ bool editor_mode::move_switch_paths(TTrack &Track, std::vector<segment_data> con
|
||||
return false;
|
||||
}
|
||||
Track.m_paths.assign(Paths.begin(), Paths.begin() + 2);
|
||||
auto const notes{plan_switch_branches(Track, joints, branches, handled)};
|
||||
auto const loose{switch_rejoin(Track, joints, handled, edit)};
|
||||
editor_track::commit(edit.changed);
|
||||
for (auto const &branch : branches)
|
||||
lay_switch_branch(branch, edit);
|
||||
push_track_snapshot(std::move(edit.states), std::move(edit.created), std::move(edit.removed));
|
||||
if (false == loose.empty())
|
||||
Status = STR_C("Too far from the new ends of the switch, left unjoined: ") + loose;
|
||||
if (false == notes.empty())
|
||||
Status += (Status.empty() ? "" : "\n") + notes;
|
||||
return true;
|
||||
}
|
||||
|
||||
std::vector<editor_mode::switch_branch> editor_mode::switch_branches(TTrack const &Track, std::vector<editor_track::joint> const &Joints, std::vector<TTrack const *> const &Skip) const
|
||||
{
|
||||
std::vector<switch_branch> branches;
|
||||
auto const axis{editor_track::main_axis(Track)};
|
||||
if (false == axis.has_value())
|
||||
return branches;
|
||||
for (std::size_t i = 0; i < Joints.size(); ++i)
|
||||
{
|
||||
if (std::find(Skip.begin(), Skip.end(), Joints[i].other) != Skip.end() || axis->lateral(editor_track::point_position(Track, Joints[i].own)) <= m_straights.tolerance.offset)
|
||||
continue;
|
||||
if (auto branch{take_switch_branch(Track, Joints[i], i)})
|
||||
branches.push_back(std::move(*branch));
|
||||
}
|
||||
return branches;
|
||||
}
|
||||
|
||||
std::string editor_mode::plan_switch_branches(TTrack const &Track, std::vector<editor_track::joint> const &Joints, std::vector<switch_branch> &Branches, std::vector<bool> &Handled) const
|
||||
{
|
||||
std::string notes;
|
||||
for (auto branch{Branches.begin()}; branch != Branches.end();)
|
||||
{
|
||||
auto const &joint{Joints[branch->joint]};
|
||||
std::string reason;
|
||||
if (false == plan_switch_branch(Track.m_paths[joint.own.path], joint, *branch) || false == tracks_editable(branch->part.tracks, reason))
|
||||
{
|
||||
notes += (notes.empty() ? "" : "\n") + format(STR_C("The curve past the end of the switch can't keep to it, %s follows the switch on its own"), track_label(*joint.other).c_str());
|
||||
branch = Branches.erase(branch);
|
||||
continue;
|
||||
}
|
||||
Handled[branch->joint] = true;
|
||||
if (std::abs(branch->factor - 1.0) > 1e-4)
|
||||
notes += (notes.empty() ? "" : "\n") + format(STR_C("The curve past the end of the switch keeps to it with the radius of %.1f m instead of %.1f m"), branch->laid.vertices.front().radius, branch->design.vertices.front().radius);
|
||||
++branch;
|
||||
}
|
||||
return notes;
|
||||
}
|
||||
|
||||
bool editor_mode::double_room(double_switch const &Set, double &Low, double &High, double &Length, std::string &Reason)
|
||||
{
|
||||
double low{0.0};
|
||||
@@ -288,6 +660,9 @@ bool editor_mode::move_double_paths(double_switch const &Set, std::vector<segmen
|
||||
auto const joint{Set.first->m_paths.front().points[segment_data::point::end]};
|
||||
auto const &link{Set.link->m_paths.front()};
|
||||
bool const reversed{glm::distance(link.points[segment_data::point::start], joint) > glm::distance(link.points[segment_data::point::end], joint)};
|
||||
std::array<std::vector<switch_branch>, 2> branches;
|
||||
for (std::size_t k = 0; k < 2; ++k)
|
||||
branches[k] = switch_branches(*switches[k], joints[k], {Set.first, Set.link, Set.second});
|
||||
editor_track::edit edit;
|
||||
for (auto *track : {Set.first, Set.link, Set.second})
|
||||
edit.capture(*track);
|
||||
@@ -304,6 +679,13 @@ bool editor_mode::move_double_paths(double_switch const &Set, std::vector<segmen
|
||||
Set.first->m_paths.assign(Paths.begin(), Paths.begin() + 2);
|
||||
Set.second->m_paths.assign(Paths.begin() + 2, Paths.begin() + 4);
|
||||
Set.link->m_paths = {reversed ? geometry::reversed(Paths[4]) : Paths[4]};
|
||||
std::string notes;
|
||||
for (std::size_t k = 0; k < 2; ++k)
|
||||
{
|
||||
auto const note{plan_switch_branches(*switches[k], joints[k], branches[k], handled[k])};
|
||||
if (false == note.empty())
|
||||
notes += (notes.empty() ? "" : "\n") + note;
|
||||
}
|
||||
std::string loose;
|
||||
for (std::size_t k = 0; k < 2; ++k)
|
||||
{
|
||||
@@ -312,9 +694,14 @@ bool editor_mode::move_double_paths(double_switch const &Set, std::vector<segmen
|
||||
loose += (loose.empty() ? "" : ", ") + left;
|
||||
}
|
||||
editor_track::commit(edit.changed);
|
||||
for (auto const &set : branches)
|
||||
for (auto const &branch : set)
|
||||
lay_switch_branch(branch, edit);
|
||||
push_track_snapshot(std::move(edit.states), std::move(edit.created), std::move(edit.removed));
|
||||
if (false == loose.empty())
|
||||
Status = STR_C("Too far from the new ends of the switch, left unjoined: ") + loose;
|
||||
if (false == notes.empty())
|
||||
Status += (Status.empty() ? "" : "\n") + notes;
|
||||
return true;
|
||||
}
|
||||
|
||||
@@ -404,6 +791,19 @@ void editor_mode::switch_slide_start(TTrack &Track)
|
||||
slide.joints.push_back(axis->along(path.points[index]));
|
||||
}
|
||||
}
|
||||
std::vector<TTrack const *> const skip(members.begin(), members.end());
|
||||
for (auto *member : members)
|
||||
{
|
||||
if (member->eType != tt_Switch)
|
||||
continue;
|
||||
std::size_t const offset{set.has_value() && member == set->second ? 2u : 0u};
|
||||
auto const joints{editor_track::joints(*member)};
|
||||
for (auto &branch : switch_branches(*member, joints, skip))
|
||||
{
|
||||
auto const &joint{joints[branch.joint]};
|
||||
slide.branches.push_back({joint, offset + static_cast<std::size_t>(joint.own.path), std::move(branch)});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void editor_mode::switch_slide_update()
|
||||
@@ -465,16 +865,27 @@ void editor_mode::draw_switch_slide() const
|
||||
}
|
||||
screen_projection const projection;
|
||||
auto *drawlist{ImGui::GetBackgroundDrawList(ImGui::GetMainViewport())};
|
||||
for (auto const &path : paths)
|
||||
{
|
||||
bezier const curve{path};
|
||||
auto const draw = [&](segment_data const &Path, ImU32 const Color, float const Width) {
|
||||
bezier const curve{Path};
|
||||
auto previous{curve.point(0.0)};
|
||||
for (int i = 1; i <= 24; ++i)
|
||||
{
|
||||
auto const next{curve.point(i / 24.0)};
|
||||
projection.line(drawlist, previous, next, overlay_color::marked, 3.0f);
|
||||
projection.line(drawlist, previous, next, Color, Width);
|
||||
previous = next;
|
||||
}
|
||||
};
|
||||
for (auto const &path : paths)
|
||||
draw(path, overlay_color::marked, 3.0f);
|
||||
// the curves past the switch, laid again up to the straight beyond them
|
||||
for (auto const &line : slide.branches)
|
||||
{
|
||||
if (line.path >= paths.size())
|
||||
continue;
|
||||
auto branch{line.branch};
|
||||
if (plan_switch_branch(paths[line.path], line.joint, branch))
|
||||
for (auto const &piece : branch.pieces)
|
||||
draw(piece, overlay_color::grip, 2.0f);
|
||||
}
|
||||
ImVec2 at;
|
||||
auto const start{paths.front().points[segment_data::point::start]};
|
||||
|
||||
Reference in New Issue
Block a user