Add startPoint and endPoint support to TSP solver and Python wrapper; add tests
- Enhanced the C++ TSP solver to accept optional start and end points, so the route can begin and/or end at the closest point to specified coordinates. - Updated the Python pybind11 wrapper and PathUtils.sort_locations_tsp to support startPoint and endPoint as named parameters. - Added a new Python test suite (TestTSPSolver.py) to verify correct handling of start/end points and integration with PathUtils. - Registered the new test in TestCAMApp.py and CMakeLists.txt for automatic test discovery.
This commit is contained in:
@@ -25,13 +25,72 @@
|
||||
|
||||
namespace py = pybind11;
|
||||
|
||||
std::vector<int> tspSolvePy(const std::vector<std::pair<double, double>>& points)
|
||||
std::vector<int> tspSolvePy(const std::vector<std::pair<double, double>>& points,
|
||||
const py::object& startPoint = py::none(),
|
||||
const py::object& endPoint = py::none())
|
||||
{
|
||||
std::vector<TSPPoint> pts;
|
||||
for (const auto& p : points) {
|
||||
pts.emplace_back(p.first, p.second);
|
||||
}
|
||||
return TSPSolver::solve(pts);
|
||||
|
||||
// Handle optional start point
|
||||
TSPPoint* pStartPoint = nullptr;
|
||||
TSPPoint startPointObj(0, 0);
|
||||
if (!startPoint.is_none()) {
|
||||
try {
|
||||
// Use py::cast to convert to standard C++ types
|
||||
auto sp = startPoint.cast<std::vector<double>>();
|
||||
if (sp.size() >= 2) {
|
||||
startPointObj.x = sp[0];
|
||||
startPointObj.y = sp[1];
|
||||
pStartPoint = &startPointObj;
|
||||
}
|
||||
}
|
||||
catch (py::cast_error&) {
|
||||
// If casting fails, try accessing elements directly
|
||||
try {
|
||||
if (py::len(startPoint) >= 2) {
|
||||
startPointObj.x = py::cast<double>(startPoint.attr("__getitem__")(0));
|
||||
startPointObj.y = py::cast<double>(startPoint.attr("__getitem__")(1));
|
||||
pStartPoint = &startPointObj;
|
||||
}
|
||||
}
|
||||
catch (py::error_already_set&) {
|
||||
// Ignore if we can't access the elements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Handle optional end point
|
||||
TSPPoint* pEndPoint = nullptr;
|
||||
TSPPoint endPointObj(0, 0);
|
||||
if (!endPoint.is_none()) {
|
||||
try {
|
||||
// Use py::cast to convert to standard C++ types
|
||||
auto ep = endPoint.cast<std::vector<double>>();
|
||||
if (ep.size() >= 2) {
|
||||
endPointObj.x = ep[0];
|
||||
endPointObj.y = ep[1];
|
||||
pEndPoint = &endPointObj;
|
||||
}
|
||||
}
|
||||
catch (py::cast_error&) {
|
||||
// If casting fails, try accessing elements directly
|
||||
try {
|
||||
if (py::len(endPoint) >= 2) {
|
||||
endPointObj.x = py::cast<double>(endPoint.attr("__getitem__")(0));
|
||||
endPointObj.y = py::cast<double>(endPoint.attr("__getitem__")(1));
|
||||
pEndPoint = &endPointObj;
|
||||
}
|
||||
}
|
||||
catch (py::error_already_set&) {
|
||||
// Ignore if we can't access the elements
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return TSPSolver::solve(pts, pStartPoint, pEndPoint);
|
||||
}
|
||||
|
||||
PYBIND11_MODULE(tsp_solver, m)
|
||||
@@ -40,5 +99,12 @@ PYBIND11_MODULE(tsp_solver, m)
|
||||
m.def("solve",
|
||||
&tspSolvePy,
|
||||
py::arg("points"),
|
||||
"Solve TSP for a list of (x, y) points using 2-Opt, returns visit order");
|
||||
py::arg("startPoint") = py::none(),
|
||||
py::arg("endPoint") = py::none(),
|
||||
"Solve TSP for a list of (x, y) points using 2-Opt, returns visit order.\n"
|
||||
"Optional arguments:\n"
|
||||
"- startPoint: Optional [x, y] point where the path should start (closest point will be "
|
||||
"chosen)\n"
|
||||
"- endPoint: Optional [x, y] point where the path should end (closest point will be "
|
||||
"chosen)");
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user