@@ -704,6 +704,154 @@ bool LinkJointGraph::link_is_static(const Link& link) const {
704
704
ForestBuildingOptions::kStatic );
705
705
}
706
706
707
+ /* Runs through the Mobods in the model but records the (active) Link
708
+ indexes rather than the Mobod indexes. */
709
+ std::vector<BodyIndex> LinkJointGraph::FindPathFromWorld (
710
+ BodyIndex link_index) const {
711
+ ThrowIfForestNotBuiltYet (__func__);
712
+ const SpanningForest::Mobod* mobod =
713
+ &forest ().mobods ()[link_to_mobod (link_index)];
714
+ std::vector<BodyIndex> path (mobod->level () + 1 );
715
+ while (mobod->inboard ().is_valid ()) {
716
+ const Link& link = links (mobod->link_ordinal ());
717
+ path[mobod->level ()] = link .index (); // Active Link if composite.
718
+ mobod = &forest ().mobods (mobod->inboard ());
719
+ }
720
+ DRAKE_DEMAND (mobod->is_world ());
721
+ path[0 ] = BodyIndex (0 );
722
+ return path;
723
+ }
724
+
725
+ BodyIndex LinkJointGraph::FindFirstCommonAncestor (BodyIndex link1_index,
726
+ BodyIndex link2_index) const {
727
+ ThrowIfForestNotBuiltYet (__func__);
728
+ const MobodIndex mobod_ancestor = forest ().FindFirstCommonAncestor (
729
+ link_to_mobod (link1_index), link_to_mobod (link2_index));
730
+ const Link& ancestor_link =
731
+ links (forest ().mobod_to_link_ordinal (mobod_ancestor));
732
+ return ancestor_link.index ();
733
+ }
734
+
735
+ std::vector<BodyIndex> LinkJointGraph::FindSubtreeLinks (
736
+ BodyIndex link_index) const {
737
+ ThrowIfForestNotBuiltYet (__func__);
738
+ const MobodIndex root_mobod_index = link_to_mobod (link_index);
739
+ return forest ().FindSubtreeLinks (root_mobod_index);
740
+ }
741
+
742
+ // Our link_composites collection doesn't include lone Links that aren't welded
743
+ // to anything. The return from this function must include every Link, with
744
+ // the World link in the first set (even if nothing is welded to it).
745
+ std::vector<std::set<BodyIndex>> LinkJointGraph::GetSubgraphsOfWeldedLinks ()
746
+ const {
747
+ ThrowIfForestNotBuiltYet (__func__);
748
+
749
+ std::vector<std::set<BodyIndex>> subgraphs;
750
+
751
+ // First, collect all the precomputed Link Composites. World is always
752
+ // the first one, even if nothing is welded to it.
753
+ for (const LinkComposite& composite : link_composites ()) {
754
+ subgraphs.emplace_back (
755
+ std::set<BodyIndex>(composite.links .cbegin (), composite.links .cend ()));
756
+ }
757
+
758
+ // Finally, make one-Link subgraphs for Links that aren't in any composite.
759
+ for (const Link& link : links ()) {
760
+ if (link .composite ().has_value ()) continue ;
761
+ subgraphs.emplace_back (std::set<BodyIndex>{link .index ()});
762
+ }
763
+
764
+ return subgraphs;
765
+ }
766
+
767
+ // Strategy here is to make repeated use of CalcLinksWeldedTo(), separating
768
+ // the singleton sets from the actually-welded sets, and then move the
769
+ // singletons to the end to match what GetSubgraphsOfWeldedLinks() does.
770
+ std::vector<std::set<BodyIndex>> LinkJointGraph::CalcSubgraphsOfWeldedLinks ()
771
+ const {
772
+ // Work with ordinals rather than indexes.
773
+ std::vector<bool > visited (num_user_links (), false );
774
+
775
+ // World always comes first, even if it is alone.
776
+ std::vector<std::set<BodyIndex>> subgraphs{CalcLinksWeldedTo (BodyIndex (0 ))};
777
+ for (BodyIndex index : subgraphs.back ())
778
+ visited[index_to_ordinal (index )] = true ;
779
+
780
+ std::vector<std::set<BodyIndex>> singletons;
781
+ // If a Forest was already built, there may be shadow links added to
782
+ // the graph -- don't process those here.
783
+ for (LinkOrdinal link_ordinal (1 ); link_ordinal < num_user_links ();
784
+ ++link_ordinal) {
785
+ const Link& link = links (link_ordinal);
786
+ if (link .is_shadow () || visited[link_ordinal]) continue ;
787
+ std::set<BodyIndex> welded_links = CalcLinksWeldedTo (link .index ());
788
+ for (BodyIndex index : welded_links)
789
+ visited[index_to_ordinal (index )] = true ;
790
+ auto * which_list = ssize (welded_links) == 1 ? &singletons : &subgraphs;
791
+ which_list->emplace_back (std::move (welded_links));
792
+ }
793
+
794
+ // Now move all the singletons onto the end of the subgraphs list.
795
+ for (auto & singleton : singletons)
796
+ subgraphs.emplace_back (std::move (singleton));
797
+
798
+ return subgraphs;
799
+ }
800
+
801
+ // If the Link isn't part of a LinkComposite just return the Link. Otherwise,
802
+ // return all the Links in its LinkComposite.
803
+ std::set<BodyIndex> LinkJointGraph::GetLinksWeldedTo (
804
+ BodyIndex link_index) const {
805
+ ThrowIfForestNotBuiltYet (__func__);
806
+ DRAKE_DEMAND (link_index.is_valid ());
807
+ DRAKE_THROW_UNLESS (has_link (link_index));
808
+ const Link& link = link_by_index (link_index);
809
+ const std::optional<LinkCompositeIndex> composite_index = link .composite ();
810
+ if (!composite_index.has_value ()) return std::set<BodyIndex>{link_index};
811
+ const std::vector<BodyIndex>& welded_links =
812
+ link_composites (*composite_index).links ;
813
+ return std::set<BodyIndex>(welded_links.cbegin (), welded_links.cend ());
814
+ }
815
+
816
+ // Without a Forest we don't have LinkComposites available so recursively
817
+ // chase Weld joints instead.
818
+ std::set<BodyIndex> LinkJointGraph::CalcLinksWeldedTo (
819
+ BodyIndex link_index) const {
820
+ std::set<BodyIndex> result;
821
+ AppendLinksWeldedTo (link_index, &result);
822
+ return result;
823
+ }
824
+
825
+ void LinkJointGraph::AppendLinksWeldedTo (BodyIndex link_index,
826
+ std::set<BodyIndex>* result) const {
827
+ DRAKE_DEMAND (result != nullptr );
828
+ DRAKE_DEMAND (link_index.is_valid ());
829
+ DRAKE_THROW_UNLESS (has_link (link_index));
830
+ DRAKE_DEMAND (result->count (link_index) == 0 );
831
+
832
+ const Link& link = link_by_index (link_index);
833
+
834
+ // A Link is always considered welded to itself.
835
+ result->insert (link_index);
836
+ for (auto joint_index : link .joints ()) {
837
+ const Joint& joint = joint_by_index (joint_index);
838
+ if (joint.traits_index () != weld_joint_traits_index ()) continue ;
839
+ const BodyIndex welded_link_index = joint.other_link_index (link_index);
840
+ // Don't reprocess if we already did the other end.
841
+ if (result->count (welded_link_index) == 0 )
842
+ AppendLinksWeldedTo (welded_link_index, &*result);
843
+ }
844
+ }
845
+
846
+ void LinkJointGraph::ThrowIfForestNotBuiltYet (const char * func) const {
847
+ if (!forest_is_valid ()) {
848
+ throw std::logic_error (
849
+ fmt::format (" {}(): no SpanningForest available. Call BuildForest() "
850
+ " before calling this function." ,
851
+ func));
852
+ }
853
+ }
854
+
707
855
void LinkJointGraph::ThrowLinkWasRemoved (const char * func,
708
856
BodyIndex link_index) const {
709
857
throw std::logic_error (fmt::format (
0 commit comments