mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-18 06:49:18 +02:00
A turnout's diverging track does not leave the through track tangentially. It runs straight along the through track to the blade tip (odcinek przediglicowy), breaks away there by the blade angle (kąt nagięcia iglicy, beta), runs straight over the blade to its heel, and only then curves - through alfa - beta, not the whole crossing angle - to the frog. Give TurnoutSegment a turn_in: a signed heading break applied before the segment is laid, which is the only way to express a track leaving by an angle rather than a tangent. lay_turnout then places the lead, the break and the blade ahead of the curve and hands the curve the angle that is actually left to spend; a zero blade length keeps the break and starts the curve at the tip. Reject a blade angle that is negative or already reaches the crossing angle. All-zero blade figures lay exactly the old tangent arc from PR, so existing layouts are untouched. Junction and turnout_preset carry the three new figures through to the panel; the presets themselves still hold zeros until the Id-1 catalogue numbers are in. Add test_turnout: the lead sits on the through track, the blade breaks by beta, and the frog still lands on the full crossing angle and the catalogue length, mirrored for a right-hand turnout.
180 lines
6.9 KiB
C++
180 lines
6.9 KiB
C++
/*
|
|
This Source Code Form is subject to the
|
|
terms of the Mozilla Public License, v.
|
|
2.0. If a copy of the MPL was not
|
|
distributed with this file, You can
|
|
obtain one at
|
|
http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#pragma once
|
|
|
|
#include "application/uilayer.h"
|
|
|
|
#include "editor/orthophoto.h"
|
|
#include "maj0sted/app/basket_draft.hpp"
|
|
#include "maj0sted/app/editor_document.hpp"
|
|
#include "maj0sted/app/straight_constraints.hpp"
|
|
#include "maj0sted/io/scn_export.hpp"
|
|
#include "maj0sted/editor/editor.hpp"
|
|
#include "maj0sted/editor/ribbon.hpp"
|
|
|
|
#include <vector>
|
|
|
|
// track layout tool: straights laid down by hand on the scenery itself and the curves fitted into
|
|
// the gaps between them. the panel holds only the controls - the drawing happens in the viewport,
|
|
// which the tool puts into a top-down orthographic plan view. none of the geometry is computed
|
|
// here: maj0sted solves the whole document and the panel paints what comes back.
|
|
class plan_panel : public ui_panel
|
|
{
|
|
|
|
public:
|
|
plan_panel(std::string const &Name, bool const Isopen);
|
|
|
|
void update() override;
|
|
void render_contents() override;
|
|
|
|
private:
|
|
// methods
|
|
void render_toolbar();
|
|
void render_draft();
|
|
void render_gaps();
|
|
void render_compound(maj0sted::editor::GapFit &Fit, std::size_t const Gap, bool &Dirty);
|
|
void fit_compound_to_guides();
|
|
void clear_fit_guides();
|
|
void render_storage();
|
|
void export_scn();
|
|
void render_newmap_dialog();
|
|
void render_location_dialog();
|
|
void start_map(bool const Georeferenced, double const Originx, double const Originy);
|
|
void handle_scene();
|
|
void draw_on_scene();
|
|
void solve();
|
|
void apply_constraints();
|
|
maj0sted::editor::NiweletaSpec *current_niweleta();
|
|
maj0sted::editor::NiweletaSpec const *current_niweleta() const;
|
|
void align_fits();
|
|
bool fit_applied(std::size_t const Gap) const;
|
|
// independent anchored straights: each endpoint is its own handle
|
|
maj0sted::editor::StraightSpec const *handle_straight(std::size_t const Niw, std::size_t const Index) const;
|
|
void append_vertex(double const X, double const Y);
|
|
void drop_last_vertex();
|
|
void delete_selected_straight();
|
|
void delete_current_niweleta();
|
|
void clear_selection();
|
|
// hit tests (screen pixels). niw=-1 means "current niweleta only"
|
|
bool hit_endpoint(ImVec2 const &Mouse, int &OutNiw, int &OutStr, int &OutEnd) const;
|
|
bool hit_straight(ImVec2 const &Mouse, int &OutNiw, int &OutStr, bool const AllNiwelety) const;
|
|
// nearest niweleta by its solved axis (curves included), for a forgiving double-click select
|
|
int nearest_niweleta_axis(ImVec2 const &Mouse, float const Tolerance) const;
|
|
// nearest station (arc length) on a through niweleta's axis to a plan point; for placing/moving switches
|
|
bool station_on_through(std::size_t const Through, double const Wx, double const Wy, double &OutStation) const;
|
|
// compound basket: draggable ends of entry/exit KP, free arcs and between-KP
|
|
bool hit_compound_station(ImVec2 const &Mouse, int &OutGap, int &OutKind, int &OutArc) const;
|
|
void drag_compound_station(double const X, double const Y);
|
|
// arc-first draft: continue from a straight end, click appends luk/KP by length
|
|
void begin_draft();
|
|
void discard_draft();
|
|
void start_draft_from_end(std::size_t const Str, int const End);
|
|
void append_draft_at(double const X, double const Y);
|
|
void undo_draft_fragment();
|
|
bool finish_draft();
|
|
bool reopen_draft_from_gap(std::size_t const Gap);
|
|
void rebuild_draft_preview();
|
|
bool draft_tip(double &OutX, double &OutY) const;
|
|
bool draft_end_pose(double &OutX, double &OutY, double &OutAz) const;
|
|
bool hit_draft_station(ImVec2 const &Mouse, int &OutKind, int &OutArc) const;
|
|
void drag_draft_station(double const X, double const Y);
|
|
bool anchor_draft_to_gap(std::size_t const Gap);
|
|
// switches (rozjazdy): a branch leaves a through niweleta without splitting it. clicking the
|
|
// through track drops a switch there and starts a branch niweleta pinned to its frog
|
|
void add_switch_on(std::size_t const Through, double const Wx, double const Wy);
|
|
void render_switches();
|
|
void go_to_plan();
|
|
|
|
// members
|
|
maj0sted::app::EditorDocument m_document;
|
|
std::vector<maj0sted::editor::NiweletaPolys> m_solved;
|
|
std::vector<maj0sted::editor::JunctionGeom> m_junctions;
|
|
std::size_t m_niweleta{0};
|
|
|
|
// switch placement: 0 idle, 1 waiting for a click on the through track
|
|
int m_pick_switch{0};
|
|
int m_sw_side{0}; // 0 left, 1 right
|
|
double m_sw_crossing{9.0}; // skos 1:n
|
|
double m_sw_radius{190.0}; // internal arc radius (metres)
|
|
double m_sw_length{0.0}; // catalogue length from the chosen template (0 = just the curve)
|
|
double m_sw_pre_blade{0.0}; // odcinek przediglicowy from the template, metres
|
|
double m_sw_blade_angle{0.0}; // kąt nagięcia iglicy (beta) from the template, radians
|
|
double m_sw_blade_length{0.0};// the straight blade from the template, metres
|
|
int m_drag_junction{-1}; // index of the switch being slid along its through track, or -1
|
|
|
|
int m_sel_straight{-1};
|
|
int m_sel_end{-1};
|
|
bool m_dragging{false};
|
|
// captured at drag start so Shift keeps the angle while the length changes
|
|
double m_drag_fixed_x{0.0};
|
|
double m_drag_fixed_y{0.0};
|
|
double m_drag_ux{1.0};
|
|
double m_drag_uy{0.0};
|
|
|
|
// compound station drag: kind 0=entry KP end, 1=free-arc end, 2=between-KP end, 3=exit KP start
|
|
bool m_dragging_cmp{false};
|
|
int m_cmp_gap{-1};
|
|
int m_cmp_kind{-1};
|
|
int m_cmp_arc{-1};
|
|
|
|
bool m_drawing{true};
|
|
bool m_pending{false};
|
|
double m_pendingx{0.0};
|
|
double m_pendingy{0.0};
|
|
|
|
// 0 = idle, 1 = picking parallel reference, 2 = picking skew reference
|
|
int m_pick_rel{0};
|
|
// after a reference is chosen, ask for the numeric parameter
|
|
bool m_ask_rel_param{false};
|
|
double m_ask_rel_value{4.5};
|
|
// compound: gap being edited; pick mode accumulates guide points, then Fit runs the solver
|
|
int m_sel_gap{-1};
|
|
bool m_pick_compound_point{false};
|
|
struct FitGuide
|
|
{
|
|
double x{0.0};
|
|
double y{0.0};
|
|
};
|
|
std::vector<FitGuide> m_fit_guides;
|
|
|
|
maj0sted::app::BasketDraft m_draft;
|
|
std::vector<maj0sted::editor::PlanPolyline> m_draft_polys;
|
|
// 0 idle, 1 pick straight end to continue from
|
|
int m_draft_place{0};
|
|
// next click appends: 0 = luk, 1 = KP (wej/między), 2 = KP wyjście (kończy: prosta + kotwica)
|
|
int m_draft_append_kind{0};
|
|
double m_draft_r{300.0};
|
|
double m_draft_finish_straight{100.0};
|
|
// 0 idle, 1 click a straight to choose the gap to anchor into
|
|
int m_anchor_pick{0};
|
|
bool m_dragging_draft{false};
|
|
int m_draft_kind{-1};
|
|
int m_draft_arc{-1};
|
|
|
|
char m_namebuf[128]{};
|
|
|
|
std::string m_status;
|
|
char m_path[256]{"editor/plan.m0s"};
|
|
char m_scn_path[256]{"scenery/plan_export.scn"};
|
|
|
|
bool m_pickingplace{false};
|
|
double m_pickx{0.0};
|
|
double m_picky{0.0};
|
|
double m_mapviewx{0.0};
|
|
double m_mapviewy{0.0};
|
|
double m_mapscale{0.0};
|
|
|
|
editor::wms_image m_topomap{maj0sted::editor::WmsConfig::geoportal_topo()};
|
|
editor::orthophoto_source m_ortho;
|
|
// 1 km topo cells for views wider than a kilometre (orto's 100 m grid is too dense there)
|
|
editor::orthophoto_source m_topo{maj0sted::editor::WmsConfig::geoportal_topo(), 1000, "topo"};
|
|
bool m_showortho{true};
|
|
};
|