Skip to content

Commit

Permalink
Merge pull request #1401 from DarcJC/dev/darc/roads
Browse files Browse the repository at this point in the history
[WIP] Roads generator
  • Loading branch information
littlemine authored Sep 12, 2023
2 parents 9e7e232 + 5ba9eb2 commit db6f168
Show file tree
Hide file tree
Showing 17 changed files with 13,029 additions and 31 deletions.
11 changes: 11 additions & 0 deletions projects/Roads/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@

# Required Libraries

1. `tinyspline`
2. `boost`
3. `Eigen`

**For Windows**
```powershell
./vcpkg install boost:x64-windows Eigen:x64-windows tinyspline:x64-windows
```
626 changes: 606 additions & 20 deletions projects/Roads/nodes/src/cost.cpp

Large diffs are not rendered by default.

38 changes: 37 additions & 1 deletion projects/Roads/utilities/CMakeLists.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
SET(Boost_ADDITIONAL_VERSIONS "1.82.0" "1.82")

FIND_PACKAGE(Eigen3 REQUIRED NO_MODULE)
FIND_PACKAGE(OpenMP REQUIRED)
FIND_PACKAGE(Boost COMPONENTS graph)
#find_package(TBB CONFIG REQUIRED COMPONENTS tbb tbbmalloc)

FILE(GLOB_RECURSE SOURCE_FILES src/*.cpp src/*.h)
FILE(GLOB_RECURSE SOURCE_FILES src/*.cpp src/*.h src/thirdparty/parson.c src/thirdparty/tinyspline.c src/thirdparty/tinysplinecxx.cxx)

ADD_LIBRARY(Roads STATIC ${SOURCE_FILES})

Expand All @@ -9,3 +14,34 @@ TARGET_LINK_LIBRARIES(Roads PRIVATE Eigen3::Eigen)

ADD_EXECUTABLE(RoadsTest test/main.cpp)
TARGET_LINK_LIBRARIES(RoadsTest PUBLIC Roads)

IF (OPENMP_FOUND)
SET(CMAKE_C_FLAGS "${CMAKE_C_FLAGS} ${OpenMP_C_FLAGS}")
SET(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${OpenMP_CXX_FLAGS}")
SET(CMAKE_EXE_LINKER_FLAGS "${CMAKE_EXE_LINKER_FLAGS}" ${OpenMP_EXE_LINKER_FLAGS})
TARGET_LINK_LIBRARIES(Roads PRIVATE OpenMP::OpenMP_CXX)
if (MSVC)
include(CheckCXXCompilerFlag)
check_cxx_compiler_flag("/openmp:experimental" MSVC_SUPPORT_OPENMP_EXPERIMENTAL)
if (MSVC_SUPPORT_OPENMP_EXPERIMENTAL)
target_compile_options(zeno PRIVATE $<IF:$<COMPILE_LANGUAGE:CXX>,/openmp:experimental,>)
endif()
check_cxx_compiler_flag("/openmp:llvm" MSVC_SUPPORT_OPENMP_LLVM)
if (MSVC_SUPPORT_OPENMP_LLVM)
target_compile_options(zeno PRIVATE $<IF:$<COMPILE_LANGUAGE:CXX>,/openmp:llvm,>)
endif()
endif()
ELSE()
MESSAGE(FATAL_ERROR "Roads Module is depending on OpenMP. You have to install it first.")
ENDIF()

IF (TARGET Boost::graph)
TARGET_LINK_LIBRARIES(Roads PRIVATE Boost::graph)
ELSE()
MESSAGE(FATAL_ERROR "Roads Module is depending on Boost::graph. You have to install it first.")
ENDIF()

#TARGET_LINK_LIBRARIES(Roads PRIVATE TBB::tbb TBB::tbbmalloc)

ADD_COMPILE_DEFINITIONS(TINYSPLINE_EXPORT=1)
ADD_COMPILE_DEFINITIONS(TINYSPLINE_FLOAT_PRECISION=1)
62 changes: 56 additions & 6 deletions projects/Roads/utilities/include/roads/data.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
#pragma once

#include "Eigen/Eigen"
#include "boost/math/constants/constants.hpp"
#include <array>
#include <cassert>
#include <cmath>
#include <vector>
#include <functional>
#include <unordered_map>

namespace roads {

Expand All @@ -15,11 +17,28 @@ namespace roads {
OBTUSE = 2,
};

enum class ConnectiveType {
FOUR = 4,
EIGHT = 8,
SIXTEEN = 16,
THIRDTY_TWO = 32,
FOURTY = 40,
};

struct Point : public Eigen::Vector3d {
Point(const std::array<float, 3>& InArray) : Eigen::Vector3d(InArray[0], InArray[1], InArray[2]) {}
Point() = default;

using Eigen::Vector3d::Vector3d;
};

struct Point2D : public Eigen::Vector2d {
using Eigen::Vector2d::Vector2d;
};

struct IntPoint2D : public std::array<long, 2> {};

struct IntPoint : public std::array<long, 3> {};

struct Triangle : public std::array<Point, 3> {
bool AnyAngleLargerThan(const double Degree) {
assert(Degree <= 180.0f);
Expand All @@ -34,20 +53,51 @@ namespace roads {
};

template <typename T>
struct ArrayList : public std::vector<T> {};
struct ArrayList : public std::vector<T> {
using std::vector<T>::vector;
using std::vector<T>::size;
using std::vector<T>::begin;
using std::vector<T>::end;

bool IsValidIndex(size_t Index) const {
return Index < size();
}
};

template <size_t X, size_t Y, typename GridPointType = Point>
struct Grid : public std::array<GridPointType, X * Y> {};

template <typename GridPointType = Point>
struct DynamicGrid : public std::vector<GridPointType> {
template<typename GridPointType>
struct CustomGridBase : public ArrayList<GridPointType> {
size_t Nx, Ny;

DynamicGrid(const size_t InNx, const size_t InNy) : std::vector<GridPointType>(InNx * InNy), Nx(InNx), Ny(InNy) {}
DynamicGrid(DynamicGrid&& OtherGridToMove) noexcept : std::vector<GridPointType>(std::forward<std::vector<GridPointType>>(OtherGridToMove)) {
CustomGridBase(const size_t InNx, const size_t InNy) : ArrayList<GridPointType>(InNx * InNy), Nx(InNx), Ny(InNy) { }
CustomGridBase(CustomGridBase&& OtherGridToMove) noexcept : ArrayList<GridPointType>(std::forward<ArrayList<GridPointType>>(OtherGridToMove)) {
Nx = OtherGridToMove.Nx;
Ny = OtherGridToMove.Ny;
}
};

template <typename GridPointType = Point>
struct DynamicGrid : public CustomGridBase<GridPointType> {
using CustomGridBase<GridPointType>::CustomGridBase;
};

constexpr double PI = boost::math::constants::pi<double>();
constexpr double PI2 = boost::math::constants::pi<double>() * 2.0;

template <typename KeyType, typename ValueType>
struct DefaultedHashMap : public std::unordered_map<KeyType, ValueType> {
using std::unordered_map<KeyType, ValueType>::unordered_map;

ValueType& DefaultAt(const KeyType& InKey, const ValueType& DefaultValue = ValueType()) {
const auto Target = this->_Find_last(InKey, this->_Traitsobj(InKey));
if (Target._Duplicate) {
return Target._Duplicate->_Myval.second;
}

return const_cast<ValueType&>(DefaultValue);
}
};

}// namespace roads
233 changes: 233 additions & 0 deletions projects/Roads/utilities/include/roads/grid.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,233 @@
#pragma once

#include "pch.h"
#include <functional>
#include <numeric>
#include <queue>
#include <unordered_map>

//#include "boost/graph/use_mpi.hpp"
#include "boost/graph/adjacency_list.hpp"
#include "boost/graph/graph_concepts.hpp"
#include "boost/graph/graph_traits.hpp"

namespace tinyspline {
class BSpline;
}

namespace roads {

struct AdvancePoint {
Point Position;
double Gradient = 0.0;
};

using HeightPoint = double;
using SlopePoint = double;
struct CostPoint : std::array<size_t, 3> {
CostPoint() = default;

CostPoint(size_t x, size_t y, size_t a = 0, double InHeight = 0.0, double InGradient = 0.0 /*, double InCurvature = 0.0**/)
: std::array<size_t, 3>(), Height(InHeight), Gradient(InGradient) {
at(0) = x;
at(1) = y;
at(2) = a;
}

bool operator==(const CostPoint &Rhs) {
return at(0) == Rhs.at(0) && at(1) == Rhs.at(1) && at(2) == Rhs.at(2);
}

double Height;
double Gradient;// slope

// We need directional curvature, we cannot pre calculate it
//double Curvature;
};

using EdgeWeightProperty = boost::property<boost::edge_weight_t, double>;
using WeightedGridUndirectedGraph = boost::adjacency_list<boost::listS, boost::vecS, boost::directedS, boost::no_property, EdgeWeightProperty>;
using Edge = std::pair<size_t, size_t>;

ROADS_API ROADS_INLINE double EuclideanDistance(const Point &Point1, const Point &Point2);

ROADS_API ROADS_INLINE double EuclideanDistance(const Point2D &Point1, const Point2D &Point2);

ROADS_API DynamicGrid<SlopePoint> CalculateSlope(const DynamicGrid<HeightPoint> &InHeightField);

namespace energy {
template<typename IntLikeT>
inline IntLikeT GreatestCommonDivisor(IntLikeT i, IntLikeT j) {
if (0 == j) {
return i;
}
return GreatestCommonDivisor(j, i % j);
}

template<typename PointType = IntPoint2D>
void RoadsShortestPath(const PointType &StartPoint, const PointType &GoalPoint, const PointType &Bounds, const int32_t MaskK, const int32_t MaskA, const float WeightHeuristic, DefaultedHashMap<PointType, PointType> &PredecessorList, DefaultedHashMap<PointType, float> &CostTo, const std::function<float(const PointType &, const PointType &)> &CostFunction) {
auto NewCostFunc = [&CostFunction, &GoalPoint, &Bounds](const PointType &a, const PointType &b) -> float {
float Result = CostFunction(a, b);
if (Result < 0) {
printf("[Roads] Minus cost P1(%d,%d) P2(%d,%d) Cost=%f.", a[0], a[1], b[0], b[1], Result);
throw std::runtime_error("[Roads] Minus edge weight detected.");
}
return Result;
//return CostFunction(a, b) + std::abs<float>(GoalPoint[0] - b[0]) + std::abs<float>(GoalPoint[1] - b[1]);
};

std::unordered_map<std::pair<PointType, PointType>, float> SimpleCost;
std::function<float(const PointType &, const PointType &, bool)> StraightCost = [&NewCostFunc, MaskK, &SimpleCost, &StraightCost](const PointType &From, const PointType &To, bool bMoveX = true) mutable -> float {
auto CurrentPair = std::make_pair(From, To);
if (SimpleCost.count(CurrentPair)) {
return SimpleCost[CurrentPair];
}

if (From[0] == To[0] && From[1] == To[1]) {
SimpleCost[CurrentPair] = 0.0f;
return 0.0f;
}

PointType NextPoint = From;

if (To[0] != From[0]) {
if (std::abs<int32_t>(From[0] - To[0]) <= MaskK) {
NextPoint[0] = To[0];
} else if (From[0] < To[0]) {
NextPoint[0] += MaskK;
} else {
NextPoint[0] -= MaskK;
}
}
if (To[1] != From[1]) {
if (std::abs<int32_t>(From[1] - To[1]) <= MaskK) {
NextPoint[1] = To[1];
} else if (From[1] < To[1]) {
NextPoint[1] += MaskK;
} else {
NextPoint[1] -= MaskK;
}
}

//printf("---- A(%d,%d) B(%d,%d)\n", NextPoint[0], NextPoint[0], To[1], To[1]);

float Cost = NewCostFunc(From, NextPoint) + StraightCost(NextPoint, To, !bMoveX);
SimpleCost[CurrentPair] = Cost;
return Cost;
};

// Adding Chebyshev distance as heuristic function
auto Comparator = [&CostTo, &GoalPoint, &Bounds, &StraightCost, WeightHeuristic](const PointType &Point1, const PointType &Point2) {
//float DeltaA = std::max(std::abs<int32_t>(Point1[0] - GoalPoint[0]) / Bounds[0], std::abs<int32_t>(Point1[1] - GoalPoint[1]) / Bounds[1]);
//float DeltaB = std::max(std::abs<int32_t>(Point2[0] - GoalPoint[0]) / Bounds[0], std::abs<int32_t>(Point2[1] - GoalPoint[1]) / Bounds[1]);
//float DeltaA = std::abs<int32_t>(Point1[0] - GoalPoint[0]) + std::abs<int32_t>(Point1[1] - GoalPoint[1]);
//float DeltaB = std::abs<int32_t>(Point2[0] - GoalPoint[0]) + std::abs<int32_t>(Point2[1] - GoalPoint[1]);
//float DeltaA = std::sqrt(std::pow(Point1[0] - GoalPoint[0], 2) + std::pow(Point1[1] - GoalPoint[1], 2));
//float DeltaB = std::sqrt(std::pow(Point2[0] - GoalPoint[0], 2) + std::pow(Point2[1] - GoalPoint[1], 2));

float DeltaA = StraightCost(Point1, GoalPoint, true);
float DeltaB = StraightCost(Point2, GoalPoint, true);

float CostA = CostTo.DefaultAt(Point1, std::numeric_limits<float>::max()) + WeightHeuristic * DeltaA;
float CostB = CostTo.DefaultAt(Point2, std::numeric_limits<float>::max()) + WeightHeuristic * DeltaB;
return CostA > CostB;
};

// initialize a priority queue Q with the initial point StartPoint
CostTo.reserve(Bounds[0] * Bounds[1] * MaskA);
std::priority_queue<PointType, ArrayList<PointType>, decltype(Comparator)> Q(Comparator);
for (size_t a = 0; a < MaskA; a++) {
PointType NewPoint = StartPoint;
NewPoint[2] = a;
CostTo[StartPoint] = 0.f;
}

PredecessorList[StartPoint] = StartPoint;
Q.push(StartPoint);

std::unordered_map<PointType, bool> Visual;

// while Q is not empty
while (!Q.empty()) {
// select the point p_ij from the priority queue with the smallest cost value c(p_ij)
PointType Point = Q.top();
Q.pop();

// if destination has been found p_ij = b, stop the algorithm
if (GoalPoint == Point) {
break;
}

if (Visual.find(Point) != std::end(Visual)) {
continue;
}
Visual[Point] = 1;

//printf("-- P(%d,%d) %f\n", Point[0], Point[1], CostTo[Point]);

// extended mask for angle
for (size_t angle = 0; angle < MaskA; ++angle) {
// step3. for all points q ∈ M_k(p_ij)
for (int32_t dx = -MaskK; dx <= MaskK; ++dx) {
for (int32_t dy = -MaskK; dy <= MaskK; ++dy) {
if (GreatestCommonDivisor(std::abs(dx), std::abs(dy)) == 1) {
PointType NeighbourPoint{Point[0] + dx, Point[1] + dy, angle};
if (NeighbourPoint[0] < 0 || NeighbourPoint[1] < 0 || NeighbourPoint[0] >= Bounds[0] || NeighbourPoint[1] >= Bounds[1]) continue;

float NewCost = CostTo[Point] + NewCostFunc(Point, NeighbourPoint);
if (NewCost < 0) {
throw std::runtime_error("[Roads] Graph should not have negative weight. Check your curve !");
}
//printf("---- N(%d,%d) %f\n", NeighbourPoint[0], NeighbourPoint[1], NewCost);

if (NewCost < CostTo.DefaultAt(NeighbourPoint, std::numeric_limits<float>::max())) {
PredecessorList[NeighbourPoint] = Point;
CostTo[NeighbourPoint] = NewCost;
Q.push(NeighbourPoint);
}
}
}
}
}
}
}
}// namespace energy

namespace spline {
//template<typename PointContainerType = ArrayList<std::array<float, 3>>, typename SegmentContainerType = std::array<int, 2>>
class tinyspline::BSpline GenerateBSplineFromSegment(const ArrayList<std::array<float, 3>> &InPoints, const ArrayList<std::array<int, 2>> &Segments);

//template<typename PointContainerType = ArrayList<std::array<float, 3>>, typename SegmentContainerType = ArrayList<std::array<int, 2>>>
ArrayList<Eigen::Vector3f> GenerateAndSamplePointsFromSegments(const class tinyspline::BSpline& Spline, int32_t SamplePoints = 1000);

float Distance(const Eigen::Vector3d &point, const tinyspline::BSpline &bSpline, float t);

float FindNearestPoint(const Eigen::Vector3d &point, const tinyspline::BSpline &bSpline, float& t, float step = 0.01, float tolerance = 1e-6);

float FindNearestPointSA(const Eigen::Vector3d &Point, const tinyspline::BSpline &Spline);

ArrayList<float> CalcRoadMask(const std::vector<std::array<float, 3>>& Points, const tinyspline::BSpline& SplineQwQ, float MaxDistance);
}// namespace spline

}// namespace roads


namespace std {
template<>
struct hash<roads::CostPoint> {
size_t operator()(const roads::CostPoint &rhs) const {
constexpr size_t Seed = 10086;
size_t Result = (rhs[0] + 0x9e3779b9 + (Seed << 4) + (Seed >> 2)) ^ (rhs[1] + 0x9e3779b9 + (Seed << 6) + (Seed >> 4)) ^ (rhs[2] + 0x9e3779b9 + (Seed << 8) + (Seed >> 6));
return Result;
}
};

template<>
struct hash<std::pair<roads::CostPoint, roads::CostPoint>> {
size_t operator()(const std::pair<roads::CostPoint, roads::CostPoint> &rhs) const {
constexpr size_t Seed = 10086;
auto h = hash<roads::CostPoint>();
return (h(rhs.first) + (Seed << 4) + (Seed >> 2)) ^ (h(rhs.second) + (Seed << 6) + (Seed >> 4));
}
};
}// namespace std
Loading

0 comments on commit db6f168

Please sign in to comment.