Bend path close to Bezier curve

This commit is contained in:
Tomasz Zieliński
2025-11-25 21:34:27 +01:00
parent 045251bdc2
commit a52ff92344
4 changed files with 72 additions and 3 deletions
+18 -2
View File
@@ -24,9 +24,25 @@
VCMI_LIB_NAMESPACE_BEGIN
void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src, bool onlyStraight /* = true */)
void replaceWithCurvedPath(rmg::Path & path, Zone & zone, const int3 & src, bool onlyStraight /* = true */)
{
auto costFunction = rmg::Path::createCurvedCostFunction(zone.area()->getBorder());
// Get random control points from within the zone
auto & rng = zone.getRand();
const auto & tiles = zone.area()->getTilesVector();
if(tiles.size() < 2)
{
logGlobal->warn("Zone too small for curved path");
return;
}
// Pick two random control points (Q1, Q2) from zone tiles
int3 control1 = tiles[rng.nextInt(0, static_cast<int>(tiles.size()) - 1)];
int3 control2 = tiles[rng.nextInt(0, static_cast<int>(tiles.size()) - 1)];
// Create Bezier cost function: Q0=start, Q1=control1, Q2=control2, Q3=src
auto costFunction = rmg::Path::createBezierCostFunction(src, control1, control2, zone.getPos());
auto pathArea = zone.areaForRoads();
rmg::Path curvedPath(pathArea);
curvedPath.connect(zone.freePaths().get());
+1 -1
View File
@@ -34,7 +34,7 @@ public:
}
};
void replaceWithCurvedPath(rmg::Path & path, const Zone & zone, const int3 & src, bool onlyStraight = true);
void replaceWithCurvedPath(rmg::Path & path, Zone & zone, const int3 & src, bool onlyStraight = true);
rmg::Tileset collectDistantTiles(const Zone & zone, int distance);
+51
View File
@@ -229,4 +229,55 @@ Path::MoveCostFunction Path::createCurvedCostFunction(const Area & border)
};
}
float Path::distanceToCubicBezier(const int3 & point, const int3 & p0, const int3 & p1, const int3 & p2, const int3 & p3)
{
// Approximate distance to cubic Bezier curve by sampling
// B(t) = (1-t)³P0 + 3(1-t)²tP1 + 3(1-t)t²P2 + t³P3
constexpr int SAMPLES = 20;
float minDistSq = std::numeric_limits<float>::max();
for(int i = 0; i <= SAMPLES; ++i)
{
float t = static_cast<float>(i) / SAMPLES;
float oneMinusT = 1.0f - t;
float oneMinusT2 = oneMinusT * oneMinusT;
float oneMinusT3 = oneMinusT2 * oneMinusT;
float t2 = t * t;
float t3 = t2 * t;
// Cubic Bezier formula
float bx = oneMinusT3 * p0.x + 3.0f * oneMinusT2 * t * p1.x + 3.0f * oneMinusT * t2 * p2.x + t3 * p3.x;
float by = oneMinusT3 * p0.y + 3.0f * oneMinusT2 * t * p1.y + 3.0f * oneMinusT * t2 * p2.y + t3 * p3.y;
float dx = point.x - bx;
float dy = point.y - by;
float distSq = dx * dx + dy * dy;
if(distSq < minDistSq)
minDistSq = distSq;
}
//return std::sqrt(minDistSq);
return minDistSq;
}
Path::MoveCostFunction Path::createBezierCostFunction(const int3 & p0, const int3 & p1, const int3 & p2, const int3 & p3)
{
// p0: start point (Q0)
// p1: first control point (Q1) - any tile in zone
// p2: second control point (Q2) - any tile in zone
// p3: end point (Q3)
return [p0, p1, p2, p3](const int3 & src, const int3 & dst) -> float
{
// Distance from destination tile to the Bezier curve
float distToCurve = distanceToCubicBezier(dst, p0, p1, p2, p3);
// Penalize tiles far from the curve
// Square for stronger effect
return distToCurve * distToCurve;
};
}
VCMI_LIB_NAMESPACE_END
+2
View File
@@ -45,7 +45,9 @@ public:
static Path invalid();
static MoveCostFunction createCurvedCostFunction(const Area & border);
static MoveCostFunction createBezierCostFunction(const int3 & p0, const int3 & p1, const int3 & p2, const int3 & p3);
static float nonEuclideanCostFunction(const int3& src, const int3& dst, const int3& center);
static float distanceToCubicBezier(const int3 & point, const int3 & p0, const int3 & p1, const int3 & p2, const int3 & p3);
private: