17
0
mirror of https://github.com/MaSzyna-EU07/maszyna.git synced 2026-10-26 23:59:48 +01:00

fix(rails): the rail going on from the heel of a blade raised as the blade at the heel and forged down to the plain rail along 0.5 m, no step at the heel; the wing rails made over into the low section by a share of their own along them, the rise apart from it; a fastening holding raised rails, as the plate of a frog under the wing rails and the nose, moved up on a pad as far as the least raised of them, its clips with them, a rail not raised sunk in it, a rail raised more on a pad of its own on it; the fastenings selftest lists the lifts and the pads

This commit is contained in:
maj00r
2026-10-23 22:00:11 +02:00
parent 0513a92bc3
commit 997a8da41d
9 changed files with 98 additions and 54 deletions

View File

@@ -172,8 +172,11 @@ editor_mode::selftest_result editor_mode::selftest_sleepers(std::string const &c
std::string text{format("SELFTEST fastenings %s at %.2f %.2f:", track->name().c_str(), middle.x, middle.z)};
for (auto const &fastening : rails.on(beam, *model))
{
text += format(" %s %.3f %.3f %+.0f", track_sleepers::part_names[fastening.part], fastening.point.x, fastening.point.z, glm::degrees(std::atan2(fastening.across.y, fastening.across.x)));
++counts[fastening.part];
text += format(" %s%s %.3f %.3f %+.0f", fastening.pad ? "pad:" : "", track_sleepers::part_names[fastening.part], fastening.point.x, fastening.point.z, glm::degrees(std::atan2(fastening.across.y, fastening.across.x)));
if (fastening.lift != 0.f)
text += format(" ^%.3f", fastening.lift);
if (false == fastening.pad)
++counts[fastening.part];
}
WriteLog(text);
}

View File

@@ -383,6 +383,7 @@ struct rail_piece {
float lift { 0.f };
float wallu { -1.f };
knots::line rises;
knots::line lows;
};
struct top_band {
@@ -609,6 +610,7 @@ private:
double const m_guardoffset;
double const m_rise; // of the feet of the wing rails of the low section
glm::dvec2 const m_stock; // the stretch the rail is the stock rail of a blade along
double const m_heel; // of the blade the rail goes on from
std::size_t m_topfirst;
std::size_t m_toplast { 0 };
std::vector<material_handle> m_tints;
@@ -632,6 +634,7 @@ TTrack::rail_layer::rail_layer( TTrack &Track, rail_layout const &Layout, rail_d
m_guardoffset { -( flangeway + Drawing.size.head ) },
m_rise { track_sleepers::wing_rise( Track ) },
m_stock { track_sleepers::stock_stretch( Track, Path, Side ) },
m_heel { track_sleepers::blade_heel( Track, Path, Side ) },
m_topfirst { Profile.size() } {
// the running surface on top of the head is split along the edge of the wheel treads, the part of it out of their reach goes matte; the rest of the profile and the end faces are laid whole
for( std::size_t j = 0; j < Profile.size(); ++j ) {
@@ -797,7 +800,8 @@ void TTrack::rail_layer::lay_cuts( gfx::vertex_array const &Shape, std::vector<r
// a piece carved between two cuts has the same lines at both
auto const own { i < Cuts.size() && false == ( i > 0 && Cuts[ i - 1 ].carved && Cuts[ i ].carved ) };
auto const pieceoffsets { part_offsets( i > 0 ? &Cuts[ i - 1 ] : nullptr, i < Cuts.size() ? &Cuts[ i ] : nullptr ) };
auto const piecerises { part_rises( i > 0 ? &Cuts[ i - 1 ] : nullptr, i < Cuts.size() ? &Cuts[ i ] : nullptr, Wheels ? m_rise : 0.0, m_stock ) };
auto const pieceshares { Wheels && m_rise > 0.0 ? wing_shares( i > 0 ? &Cuts[ i - 1 ] : nullptr, i < Cuts.size() ? &Cuts[ i ] : nullptr, m_stock ) : knots::line {} };
auto const piecerises { part_rises( pieceshares, Wheels ? m_rise : 0.0, m_heel ) };
// the limits of the head from both ends can overlap along a short piece, as the planed wing rail of one frog and the cut of the next: the tighter of them holds
auto const pieceextents { own ? knots::bound( extents, Cuts[ i ].endextents, true ) : extents };
auto const pieceinner { own ? knots::bound( inner, Cuts[ i ].endinner, false ) : inner };
@@ -846,7 +850,8 @@ void TTrack::rail_layer::lay_cuts( gfx::vertex_array const &Shape, std::vector<r
.capto = k + 1 == splits.size() && capend,
.walls = false == Wheels,
.wallu = Wallu,
.rises = piecerises };
.rises = piecerises,
.lows = pieceshares };
if( Wheels && tread != nullptr && false == tread->edge.empty() && m_topfirst < m_toplast ) {
treaded( whole, *tread, ends );
}
@@ -1279,9 +1284,10 @@ loft_span TTrack::rail_layer::span( rail_piece const &Piece, piece_ends const &E
result.walls = Piece.walls;
result.wallu = Piece.wallu;
result.rises = Piece.rises;
result.lows = Piece.lows;
if( false == Piece.rises.empty() ) {
result.head = { m_drawing.size.neck, m_drawing.size.crest };
result.low = { m_rise, low_web, low_inward, low_outward, low_edge, low_root };
result.low = { low_web, low_inward, low_outward, low_edge, low_root };
}
if( std::abs( Piece.from - Ends.from ) < 1e-6 ) {
result.fromend = Ends.fromend;

View File

@@ -185,21 +185,21 @@ inline knots::line part_offsets( rail_cut const *Before, rail_cut const *After )
knots::sort( result );
return result;
}
// how far the foot of a part of a rail between two cuts is raised, Rise along a wing rail of the low section: from wing_low before the throat on,
// down to the plain rail past it along its forged end; the rail whole along Stock, the stock rail of a blade there
inline knots::line part_rises( rail_cut const *Before, rail_cut const *After, double const Rise, glm::dvec2 const &Stock ) {
// how much of the low section of the blades a part of a rail between two cuts is of along it, 0 to 1: a wing rail from wing_low before the throat
// on, down to the plain rail past it along its forged end; the rail whole along Stock, the stock rail of a blade there
inline knots::line wing_shares( rail_cut const *Before, rail_cut const *After, glm::dvec2 const &Stock ) {
knots::line result;
auto const going { Before != nullptr && Before->startkind == rail_end::point };
auto const coming { After != nullptr && After->endkind == rail_end::point };
if( Rise <= 0.0 || ( false == going && false == coming ) ) {
if( false == going && false == coming ) {
return result;
}
auto const lowto { going ? Before->startanchor + wing_low : 0.0 };
auto const lowfrom { coming ? After->endanchor - wing_low : 0.0 };
auto const through { going && coming && lowto + 2.0 * wing_forged >= lowfrom };
auto const stock { Stock.y > Stock.x };
// the share of the rise: whole along the low section, none along the stock rail, between along the forged ends
// whole along the low section, none along the stock rail, between along the forged ends
auto const share = [&]( double const At ) {
auto const low { through ? 1.0 : std::max(
going ? std::clamp( ( lowto + wing_forged - At ) / wing_forged, 0.0, 1.0 ) : 0.0,
@@ -210,7 +210,33 @@ inline knots::line part_rises( rail_cut const *Before, rail_cut const *After, do
std::sort( places.begin(), places.end() );
for( auto const at : places ) {
if( result.empty() || at > result.back().x + 1e-6 ) {
result.emplace_back( at, Rise * share( at ) );
result.emplace_back( at, share( at ) );
}
}
return result;
}
// how far the foot of the part is raised: by Rise along the low section of a wing rail of Shares, and from the heel of a blade at Heel, the
// rail going on from it, down to the plain rail along the forged end of the blade; not along the blade itself, raised on its own
inline knots::line part_rises( knots::line const &Shares, double const Rise, double const Heel ) {
knots::line result;
if( Rise <= 0.0 || ( Shares.empty() && Heel <= 0.0 ) ) {
return result;
}
std::vector<double> places;
for( auto const &knot : Shares ) {
places.push_back( knot.x );
}
if( Heel > 0.0 ) {
places.push_back( Heel - 1e-3 );
places.push_back( Heel );
places.push_back( Heel + wing_forged );
}
std::sort( places.begin(), places.end() );
for( auto const at : places ) {
if( result.empty() || at > result.back().x + 1e-6 ) {
auto const heel { Heel > 0.0 && at >= Heel ? std::clamp( ( Heel + wing_forged - at ) / wing_forged, 0.0, 1.0 ) : 0.0 };
result.emplace_back( at, Rise * std::max( knots::sample( Shares, at, 0.0 ), heel ) );
}
}
return result;

View File

@@ -717,7 +717,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
for( auto distance { std::ceil( Span.from / 0.5 ) * 0.5 }; distance < std::min( Span.to, m_dense ); distance += 0.5 ) {
stations.push_back( distance );
}
for( auto const *line : { &Span.offsets, &Span.extents, &Span.inner, &Span.drops, &Span.rises } ) {
for( auto const *line : { &Span.offsets, &Span.extents, &Span.inner, &Span.drops, &Span.rises, &Span.lows } ) {
for( auto const &knot : *line ) {
if( knot.x > Span.from && knot.x < Span.to ) {
stations.push_back( knot.x );
@@ -771,7 +771,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
} drawn;
auto const across = [&]( gfx::basic_vertex const &Point ) {
return static_cast<double>( Span.side * Point.position.x - Span.halfwidth ); };
auto low { Span.low.full > 0.0 && Span.head.x > bottom };
auto low { false == Span.lows.empty() && Span.head.x > bottom };
if( low ) {
glm::dvec2 foot { 1e6, -1e6 };
for( auto const &point : ShapePoints ) {
@@ -806,11 +806,11 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
}
low = ( drawn.web < std::min( drawn.inward, drawn.outward ) && drawn.web < drawn.head && drawn.edge > 0.0 && drawn.edge < drawn.root && drawn.root < Span.head.x - bottom && drawn.underside < 1e6 );
}
// the profile made over into the low section with its foot raised by Rise, by as much of it as the rise is of the full one: below the
// head the web thickened, the foot reshaped, the web shortened
// the profile with its foot raised by Rise made over into the low section by Share of it: below the head the web thickened, the foot
// reshaped, the web shortened; with none of it, only raised
std::vector<gfx::basic_vertex> lowpoints;
auto const lowered = [&]( double const Rise ) -> gfx::vertex_array const & {
auto const share { std::clamp( Rise / Span.low.full, 0.0, 1.0 ) };
auto const lowered = [&]( double const Rise, double const Share ) -> gfx::vertex_array const & {
auto const share { std::clamp( Share, 0.0, 1.0 ) };
auto const web { drawn.web * glm::mix( 1.0, Span.low.web, share ) };
auto const inward { drawn.inward * glm::mix( 1.0, Span.low.inward, share ) };
auto const outward { drawn.outward * glm::mix( 1.0, Span.low.outward, share ) };
@@ -840,7 +840,7 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
height <= drawn.edge ? height * edge / drawn.edge :
height <= drawn.root ? edge + ( height - drawn.edge ) * ( root - edge ) / ( drawn.root - drawn.edge ) :
root + ( height - drawn.root ) * ( top - root ) / ( below - drawn.root ) };
point.position.y = static_cast<float>( bottom + below - top + made );
point.position.y = static_cast<float>( glm::mix( static_cast<double>( raised( point.position.y, Rise ) ), bottom + below - top + made, share ) );
}
point.position.x = static_cast<float>( Span.side * ( Span.halfwidth + drawn.middle + std::copysign( moved, off ) ) );
}
@@ -879,7 +879,8 @@ bool TSegment::RenderLoftSpan( gfx::vertex_array &Output, glm::dvec3 const &Orig
auto const worn { knots::sample( Span.ramp, Distance, -1e6 ) };
auto const drop { knots::sample( Span.drops, Distance, 0.0 ) };
auto const sampled { Span.rises.empty() ? Span.rise : knots::sample( Span.rises, Distance, 0.0 ) };
auto const &points { low && sampled > 0.0 ? lowered( sampled ) : ShapePoints };
auto const share { low ? knots::sample( Span.lows, Distance, 0.0 ) : 0.0 };
auto const &points { low && ( sampled > 0.0 || share > 0.0 ) ? lowered( sampled, share ) : ShapePoints };
auto const rise { low ? 0.0 : sampled };
static loft_end const unshaped;
auto const &shaped { Distance <= Span.from ? Span.fromend : Distance >= Span.to ? Span.toend : unshaped };

View File

@@ -31,7 +31,6 @@ struct loft_end {
// a rail profile made over below its head into a lower section, as the wing rails of a frog of the section of the blades: its web, its
// foot inwards and outwards as many times as thick and wide, the top of the edges of its foot and the root of its web as many times as high
struct loft_low {
double full { 0.0 }; // the rise of the foot the profile is the whole low section at; none where it's 0
double web { 1.0 };
double inward { 1.0 };
double outward { 1.0 };
@@ -60,7 +59,8 @@ struct loft_span {
glm::dvec2 head { 0.0 };
double rise { 0.0 }; // how far the foot of the profile is raised, the web below the head (head.x) shortened by as much
knots::line rises; // along the span, in place of rise where given
loft_low low; // what the profile is made over into as its foot is raised
knots::line lows; // along the span, how much of the low section the profile is made over into below its head, 0 to 1
loft_low low; // what the profile is made over into along lows
};
struct segment_data {

View File

@@ -53,14 +53,11 @@ fastened_rails::fastened_rails( TTrack &Track ) {
auto const cut { track_rails::cut_rails( *track ) };
auto const switched { track->eType == tt_Switch && track->SwitchExtension != nullptr };
auto const rise { wing_rise( *track ) };
// the side of the blade of the path
auto const bladeside = [&]( int const Path ) {
return ( track->SwitchExtension->RightSwitch == ( Path == 0 ) ? 1 : -1 ); };
// what the rail is along the path: a blade, the stock rail of a blade, checked
auto const add = [&]( rail &&Line, int const Path, int const Side ) {
if( switched ) {
if( Side == bladeside( Path ) ) {
Line.blade = { 0.0, track_rails::blade_length( *track, Path ) };
if( auto const heel { blade_heel( *track, Path, Side ) }; heel > 0.0 ) {
Line.blade = { 0.0, heel };
}
Line.stock = stock_stretch( *track, Path, Side );
}
@@ -95,7 +92,7 @@ fastened_rails::fastened_rails( TTrack &Track ) {
line.track = track;
line.path = path;
line.side = side;
line.rises = track_rails::part_rises( before, after, rise, stock_stretch( *track, path, side ) );
line.rises = track_rails::part_rises( rise > 0.0 ? track_rails::wing_shares( before, after, stock_stretch( *track, path, side ) ) : knots::line {}, rise, blade_heel( *track, path, side ) );
line.foot = size.web - size.low;
line.low = glm::dvec2 { std::numeric_limits<double>::max() };
line.high = glm::dvec2 { std::numeric_limits<double>::lowest() };
@@ -673,33 +670,34 @@ void fastened_rails::lift( std::vector<fastening> &Fastenings, std::vector<hit>
auto const reach { footprint( Fastening ) };
return Fastening.part != part_chair && Rail.at >= reach.x && Rail.at <= reach.y; };
std::vector<fastening> pads;
std::vector<bool> lifted( Fastenings.size(), false );
for( auto &fastening : Fastenings ) {
if( fastening.pad || fastening.part == part_chair ) {
continue;
}
// moved up on a pad as wide as it as far as the least raised of the raised rails it holds, a rail not raised sunk in it
auto lift { 0.0 };
for( auto const &rail : Rails ) {
if( rail.rise >= pad_least && holds( fastening, rail ) ) {
lift = ( lift > 0.0 ? std::min( lift, rail.rise ) : rail.rise );
}
}
if( lift <= 0.0 ) {
continue;
}
auto const &span { Model.spans[ fastening.part ] };
auto const a { fastening.placed( span.x, fastening.scale ) };
auto const b { fastening.placed( span.y, fastening.scale ) };
pads.push_back( pad( fastening, std::min( a, b ), std::max( a, b ), Model.bed, Model.heights[ fastening.part ].x + static_cast<float>( lift ) ) );
fastening.lift = static_cast<float>( lift );
}
// a rail raised more than its fastening on a pad of its own on it, as wide as its foot, short of the rails beside
for( std::size_t index = 0; index < Rails.size(); ++index ) {
auto const &rail { Rails[ index ] };
if( rail.rise < pad_least ) {
auto const under { std::find_if( Fastenings.begin(), Fastenings.end(), [&]( fastening const &Fastening ) {
return false == Fastening.pad && holds( Fastening, rail ); } ) };
if( under == Fastenings.end() || rail.rise - under->lift < pad_least ) {
continue;
}
auto const under { std::find_if( Fastenings.begin(), Fastenings.end(), [&]( fastening const &Fastening ) { return holds( Fastening, rail ); } ) };
if( under == Fastenings.end() ) {
continue;
}
auto const which { static_cast<std::size_t>( under - Fastenings.begin() ) };
if( lifted[ which ] ) {
continue;
}
auto const alike { std::all_of( Rails.begin(), Rails.end(), [&]( hit const &Other ) {
return false == holds( *under, Other ) || std::abs( Other.rise - rail.rise ) < pad_least; } ) };
if( alike ) {
// the fastening holding only the rails raised as much, moved up as far on a pad as wide as it
auto const &span { Model.spans[ under->part ] };
auto const a { under->placed( span.x, under->scale ) };
auto const b { under->placed( span.y, under->scale ) };
pads.push_back( pad( *under, std::min( a, b ), std::max( a, b ), Model.bed, Model.heights[ under->part ].x + static_cast<float>( rail.rise ) ) );
under->lift = static_cast<float>( rail.rise );
lifted[ which ] = true;
continue;
}
// the rail on a pad of its own on the fastening, as wide as its foot, short of the rails beside
auto low { rail.foot };
auto high { rail.foot };
if( index > 0 ) {
@@ -712,7 +710,7 @@ void fastened_rails::lift( std::vector<fastening> &Fastenings, std::vector<hit>
continue;
}
fastening const at { padpart, Beam.from + ( Beam.to - Beam.from ) * ( rail.at / glm::length( plan( Beam.to - Beam.from ) ) ), axis };
pads.push_back( pad( at, -low, high, Model.seat, Model.seat + static_cast<float>( rail.rise ) ) );
pads.push_back( pad( at, -low, high, Model.seat + under->lift, Model.seat + static_cast<float>( rail.rise ) ) );
}
Fastenings.insert( Fastenings.end(), pads.begin(), pads.end() );
}

View File

@@ -64,7 +64,7 @@ private:
TTrack *track { nullptr };
int path { 0 };
int side { 1 };
knots::line rises; // of its foot, along the path, a wing rail of the low section raised as the blades
knots::line rises; // of its foot, along the path: a wing rail of the low section raised as the blades, the rail going on from the heel of a blade
std::vector<glm::dvec2> points;
std::vector<double> heights;
std::vector<glm::dvec2> inward; // unit vectors in plan from the rail toward the middle of its path
@@ -92,8 +92,8 @@ private:
// the fastenings of rails close by fitted to the room between them: narrowed across, a plate cut short at the fastening of the
// rail beside it or left out, the plates of two rails lying over each other made one
static void fit( std::vector<fastening> &Fastenings, beam const &Beam, beam_model const &Model );
// the fastenings under the raised rails: one holding only rails raised as much moved up as far on a pad, a rail raised
// over a fastening with others on a pad of its own on it
// the fastenings under the raised rails: one holding them moved up on a pad as far as the least raised of them, a rail not raised
// sunk in it, a rail raised more on a pad of its own on it
static void lift( std::vector<fastening> &Fastenings, std::vector<hit> const &Rails, beam const &Beam, beam_model const &Model );
std::vector<rail> m_rails;

View File

@@ -85,6 +85,14 @@ glm::dvec2 stock_stretch( TTrack const &Track, int const Path, int const Side )
return { std::max( 0.0, ( shape.valid ? shape.tip : 0.0 ) - chair_lead ), track_rails::blade_length( Track, 1 - Path ) };
}
double blade_heel( TTrack const &Track, int const Path, int const Side ) {
if( Track.eType != tt_Switch || Track.SwitchExtension == nullptr || Side != ( Track.SwitchExtension->RightSwitch == ( Path == 0 ) ? 1 : -1 ) ) {
return 0.0;
}
return track_rails::blade_length( Track, Path );
}
std::vector<std::pair<double, double>> shared_zones( TTrack const &Track, int const Path ) {
std::vector<std::pair<double, double>> result;

View File

@@ -130,6 +130,8 @@ double wing_rise( TTrack const &Track );
// the stretch along the path its rail of Side is the stock rail of the blade of the other path along, with the slide chairs from
// a little before the tip of the blade to its heel; from past to where there's none
glm::dvec2 stock_stretch( TTrack const &Track, int const Path, int const Side );
// how far along the path its rail of Side is the blade of the path, to its heel, the rail going on from there; 0 where it's none
double blade_heel( TTrack const &Track, int const Path, int const Side );
// where along the path of the track its rails lie on timbers shared with other tracks: where another track runs so near that
// the plain sleepers of the two would touch, of the lengths of the models they'd be made of. this goes by the shape of the
// tracks alone, so it holds whether the beams are drawn or not. the stretches in the order along the path, none where there