mirror of
https://github.com/MaSzyna-EU07/maszyna.git
synced 2026-11-01 09:12:08 +01:00
refactor(track): the sleeper layout split by concern instead of one TrackSleepers.cpp
- SleeperRules: the rules beams are laid by and the faults counted where the layout breaks them - SleeperGeometry: plan helpers, beam boxes with their overlap and support tests and a grid of cells, shared by laying, filling gaps, keeping beams apart and checking instead of three copies of each - SleeperPaths: sampled paths, how near they run and whether one carries another; the tracks near a track and the stretches of a path where a test holds, shared by paths_around/unsettle_around and shared_zones/zones_of - SleeperFeatures: frogs and crossings the timbers turn to; SleeperLines: lines of beams, the rails they carry, where they part - SleeperLayout: clusters and their layout, given the epoch instead of reading it; SleeperModels: the models of the beams - SleeperWorker: the layouts kept per epoch and worked out in the background, behind a small interface - TrackSleepers: the facade, laying the instances, the shared zones and the check - sleepercheck reports a digest of all beams; geomhash skips empty chunks instead of crashing The layout is unchanged: geomhash, sleepercheck with the digest, wings, space, guards, crossings and switches give the same output on baltyk, zz_rozjazdy_id4, zz_rf_slips, zz_rf_wide, zz_slips_s42, zz_demo_przechylka and zz_connections.
This commit is contained in:
148
world/SleeperGeometry.h
Normal file
148
world/SleeperGeometry.h
Normal file
@@ -0,0 +1,148 @@
|
||||
/*
|
||||
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 <algorithm>
|
||||
#include <cmath>
|
||||
#include <cstdint>
|
||||
#include <map>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
#include <glm/glm.hpp>
|
||||
|
||||
#include "world/SleeperRules.h"
|
||||
#include "world/TrackSleepers.h"
|
||||
|
||||
// geometry in plan, x and z of the world, the sleepers are laid out in
|
||||
namespace track_sleepers {
|
||||
|
||||
inline glm::dvec2 plan( glm::dvec3 const &Vector ) {
|
||||
return { Vector.x, Vector.z };
|
||||
}
|
||||
|
||||
inline double cross( glm::dvec2 const &Left, glm::dvec2 const &Right ) {
|
||||
return Left.x * Right.y - Left.y * Right.x;
|
||||
}
|
||||
|
||||
// the angle between two directions in plan, either way along them
|
||||
inline double angle_between( glm::dvec2 const &Left, glm::dvec2 const &Right ) {
|
||||
|
||||
return std::acos( std::min( 1.0, std::abs( glm::dot( Left, Right ) ) ) );
|
||||
}
|
||||
|
||||
// the direction turned by the angle, counterclockwise from x to z
|
||||
inline glm::dvec2 rotated( glm::dvec2 const &Direction, double const Angle ) {
|
||||
|
||||
return { Direction.x * std::cos( Angle ) - Direction.y * std::sin( Angle ), Direction.x * std::sin( Angle ) + Direction.y * std::cos( Angle ) };
|
||||
}
|
||||
|
||||
// the angle the direction is turned off the forward way, either way along it
|
||||
inline double turn_off( glm::dvec2 const &Forward, glm::dvec2 Direction ) {
|
||||
|
||||
if( glm::dot( Forward, Direction ) < 0.0 ) {
|
||||
Direction = -Direction;
|
||||
}
|
||||
return std::atan2( cross( Forward, Direction ), glm::dot( Forward, Direction ) );
|
||||
}
|
||||
|
||||
// a beam in plan, by its middle, the unit vector along it and half its length
|
||||
struct beam_box {
|
||||
glm::dvec2 centre { 0.0 };
|
||||
glm::dvec2 axis { 1.0, 0.0 };
|
||||
double half { 0.0 };
|
||||
};
|
||||
|
||||
inline beam_box box_of( beam const &Beam ) {
|
||||
|
||||
auto const span { plan( Beam.to - Beam.from ) };
|
||||
auto const length { glm::length( span ) };
|
||||
return { plan( 0.5 * ( Beam.from + Beam.to ) ), length > 1e-9 ? span / length : glm::dvec2 { 1.0, 0.0 }, 0.5 * length };
|
||||
}
|
||||
|
||||
// whether two beams keep clear of each other, with a little room left between them: an axis of either keeps them apart
|
||||
inline bool apart( beam_box const &Left, beam_box const &Right ) {
|
||||
|
||||
auto const offset { Right.centre - Left.centre };
|
||||
for( auto const &axis : { Left.axis, glm::dvec2 { -Left.axis.y, Left.axis.x }, Right.axis, glm::dvec2 { -Right.axis.y, Right.axis.x } } ) {
|
||||
auto const reach = [&]( beam_box const &Box ) {
|
||||
return Box.half * std::abs( glm::dot( Box.axis, axis ) ) + ( half_width - 0.01 ) * std::abs( cross( Box.axis, axis ) ); };
|
||||
if( std::abs( glm::dot( offset, axis ) ) > reach( Left ) + reach( Right ) ) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
// whether the point lies on the beam
|
||||
inline bool holds( beam_box const &Box, glm::dvec2 const &Point ) {
|
||||
|
||||
auto const offset { Point - Box.centre };
|
||||
return std::abs( glm::dot( offset, Box.axis ) ) <= Box.half && std::abs( cross( Box.axis, offset ) ) <= half_width;
|
||||
}
|
||||
|
||||
// values put in the square cells of a grid in plan that a square about a point reaches into, to find those near a place
|
||||
template <typename Value>
|
||||
class plan_grid {
|
||||
|
||||
public:
|
||||
using cell = std::pair<std::int64_t, std::int64_t>;
|
||||
|
||||
explicit plan_grid( double const Size ) :
|
||||
m_size( Size ) {}
|
||||
|
||||
// the value in every cell the square of the reach about the centre touches
|
||||
void insert( glm::dvec2 const &Centre, double const Reach, Value const &Inserted ) {
|
||||
|
||||
auto const low { key( Centre - glm::dvec2 { Reach } ) };
|
||||
auto const high { key( Centre + glm::dvec2 { Reach } ) };
|
||||
for( auto x { low.first }; x <= high.first; ++x ) {
|
||||
for( auto z { low.second }; z <= high.second; ++z ) {
|
||||
m_cells[ { x, z } ].push_back( Inserted );
|
||||
}
|
||||
}
|
||||
}
|
||||
// the values in the cell of the point, null where there are none
|
||||
std::vector<Value> const *at( glm::dvec2 const &Point ) const {
|
||||
|
||||
auto const found { m_cells.find( key( Point ) ) };
|
||||
return ( found != m_cells.end() ? &found->second : nullptr );
|
||||
}
|
||||
// whether the test holds for every value in the cells the square of the reach about the centre touches
|
||||
template <typename Test>
|
||||
bool all_about( glm::dvec2 const &Centre, double const Reach, Test const &Holds ) const {
|
||||
|
||||
auto const low { key( Centre - glm::dvec2 { Reach } ) };
|
||||
auto const high { key( Centre + glm::dvec2 { Reach } ) };
|
||||
for( auto x { low.first }; x <= high.first; ++x ) {
|
||||
for( auto z { low.second }; z <= high.second; ++z ) {
|
||||
auto const found { m_cells.find( { x, z } ) };
|
||||
if( found != m_cells.end() && false == std::all_of( found->second.begin(), found->second.end(), Holds ) ) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
std::map<cell, std::vector<Value>> const &cells() const {
|
||||
return m_cells;
|
||||
}
|
||||
|
||||
private:
|
||||
cell key( glm::dvec2 const &Point ) const {
|
||||
return { static_cast<std::int64_t>( std::floor( Point.x / m_size ) ), static_cast<std::int64_t>( std::floor( Point.y / m_size ) ) };
|
||||
}
|
||||
|
||||
double m_size;
|
||||
std::map<cell, std::vector<Value>> m_cells;
|
||||
};
|
||||
|
||||
|
||||
} // namespace track_sleepers
|
||||
Reference in New Issue
Block a user