mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-10-17 08:39:19 +02:00
Drop-in copy of C:\Users\maj00r\Projects\maj0sted-master minus its web/ directory - the WASM frontend is not being ported, the editor drives the same C++ API natively. What is left is plain C++20 with no dependencies: domain model (niweleta, plan and profile geometry, fitting, parallelism), project serialization, rail sampling and the editor document. src/web/ribbon.cpp was missing from the library source list; added, since without it the ribbon symbols never make it into the archive.
133 lines
5.6 KiB
C++
133 lines
5.6 KiB
C++
#include <cmath>
|
|
|
|
#include "check.hpp"
|
|
#include "maj0sted/maj0sted.hpp"
|
|
|
|
using namespace maj0sted::domain;
|
|
|
|
namespace {
|
|
|
|
void straight_computes_azimuth_and_length() {
|
|
// Going east (+x): azimuth = 90 deg (clockwise from north).
|
|
const Straight east{CartesianPosition{0.0, 0.0}, CartesianPosition{100.0, 0.0}};
|
|
CHECK(std::abs(east.length().metres() - 100.0) < 1e-9);
|
|
CHECK(std::abs(east.azimuth().degrees() - 90.0) < 1e-9);
|
|
|
|
// Going north (+y): azimuth = 0 deg.
|
|
const Straight north{CartesianPosition{0.0, 0.0}, CartesianPosition{0.0, 50.0}};
|
|
CHECK(std::abs(north.azimuth().degrees() - 0.0) < 1e-9);
|
|
CHECK(std::abs(north.length().metres() - 50.0) < 1e-9);
|
|
|
|
// A zero-length straight has no azimuth and is rejected.
|
|
CHECK_THROWS(Straight(CartesianPosition{1.0, 1.0}, CartesianPosition{1.0, 1.0}));
|
|
}
|
|
|
|
void arc_carries_radius_direction_and_optional_length() {
|
|
const CircularArc without_length{Radius::from_metres(300.0), TurnDirection::Left};
|
|
CHECK(!without_length.length().has_value());
|
|
|
|
const CircularArc with_length{Radius::from_metres(300.0), TurnDirection::Left,
|
|
Length::from_metres(45.0)};
|
|
CHECK(with_length.length().has_value());
|
|
CHECK(with_length.length().value().metres() == 45.0);
|
|
}
|
|
|
|
void adjacent_same_arcs_are_rejected() {
|
|
HorizontalAlignment same;
|
|
same.append(CircularArc{Radius::from_metres(300.0), TurnDirection::Left});
|
|
// same radius + same direction -> forbidden
|
|
CHECK_THROWS(
|
|
same.append(CircularArc{Radius::from_metres(300.0), TurnDirection::Left}));
|
|
|
|
HorizontalAlignment opposite;
|
|
opposite.append(CircularArc{Radius::from_metres(300.0), TurnDirection::Left});
|
|
// same radius, different direction -> allowed (S-curve)
|
|
opposite.append(CircularArc{Radius::from_metres(300.0), TurnDirection::Right});
|
|
CHECK(opposite.size() == 2);
|
|
|
|
HorizontalAlignment compound;
|
|
compound.append(CircularArc{Radius::from_metres(300.0), TurnDirection::Left});
|
|
// same direction, different radius -> allowed (compound curve)
|
|
compound.append(CircularArc{Radius::from_metres(500.0), TurnDirection::Left});
|
|
CHECK(compound.size() == 2);
|
|
}
|
|
|
|
void straight_to_straight_tangent_requires_equal_azimuth() {
|
|
// Collinear straights, same azimuth, touching -> tangent OK.
|
|
HorizontalAlignment collinear;
|
|
collinear.append(
|
|
Straight{CartesianPosition{0.0, 0.0}, CartesianPosition{50.0, 0.0}});
|
|
collinear.append(
|
|
Straight{CartesianPosition{50.0, 0.0}, CartesianPosition{100.0, 0.0}});
|
|
CHECK(collinear.size() == 2);
|
|
CHECK(collinear.joint(0) == JointContinuity::Tangent);
|
|
|
|
// Touching but different azimuth with a Tangent joint -> must be a break.
|
|
HorizontalAlignment unmarked;
|
|
unmarked.append(
|
|
Straight{CartesianPosition{0.0, 0.0}, CartesianPosition{50.0, 0.0}});
|
|
CHECK_THROWS(unmarked.append(
|
|
Straight{CartesianPosition{50.0, 0.0}, CartesianPosition{50.0, 50.0}}));
|
|
|
|
// Non-touching tangent straights -> invalid.
|
|
HorizontalAlignment gap;
|
|
gap.append(Straight{CartesianPosition{0.0, 0.0}, CartesianPosition{50.0, 0.0}});
|
|
CHECK_THROWS(gap.append(
|
|
Straight{CartesianPosition{60.0, 0.0}, CartesianPosition{100.0, 0.0}}));
|
|
}
|
|
|
|
void azimuth_break_between_straights_must_be_marked() {
|
|
// Corner marked as AzimuthBreak -> OK.
|
|
HorizontalAlignment kink;
|
|
kink.append(Straight{CartesianPosition{0.0, 0.0}, CartesianPosition{50.0, 0.0}});
|
|
kink.append(Straight{CartesianPosition{50.0, 0.0}, CartesianPosition{50.0, 50.0}},
|
|
JointContinuity::AzimuthBreak);
|
|
CHECK(kink.size() == 2);
|
|
CHECK(kink.joint(0) == JointContinuity::AzimuthBreak);
|
|
|
|
// AzimuthBreak with equal azimuth is pointless -> invalid.
|
|
HorizontalAlignment straight_break;
|
|
straight_break.append(
|
|
Straight{CartesianPosition{0.0, 0.0}, CartesianPosition{50.0, 0.0}});
|
|
CHECK_THROWS(straight_break.append(
|
|
Straight{CartesianPosition{50.0, 0.0}, CartesianPosition{100.0, 0.0}},
|
|
JointContinuity::AzimuthBreak));
|
|
|
|
// AzimuthBreak is only allowed between two straights.
|
|
HorizontalAlignment arc_break;
|
|
arc_break.append(
|
|
Straight{CartesianPosition{0.0, 0.0}, CartesianPosition{50.0, 0.0}});
|
|
CHECK_THROWS(arc_break.append(
|
|
CircularArc{Radius::from_metres(300.0), TurnDirection::Left},
|
|
JointContinuity::AzimuthBreak));
|
|
}
|
|
|
|
void straight_curve_joints_are_accepted_pending_fit() {
|
|
// prosta -> klotoida -> łuk -> klotoida -> prosta
|
|
HorizontalAlignment plan;
|
|
plan.append(Straight{CartesianPosition{0.0, 0.0}, CartesianPosition{50.0, 0.0}});
|
|
plan.append(TransitionCurve{Length::from_metres(30.0), std::nullopt,
|
|
Radius::from_metres(300.0), TurnDirection::Left});
|
|
plan.append(CircularArc{Radius::from_metres(300.0), TurnDirection::Left,
|
|
Length::from_metres(40.0)});
|
|
plan.append(TransitionCurve{Length::from_metres(30.0),
|
|
Radius::from_metres(300.0), std::nullopt,
|
|
TurnDirection::Left});
|
|
plan.append(Straight{CartesianPosition{200.0, 0.0}, CartesianPosition{260.0, 0.0}});
|
|
CHECK(plan.size() == 5);
|
|
// Known lengths: 50 + 30 + 40 + 30 + 60.
|
|
CHECK(plan.total_length().metres() == 210.0);
|
|
}
|
|
|
|
} // namespace
|
|
|
|
int main() {
|
|
RUN(straight_computes_azimuth_and_length);
|
|
RUN(arc_carries_radius_direction_and_optional_length);
|
|
RUN(adjacent_same_arcs_are_rejected);
|
|
RUN(straight_to_straight_tangent_requires_equal_azimuth);
|
|
RUN(azimuth_break_between_straights_must_be_marked);
|
|
RUN(straight_curve_joints_are_accepted_pending_fit);
|
|
return REPORT();
|
|
}
|