17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-31 09:59:06 +01:00

feat(editor): curves over 180°, click to load curve

This commit is contained in:
maj00r
2026-10-02 23:14:03 +02:00
parent 9877ebe261
commit a392364f06
6 changed files with 143 additions and 14 deletions

View File

@@ -238,8 +238,8 @@ result compute(design const &Design)
{
auto const t{cross(offset, enddirection) / determinant};
auto const u{cross(startdirection, offset) / determinant};
if (t <= 0.0 || u <= 0.0)
r.errors.emplace_back("Tangents of the ends intersect outside of the fragment, use two vertices");
if ((t <= 0.0 || u <= 0.0) && false == Design.vertices.front().reverse_turn)
r.errors.emplace_back("Tangents of the ends intersect outside of the fragment, turn the longer way or use two vertices");
r.vertices[0] = start + startdirection * t;
}
}
@@ -303,12 +303,15 @@ result compute(design const &Design)
auto const directionin{glm::normalize(polygon[k + 1] - polygon[k])};
auto const directionout{glm::normalize(polygon[k + 2] - polygon[k + 1])};
auto const turn{cross(directionin, directionout)};
auto const deflection{std::atan2(std::abs(turn), glm::dot(directionin, directionout))};
auto const geometric{std::atan2(std::abs(turn), glm::dot(directionin, directionout))};
auto const reverse{Design.vertices[k].reverse_turn};
auto const deflection{reverse ? 2.0 * kPi - geometric : geometric};
int const side{(turn > 0.0) != reverse ? 1 : -1};
if (deflection < 1e-7)
continue;
if (deflection > kPi - 1e-3)
if (geometric > kPi - 1e-3 || (reverse && geometric < 1e-3))
{
r.errors.emplace_back(format("Vertex %zu: the route turns back on itself", k + 1));
r.errors.emplace_back(format("Vertex %zu: a turn of about 180 or 360 degrees needs one more vertex", k + 1));
return r;
}
auto const angles = [&](double const Factor) {
@@ -327,8 +330,7 @@ result compute(design const &Design)
fit.transition_out *= low;
}
fit.deflection = deflection;
fit.tangents = [&, k, directionin, directionout, turn](double const Radius, double &Tangentin, double &Tangentout) {
int const side{turn > 0.0 ? 1 : -1};
fit.tangents = [&, k, directionin, directionout, side](double const Radius, double &Tangentin, double &Tangentout) {
auto const endin{transition_point(Design.shape, fits[k].transition_in, Radius, 1.0)};
auto const endout{transition_point(Design.shape, fits[k].transition_out, Radius, 1.0)};
auto const a{Radius + endin.y - Radius * (1.0 - std::cos(endin.angle))};
@@ -408,7 +410,10 @@ result compute(design const &Design)
auto const directionin{glm::normalize(polygon[k + 1] - polygon[k])};
auto const directionout{glm::normalize(polygon[k + 2] - polygon[k + 1])};
auto const turn{cross(directionin, directionout)};
auto const deflection{std::atan2(std::abs(turn), glm::dot(directionin, directionout))};
auto const geometric{std::atan2(std::abs(turn), glm::dot(directionin, directionout))};
auto const reverse{Design.vertices[k].reverse_turn};
auto const deflection{reverse ? 2.0 * kPi - geometric : geometric};
int const side{(turn > 0.0) != reverse ? 1 : -1};
auto const lead{glm::dot(position - cursor, directionin)};
if (deflection < 1e-7)
@@ -420,12 +425,11 @@ result compute(design const &Design)
cursor = position;
continue;
}
if (deflection > kPi - 1e-3)
if (geometric > kPi - 1e-3 || (reverse && geometric < 1e-3))
{
r.errors.emplace_back(format("Vertex %zu: the route turns back on itself", k + 1));
r.errors.emplace_back(format("Vertex %zu: a turn of about 180 or 360 degrees needs one more vertex", k + 1));
return r;
}
int const side{turn > 0.0 ? 1 : -1};
auto const radius{fit.radius};
auto const lengthin{fit.transition_in};
auto const lengthout{fit.transition_out};

View File

@@ -48,6 +48,7 @@ struct vertex
bool auto_elevation{true};
double elevation{0.0};
double vertical_radius{0.0};
bool reverse_turn{false};
};
struct design

View File

@@ -22,6 +22,7 @@ http:
#include <cmath>
#include <limits>
#include <deque>
#include <numeric>
#include <functional>
#include <sstream>
#include <unordered_set>
@@ -971,6 +972,77 @@ std::vector<editor_track::straight> editor_track::find_straights(double const Mi
return result;
}
bool editor_track::find_curve(TTrack &Track, straight_tolerance const &Tolerance, double const Gauge, curve &Curve)
{
Curve = {};
auto const curved = [&](TTrack const *Other) { return Other != nullptr && Other->eType == tt_Normal && is_supported(*Other) && false == Other->m_editorremoved && false == is_straight(*Other, Tolerance); };
if (false == curved(&Track))
return false;
std::deque<std::pair<TTrack *, bool>> run{{&Track, true}};
for (auto const throughend : {true, false})
{
TTrack *previous{&Track};
TTrack *current{throughend ? Track.trNext : Track.trPrev};
while (curved(current) && std::none_of(run.begin(), run.end(), [&](auto const &Member) { return Member.first == current; }))
{
auto const entersatstart{current->trPrev == previous};
if (throughend)
run.emplace_back(current, entersatstart);
else
run.emplace_front(current, false == entersatstart);
previous = current;
current = entersatstart ? current->trNext : current->trPrev;
}
}
std::vector<double> turns;
std::vector<double> lengths;
std::vector<bool> varying;
double smallest{0.0};
for (auto const &member : run)
{
auto const &path{member.first->m_paths.front()};
auto const tangentstart{glm::normalize(plan_of(path_tangent(path, false)))};
auto const tangentend{glm::normalize(plan_of(path_tangent(path, true)))};
auto turn{std::atan2(lateral_of(tangentstart, tangentend), glm::dot(tangentstart, tangentend))};
if (false == member.second)
turn = -turn;
turns.push_back(turn);
lengths.push_back(member.first->Length());
auto const radiusstart{path_radius(path, 0.0)};
auto const radiusend{path_radius(path, 1.0)};
auto const curvaturestart{radiusstart > 0.0 ? 1.0 / radiusstart : 0.0};
auto const curvatureend{radiusend > 0.0 ? 1.0 / radiusend : 0.0};
varying.push_back(std::abs(curvaturestart - curvatureend) > 0.1 * std::max(curvaturestart, curvatureend));
auto const radius{path_radius(path, 0.5)};
if (radius > 0.0 && (smallest == 0.0 || radius < smallest))
smallest = radius;
for (auto const roll : path.rolls)
Curve.cant = std::max(Curve.cant, Gauge * std::sin(std::abs(glm::radians(static_cast<double>(roll)))));
}
int sign{0};
for (auto const turn : turns)
{
Curve.turn += turn;
int const current{turn > 1e-9 ? 1 : turn < -1e-9 ? -1 : 0};
if (current != 0 && sign != 0 && current != sign)
++Curve.reversals;
if (current != 0)
sign = current;
}
for (std::size_t i = 0; i < run.size() && varying[i]; ++i)
Curve.transition_in += lengths[i];
for (std::size_t i = run.size(); i > 0 && varying[i - 1]; --i)
Curve.transition_out += lengths[i - 1];
if (Curve.transition_in + Curve.transition_out >= std::accumulate(lengths.begin(), lengths.end(), 0.0))
Curve.transition_in = Curve.transition_out = 0.0;
Curve.radius = smallest;
Curve.from = run.front().first;
Curve.to = run.back().first;
return true;
}
std::vector<TTrack *> editor_track::straight_affected(std::vector<straight> const &Lines)
{
std::vector<TTrack *> result;

View File

@@ -117,6 +117,18 @@ class editor_track
static straight find_straight(TTrack &Track, straight_tolerance const &Tolerance);
static std::vector<straight> find_straights(double const Minimumlength, straight_tolerance const &Tolerance);
static std::vector<TTrack *> straight_affected(std::vector<straight> const &Lines);
struct curve
{
TTrack *from{nullptr};
TTrack *to{nullptr};
double turn{0.0};
int reversals{0};
double radius{0.0};
double transition_in{0.0};
double transition_out{0.0};
double cant{0.0};
};
static bool find_curve(TTrack &Track, straight_tolerance const &Tolerance, double const Gauge, curve &Curve);
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);