diff --git a/world/TrackRails.cpp b/world/TrackRails.cpp index da2486fc..59e27db7 100644 --- a/world/TrackRails.cpp +++ b/world/TrackRails.cpp @@ -954,15 +954,11 @@ std::vector wing_extension( TTrack &Track, TTrack::check_rail_set co } // acute frog: the rail coming from the throat bends out as a wing rail, the rail going on behind the gap is a leg of the nose -void acute( TTrack::rail_plan &Plan, double const At, double const Toward, double const Slope, std::vector const &Extension, TTrack::rail_size const &Size ) { +void acute( TTrack::rail_plan &Plan, double const At, double const Toward, double const Slope, std::vector const &Extension, std::vector const &Legs ) { - auto const half { Slope / ( 1.0 + std::sqrt( 1.0 + Slope * Slope ) ) }; - auto const tip { 0.5 * nose_tip }; - auto const apart { Size.high + 0.01 }; auto const throat { At + Toward * flangeway / Slope }; auto const flare { At - Toward * wing_reach( Slope ) }; - auto const nose { At - Toward * tip / half }; - auto const blunt { At - Toward * apart / half }; + auto const nose { Toward < 0.0 ? Legs.front().x : Legs.back().x }; std::vector wing { { throat, 0.0 }, { flare, std::abs( flare - throat ) * Slope } }; wing.insert( wing.end(), Extension.begin(), Extension.end() ); auto const wingend { Extension.empty() ? flare : Extension.back().x }; @@ -978,11 +974,11 @@ void acute( TTrack::rail_plan &Plan, double const At, double const Toward, doubl cut.endoffsets = wing; cut.endrolled = rolled; cut.start = nose; - cut.startextents = { { nose, tip }, { blunt, apart } }; + cut.startextents = Legs; } else { cut.end = nose; - cut.endextents = { { blunt, apart }, { nose, tip } }; + cut.endextents = Legs; cut.start = wingend; cut.startoffsets = wing; cut.startrolled = rolled; @@ -1090,44 +1086,53 @@ struct frame_corner { double facing { 0.0 }; }; +double lateral( gauge_line const &Line, int const Side, glm::dvec3 const &Point, double &Distance ) { + + glm::dvec3 point, tangent; + for( int step = 0; step < 8; ++step ) { + Line.at( Distance, point, tangent ); + Distance += glm::dot( plan( Point - point ), glm::normalize( plan( tangent ) ) ); + } + Line.at( Distance, point, tangent ); + return -glm::dot( plan( Point - point ), plan( Line.normal( Distance, Side ) ) ); +} + +double nearest_distance( gauge_line const &Line, glm::dvec3 const &Point ) { + + auto result { Line.distances.front() }; + auto nearest { std::numeric_limits::max() }; + for( std::size_t i = 0; i < Line.points.size(); ++i ) { + auto const gap { glm::length( plan( Line.points[ i ] - Point ) ) }; + if( gap < nearest ) { + nearest = gap; + result = Line.distances[ i ]; + } + } + return result; +} + std::vector tread_edge( TTrack const &Track, int const Path, int const Side, TTrack const &Other, int const Otherpath, int const Otherside, double const From, double const To, bool &Below, std::vector &Gauge ) { auto const &ours { rail_line( Track, Path, Side > 0, false ) }; auto const &theirs { rail_line( Other, Otherpath, Otherside > 0, false ) }; - auto const project = [&]( gauge_line const &Line, int const Lineside, glm::dvec3 const &Point, double &Distance ) { - glm::dvec3 point, tangent; - for( int step = 0; step < 8; ++step ) { - Line.at( Distance, point, tangent ); - Distance += glm::dot( plan( Point - point ), glm::normalize( plan( tangent ) ) ); - } - Line.at( Distance, point, tangent ); - return -glm::dot( plan( Point - point ), plan( Line.normal( Distance, Lineside ) ) ); }; glm::dvec3 point, tangent; ours.at( From, point, tangent ); - auto otherat { theirs.distances.front() }; - auto nearest { std::numeric_limits::max() }; - for( std::size_t i = 0; i < theirs.points.size(); ++i ) { - auto const gap { glm::length( plan( theirs.points[ i ] - point ) ) }; - if( gap < nearest ) { - nearest = gap; - otherat = theirs.distances[ i ]; - } - } + auto otherat { nearest_distance( theirs, point ) }; std::vector knots; auto below { 0 }; auto const count { std::max( 2, static_cast( std::ceil( ( To - From ) / 0.1 ) ) + 1 ) }; for( int i = 0; i < count; ++i ) { auto distance { glm::mix( From, To, static_cast( i ) / ( count - 1 ) ) }; ours.at( distance, point, tangent ); - project( theirs, Otherside, point, otherat ); + lateral( theirs, Otherside, point, otherat ); glm::dvec3 line; theirs.at( otherat, line, tangent ); auto const normal { theirs.normal( otherat, Otherside ) }; auto at { distance }; - auto const gauge { project( ours, Side, line, at ) }; + auto const gauge { lateral( ours, Side, line, at ) }; Gauge.emplace_back( at, gauge ); at = distance; - auto const edge { project( ours, Side, line - normal * rolled_band, at ) }; + auto const edge { lateral( ours, Side, line - normal * rolled_band, at ) }; below += ( gauge < edge ? 1 : -1 ); knots.emplace_back( at, edge ); } @@ -1138,6 +1143,44 @@ std::vector tread_edge( TTrack const &Track, int const Path, int con return knots; } +std::vector nose_extents( TTrack const &Track, int const Path, int const Side, TTrack const &Other, int const Otherpath, int const Otherside, double const At, double const Toward, double const Slope, TTrack::rail_size const &Size ) { + + auto const half { Slope / ( 1.0 + std::sqrt( 1.0 + Slope * Slope ) ) }; + auto const tip { 0.5 * nose_tip }; + auto const apart { Size.high + 0.01 }; + auto const nose { At - Toward * tip / half }; + auto const blunt { At - Toward * apart / half }; + std::vector straight { { std::min( nose, blunt ), Toward < 0.0 ? tip : apart }, { std::max( nose, blunt ), Toward < 0.0 ? apart : tip } }; + auto const &ours { rail_line( Track, Path, Side > 0, false ) }; + auto const &theirs { rail_line( Other, Otherpath, Otherside > 0, false ) }; + glm::dvec3 point, tangent; + ours.at( nose, point, tangent ); + auto otherat { nearest_distance( theirs, point ) }; + auto extent { tip }; + auto const step { 0.1 }; + auto const limit { static_cast( std::ceil( 3.0 * std::abs( blunt - nose ) / step ) ) }; + std::vector knots; + for( int i = 0; i <= limit; ++i ) { + auto const distance { nose - Toward * step * i }; + ours.at( distance, point, tangent ); + auto const normal { ours.normal( distance, Side ) }; + for( int iteration = 0; iteration < 6; ++iteration ) { + extent += 0.5 * ( lateral( theirs, Otherside, point - normal * extent, otherat ) - extent ); + } + if( extent >= apart && false == knots.empty() ) { + auto const &last { knots.back() }; + knots.emplace_back( glm::mix( last.x, distance, ( apart - last.y ) / ( extent - last.y ) ), apart ); + break; + } + knots.emplace_back( distance, std::max( extent, 0.0 ) ); + } + if( knots.size() < 2 || knots.back().y < apart ) { + return straight; + } + std::sort( knots.begin(), knots.end(), []( glm::dvec2 const &A, glm::dvec2 const &B ) { return A.x < B.x; } ); + return knots; +} + } // namespace material_handle TTrack::rail_material( int const Path ) const { @@ -1293,7 +1336,8 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) { auto const slope { std::tan( crossing.angle ) }; wingends[ crossing.path ][ crossing.side > 0 ].emplace_back( crossing.at - toward * wing_reach( slope ), -toward ); acute( plan, crossing.at, toward, slope, - wing_extension( *this, checks, crossing.path, crossing.side, crossing.at, toward, slope, *crossing.track, crossing.otherpath, crossing.otherside, crossing.otherat, crossing.aligned ? -toward : toward ), size ); + wing_extension( *this, checks, crossing.path, crossing.side, crossing.at, toward, slope, *crossing.track, crossing.otherpath, crossing.otherside, crossing.otherat, crossing.aligned ? -toward : toward ), + nose_extents( *this, crossing.path, crossing.side, *crossing.track, crossing.otherpath, crossing.otherside, crossing.at, toward, slope, size ) ); auto const nose { toward < 0.0 ? plan.cuts.back().start : plan.cuts.back().end }; auto const shared { crossing.at - toward * ( rolled_band + size.head + tread_fade ) / slope }; tread( crossing.path, crossing.side, { std::min( nose, shared ), std::max( nose, shared ) }, *crossing.track, crossing.otherpath, toward > 0.0 ? 1 : -1, crossing.otherside ); @@ -1419,7 +1463,8 @@ TTrack::rail_layout TTrack::plan_rails( bool const Crossings ) { } wingends[ path ][ frog.side[ path ] > 0 ].emplace_back( frog.at[ path ] + wing_reach( slope ), 1.0 ); acute( plans[ path ][ frog.side[ path ] > 0 ], frog.at[ path ], -1.0, slope, - wing_extension( *this, checks, path, frog.side[ path ], frog.at[ path ], -1.0, slope, *this, 1 - path, frog.side[ 1 - path ], frog.at[ 1 - path ], 1.0 ), size ); + wing_extension( *this, checks, path, frog.side[ path ], frog.at[ path ], -1.0, slope, *this, 1 - path, frog.side[ 1 - path ], frog.at[ 1 - path ], 1.0 ), + nose_extents( *this, path, frog.side[ path ], *this, 1 - path, frog.side[ 1 - path ], frog.at[ path ], -1.0, slope, size ) ); auto const nose { plans[ path ][ frog.side[ path ] > 0 ].cuts.back().start }; auto const shared { frog.at[ path ] + ( rolled_band + size.head + tread_fade ) / slope }; tread( path, frog.side[ path ], { std::min( nose, shared ), std::max( nose, shared ) }, *this, 1 - path, -1, frog.side[ 1 - path ] );