17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-29 08:07:29 +01:00

Editor: rebuild the plan library around a solved document, and bend turnouts onto curves

The library was a heap of half-built services - fitting, parallelism, niwelety,
an SVG writer - that never met each other. In their place is one authored
Document and one solver that lays it whole: tracks as element chains, turnouts
placed on them, and a Solution that is rebuilt from scratch every time and never
fed back into the document. What cannot be laid comes back as a diagnostic with
the number in it rather than as silence or a fitted-away error.

Turnouts are dimensioned the way the drawings dimension them. The blade follows
Koc's rys. 5.9 - the curve is theoretically tangent to the opornica at PR, the
blade starts at a with a planed nose of d, and A is where the rail stands u
thick - and a + d derived that way lands on his tablica 5.1. Bending follows
5.3.6: it rotates the triangle OBC about the centre of the turnout, so the
tangent t and the crossing angle stand and both radii follow from them. That is
Koc's (5.7) and (5.8) without coding either, and it puts a symmetrically bent
Rz 49E1-190-1:9 on the catalogue's Rlus 49E1-380,292-1:9. A rozjazd standing on
a curve is bent onto it; one on a track drawn straight can carry a bend of its
own. The szyny laczace take up the difference, because the switch and the frog
are only bent, not re-cut.

The plan panel draws all of it: the catalogue drawing of a type with its
construction points, the theoretical arc dashed where no rail runs, and the
blade nose as the straight it is cut as. Dragging a straight now stops where it
would pull the track out from under a turnout.

File format goes to m0s 6. Versions 3 to 5 still open.
This commit is contained in:
maj00r
2026-08-29 19:32:46 +02:00
parent 8cd42980e6
commit 2376e4867b
97 changed files with 8608 additions and 9792 deletions

View File

@@ -17,6 +17,8 @@ http://mozilla.org/MPL/2.0/.
#include <windows.h>
#include <winhttp.h>
#pragma comment(lib, "winhttp.lib")
#else
#include <curl/curl.h>
#endif
namespace editor
@@ -127,9 +129,64 @@ bool http_get(std::string const &Url, std::vector<std::uint8_t> &Bytes, std::str
#else
bool http_get(std::string const &, std::vector<std::uint8_t> &, std::string &)
// libcurl's write callback: appends one chunk of the body to the caller's buffer
std::size_t append_body(char const *Data, std::size_t const Size, std::size_t const Count, void *Userdata)
{
return false; // no native transport on this platform yet
auto &bytes = *static_cast<std::vector<std::uint8_t> *>(Userdata);
auto const length = Size * Count;
bytes.insert(bytes.end(), Data, Data + length);
return length;
}
// plain HTTPS GET, the same shape as the WinHttp one above: one request, one answer, nothing kept
// between calls. libcurl wants its global init done once before any handle exists, and this is the
// only place in the program that uses it, so a function-local static does the job thread-safely
bool http_get(std::string const &Url, std::vector<std::uint8_t> &Bytes, std::string &Contenttype)
{
static bool const initialised = (curl_global_init(CURL_GLOBAL_DEFAULT) == CURLE_OK);
if (false == initialised)
{
return false;
}
CURL *handle = curl_easy_init();
if (handle == nullptr)
{
return false;
}
Bytes.clear();
curl_easy_setopt(handle, CURLOPT_URL, Url.c_str());
curl_easy_setopt(handle, CURLOPT_USERAGENT, kUserAgent);
curl_easy_setopt(handle, CURLOPT_FOLLOWLOCATION, 1L);
curl_easy_setopt(handle, CURLOPT_WRITEFUNCTION, append_body);
curl_easy_setopt(handle, CURLOPT_WRITEDATA, &Bytes);
// a stuck transfer must not hold a worker forever; the caller retries
curl_easy_setopt(handle, CURLOPT_CONNECTTIMEOUT, 10L);
curl_easy_setopt(handle, CURLOPT_TIMEOUT, 30L);
// workers run on their own threads: keep libcurl off signals, which are process-wide
curl_easy_setopt(handle, CURLOPT_NOSIGNAL, 1L);
bool ok = (curl_easy_perform(handle) == CURLE_OK);
if (true == ok)
{
long status = 0;
curl_easy_getinfo(handle, CURLINFO_RESPONSE_CODE, &status);
char const *type = nullptr;
if ((curl_easy_getinfo(handle, CURLINFO_CONTENT_TYPE, &type) == CURLE_OK) && (type != nullptr))
{
Contenttype.assign(type);
}
ok = (status == 200) && (false == Bytes.empty());
}
if (false == ok)
{
Bytes.clear();
}
curl_easy_cleanup(handle);
return ok;
}
#endif

View File

@@ -10,27 +10,74 @@ http://mozilla.org/MPL/2.0/.
#include "stdafx.h"
#include "editor/turnouts.h"
#include "maj0sted/domain/geometry/turnout.hpp"
#include <cmath>
namespace editor
{
namespace
{
// the blade ends where the gap to the stock rail takes the przekladka in its heel.
// this is what fixes the length of a curved blade, so it is said once here.
// TODO: dlugosci iglic z tablic Id-1, gdy beda pod reka
double const heel_offset{0.136}; // metres
// 49E1 railhead. the blade is planed from its tip until it is this wide
double const railtop_width{0.070};
// a turnout with an iglica lukowa styczna do opornicy o scietym ostrzu (Koc, rys. 5.9).
// the curve is theoretically tangent to the opornica at PR, but the blade cannot begin
// there - it begins where there is metal enough, at the ostrze, and its planed nose (d)
// runs on to the point where the rail is u thick. what lies between PR and the ostrze is
// the odcinek przediglicowy: the curve is already there, the vehicle is still on the
// through track. Koc's tablica 5.1 gives a, d and u per type, and they obey
// a + d = the run to a gap of u, taken on the blade's curve R + s/2
// so a is derived from u and d here rather than copied. The drawing dimensions it on the
// rail and a piece length is on the axis, so what goes in is the run scaled by R/(R+s/2);
// against the table itself that is a tenth of a millimetre at 1200-1:18,5, six at 190-1:9.
//
// b is the rear tangent from the drawing: a rozjazd podstawowy (krzyzownica lukowa) has
// b = R*tg(alfa/2) and no straight at all, while a straight crossing carries the arc out
// on a prosta krzyzownicowa of b - R*tg(alfa/2). Passing 0 asks for the podstawowy.
turnout_preset curved_tangent(std::string name, double crossing_n, double radius, double tip_thickness, double nose, double back_tangent = 0.0)
{
auto const alfa{std::atan(1.0 / crossing_n)};
auto const tangent{radius * std::tan(0.5 * alfa)};
auto const b{back_tangent > 0.0 ? back_tangent : tangent};
auto const lead{maj0sted::domain::blade_run_to_offset(radius, 0.0, tip_thickness) - nose};
auto const blade{maj0sted::domain::blade_run_to_offset(radius, 0.0, heel_offset) - lead};
turnout_preset preset{std::move(name), crossing_n, tangent + b, tip_thickness, nose, railtop_width, {}};
preset.pieces.push_back({0, lead, radius, radius, 0.0});
preset.pieces.push_back({1, blade, radius, radius, 0.0});
preset.pieces.push_back({2, radius * alfa - lead - blade, radius, radius, 0.0});
if (b - tangent > 1e-6)
{
preset.pieces.push_back({3, b - tangent, 0.0, 0.0, 0.0});
}
return preset;
}
} // namespace
std::vector<turnout_preset> const &turnout_presets()
{
// the standard PKP rozjazdy zwyczajne (49E1 / 60E1). crossing mark, radius and catalogue length
// (PR→KR along the through track). the crossing angles check out as atan(1/n); the lengths follow
// the type tables (metres). the diverging leaves not tangent but at the blade angle (beta) after a
// straight pre-blade lead, then arcs by (alfa - beta) to the frog.
//
// blade_angle, pre_blade and blade_length are NOT yet from Id-1 - left at 0, which lays a tangent
// arc straight from PR. Fill them per type when the catalogue figures are in; the geometry
// (domain::lay_turnout) already lays the pre-blade lead, the blade break and the shortened arc.
// the standard PKP rozjazdy zwyczajne. u and d come from Koc, tablica 5.1 (iglice
// styczne do opornicy o scietym ostrzu); the rear tangents from the catalogue drawings:
// rys. 5.13 gives Rz 49E1-190-1:9 as 27138 = 10523 + 16615 (krzyzownica prosta) and
// rys. 5.15 gives Rz 49E1-300-1:9 as 33230 = 16615 + 16615 (lukowa, rozjazd podstawowy).
// krzyzownice proste sa w 49E1-190-1:9 i 49E1-500-1:14, lukowe w 300-1:9, 500-1:12 i
// 1200-1:18,5. TODO: 1:7,5 R190 i 1:14 R500 czekaja na wlasne u/d i styczne z Id-1
static std::vector<turnout_preset> const presets{
{"Rz 1:7,5 R190", 7.5, 190.0, 25.222, 0.0, 0.0, 0.0},
{"Rz 1:9 R190", 9.0, 190.0, 27.138, 0.0, 0.0, 0.0},
{"Rz 1:9 R300", 9.0, 300.0, 33.230, 0.0, 0.0, 0.0},
{"Rz 1:12 R500", 12.0, 500.0, 41.594, 0.0, 0.0, 0.0},
{"Rz 1:14 R500", 14.0, 500.0, 42.371, 0.0, 0.0, 0.0},
{"Rz 1:14 R760", 14.0, 760.0, 54.216, 0.0, 0.0, 0.0},
{"Rz 1:18,5 R1200", 18.5, 1200.0, 64.818, 0.0, 0.0, 0.0}};
curved_tangent("Rz 1:7,5 R190", 7.5, 190.0, 0.0049, 0.1250),
curved_tangent("Rz 1:9 R190", 9.0, 190.0, 0.0049, 0.1250, 16.615),
curved_tangent("Rz 1:9 R300", 9.0, 300.0, 0.0044, 0.1250),
curved_tangent("Rz 1:12 R500", 12.0, 500.0, 0.0053, 0.1250),
curved_tangent("Rz 1:14 R500", 14.0, 500.0, 0.0053, 0.1250, 24.544),
curved_tangent("Rz 1:14 R760", 14.0, 760.0, 0.0050, 0.1250),
curved_tangent("Rz 1:18,5 R1200", 18.5, 1200.0, 0.0050, 0.1304)};
return presets;
}

View File

@@ -15,23 +15,33 @@ http://mozilla.org/MPL/2.0/.
namespace editor
{
// a standard turnout template: its designation and the defining pair a catalogue entry is built on -
// the crossing mark 1:n (skos) and the radius of the diverging arc. the actual length the switch
// works out to is computed geometrically from these (arc = R * atan(1/n)), shown next to it; the
// exact catalogue length can be pinned later if the Id-1 tables call for a different figure
// one piece of a preset's diverging path, in catalogue order from PR. mirrors
// maj0sted::editor::TurnoutPieceSpec so this header stays free of the library
struct turnout_preset_piece
{
int part; // 0 przediglicowy, 1 iglica, 2 luk, 3 krzyzownicowa
double length; // metres, always explicit - nothing here is fitted
double radius_start; // metres; 0 = straight
double radius_end; // metres; different from radius_start = clothoid
double turn_in; // kat nagiecia iglicy (beta), radians; 0 = tangent to the stock rail
};
// a standard turnout template: its designation, the catalogue numbers the laid path is
// checked against (skos 1:n and the length PR->KR along the through track), the pieces it
// is laid from, and the two blade dimensions that place its real, cut tip
struct turnout_preset
{
std::string name;
double crossing_n; // skos 1:n
double radius; // metres
double length; // catalogue length PR->KR along the through track, metres
double blade_angle; // kąt nagięcia iglicy (beta), radians; the diverging leaves the straight here
double pre_blade; // odcinek przediglicowy: straight lead PR -> blade tip, metres
double blade_length; // the straight blade itself, tip -> heel where the arc starts, metres
double crossing_n; // skos 1:n
double length; // catalogue length: a + b, the two tangent legs of the drawing, metres
double tip_thickness; // grubosc iglicy na koncu zestruganego dzioba (u), metres
double nose; // dziob iglicy: zestrugana dlugosc od ostrza (d), metres
double railtop_width; // szerokosc glowki szyny (w), metres
std::vector<turnout_preset_piece> pieces;
};
// typical Polish turnouts (rozjazdy zwyczajne). the (skos, R) pairs are the recognised standard;
// confirm against Id-1 before treating the derived length as the catalogue length
// typical Polish turnouts (rozjazdy zwyczajne). the (skos, R) pairs are the recognised
// standard; confirm the piece lengths and the blade against Id-1
std::vector<turnout_preset> const &turnout_presets();
} // namespace editor