@@ -462,6 +462,19 @@ struct DiGraph
462462 {
463463 return indexer_.get_id (node);
464464 }
465+ std::optional<std::tuple<int64_t , double >>
466+ __node_length (const std::string &node) const
467+ {
468+ auto nid = __node_id (node);
469+ if (!nid) {
470+ return {};
471+ }
472+ auto len = lengths_.find (*nid);
473+ if (len == lengths_.end ()) {
474+ return {};
475+ }
476+ return std::make_tuple (*nid, len->second );
477+ }
465478 std::string __node_id (int64_t node) const { return indexer_.id (node); }
466479 std::vector<std::string> __node_ids (const std::vector<int64_t > &nodes) const
467480 {
@@ -1957,6 +1970,64 @@ PYBIND11_MODULE(_core, m)
19571970 ;
19581971
19591972 py::class_<Path>(m, " Path" , py::module_local (), py::dynamic_attr ()) //
1973+ .def_static (
1974+ " Build" ,
1975+ [](const DiGraph &graph, const std::vector<std::string> &nodes,
1976+ std::optional<double > start_offset = {},
1977+ std::optional<double > end_offset = {},
1978+ std::optional<std::tuple<std::string, Binding>> binding = {})
1979+ -> Path {
1980+ if (nodes.empty ()) {
1981+ throw std::invalid_argument (" not any nodes" );
1982+ }
1983+ std::vector<int64_t > nids;
1984+ std::vector<double > lengths;
1985+ const auto N = nodes.size ();
1986+ nids.reserve (N);
1987+ lengths.reserve (N);
1988+ for (auto &node : nodes) {
1989+ auto nid_len = graph.__node_length (node);
1990+ if (!nid_len) {
1991+ throw std::invalid_argument (
1992+ fmt::format (" missing node {}" , node));
1993+ }
1994+ nids.push_back (std::get<0 >(*nid_len));
1995+ lengths.push_back (std::get<1 >(*nid_len));
1996+ }
1997+ double dist = 0.0 ;
1998+ for (size_t i = 1 ; i < N - 1 ; ++i) {
1999+ dist += lengths[i];
2000+ }
2001+ if (start_offset) {
2002+ start_offset = CLIP (0.0 , *start_offset, lengths.front ());
2003+ dist += lengths.front () - *start_offset;
2004+ }
2005+ if (end_offset) {
2006+ end_offset = CLIP (0.0 , *end_offset, lengths.back ());
2007+ dist += *end_offset;
2008+ }
2009+ auto p = Path (&graph, dist, nids, start_offset, end_offset);
2010+ auto round_scale = graph.round_scale ();
2011+ if (round_scale) {
2012+ p.round (*round_scale);
2013+ }
2014+ if (binding) {
2015+ auto node = std::get<0 >(*binding);
2016+ auto nid = graph.__node_id (node);
2017+ if (!nid) {
2018+ throw std::invalid_argument (
2019+ fmt::format (" invalid binding node {}" , node));
2020+ }
2021+ p.binding = std::make_tuple (*nid, std::get<1 >(*binding));
2022+ }
2023+ return p;
2024+ },
2025+ " graph" _a, " nodes" _a, //
2026+ py::kw_only (), //
2027+ " start_offset" _a = std::nullopt , //
2028+ " end_offset" _a = std::nullopt , //
2029+ " binding" _a = std::nullopt )
2030+
19602031 .def_property_readonly (
19612032 " graph" , [](const Path &self) { return self.graph ; },
19622033 rvp::reference_internal)
@@ -1982,7 +2053,11 @@ PYBIND11_MODULE(_core, m)
19822053 })
19832054 .def_property_readonly (
19842055 " binding" ,
1985- [](const Path &self) {
2056+ [](const Path &self)
2057+ -> std::optional<std::tuple<std::string, Binding>> {
2058+ if (!self.binding ) {
2059+ return {};
2060+ }
19862061 return std::make_tuple ( //
19872062 self.graph ->__node_id (std::get<0 >(*self.binding )),
19882063 std::get<1 >(*self.binding ));
0 commit comments