2929#include < wx/wx.h>
3030#endif // WX_PRECOMP
3131
32- #include " gambit.h"
33- #include " efgdisplay.h"
32+ #include " efglayout.h"
3433
3534namespace Gambit ::GUI {
3635namespace {
@@ -375,14 +374,15 @@ GameNode TreeLayout::BranchBelowHitTest(int p_x, int p_y) const
375374bool TreeLayout::InfosetHitTest (const std::shared_ptr<NodeEntry> &p_entry, const int p_x,
376375 const int p_y) const
377376{
378- if (p_entry->GetNextMember () && p_entry->GetNode ()->GetInfoset ()) {
377+ auto nextMember = ComputeNextInInfoset (p_entry);
378+ if (nextMember && p_entry->GetNode ()->GetInfoset ()) {
379379 if (p_x > p_entry->m_x + p_entry->GetSublevel () * m_infosetSpacing - 2 &&
380380 p_x < p_entry->m_x + p_entry->GetSublevel () * m_infosetSpacing + 2 ) {
381- if (p_y > p_entry->m_y && p_y < p_entry-> GetNextMember () ->m_y ) {
381+ if (p_y > p_entry->m_y && p_y < nextMember ->m_y ) {
382382 // next infoset is below this one
383383 return true ;
384384 }
385- if (p_y > p_entry-> GetNextMember () ->m_y && p_y < p_entry->m_y ) {
385+ if (p_y > nextMember ->m_y && p_y < p_entry->m_y ) {
386386 // next infoset is above this one
387387 return true ;
388388 }
@@ -557,43 +557,8 @@ GameNode TreeLayout::NextSameLevel(const GameNode &p_node) const
557557 return nullptr ;
558558}
559559
560- void TreeLayout::ComputeOffsets (const GameNode &p_node, const BehaviorSupportProfile &p_support,
561- int &p_ycoord)
562- {
563- const auto &settings = m_doc->GetStyle ();
564-
565- const auto entry = GetNodeEntry (p_node);
566- if (m_doc->GetStyle ().RootReachable () && p_node->GetInfoset () &&
567- !p_node->GetInfoset ()->GetPlayer ()->IsChance ()) {
568- const auto actions = p_support.GetActions (p_node->GetInfoset ());
569- for (const auto &action : actions) {
570- ComputeOffsets (p_node->GetChild (action), p_support, p_ycoord);
571- }
572- entry->m_y = (GetNodeEntry (p_node->GetChild (actions.front ()))->m_y +
573- GetNodeEntry (p_node->GetChild (actions.back ()))->m_y ) /
574- 2 ;
575- }
576- else if (!p_node->IsTerminal ()) {
577- const auto actions = p_node->GetInfoset ()->GetActions ();
578- for (const auto &action : actions) {
579- const auto child = p_node->GetChild (action);
580- ComputeOffsets (child, p_support, p_ycoord);
581- if (!p_node->GetPlayer ()->IsChance () && !p_support.Contains (action)) {
582- GetNodeEntry (child)->m_inSupport = false ;
583- }
584- }
585- entry->m_y = (GetNodeEntry (p_node->GetChild (actions.front ()))->m_y +
586- GetNodeEntry (p_node->GetChild (actions.back ()))->m_y ) /
587- 2 ;
588- }
589- else {
590- entry->m_y = p_ycoord;
591- p_ycoord += settings.TerminalSpacing ();
592- }
593- }
594-
595560std::shared_ptr<NodeEntry>
596- TreeLayout::ComputeNextInInfoset (const std::shared_ptr<NodeEntry> &p_entry)
561+ TreeLayout::ComputeNextInInfoset (const std::shared_ptr<NodeEntry> &p_entry) const
597562{
598563 const auto infoset = p_entry->m_node ->GetInfoset ();
599564 if (!infoset) {
@@ -620,22 +585,11 @@ TreeLayout::ComputeNextInInfoset(const std::shared_ptr<NodeEntry> &p_entry)
620585 return nullptr ;
621586}
622587
623- void TreeLayout::ComputeSublevel (const std::shared_ptr<NodeEntry> &p_entry)
624- {
625- try {
626- p_entry->m_sublevel = m_infosetSublevels.at ({p_entry->m_level , p_entry->m_node ->GetInfoset ()});
627- }
628- catch (std::out_of_range &) {
629- p_entry->m_sublevel = ++m_numSublevels[p_entry->m_level ];
630- m_infosetSublevels[{p_entry->m_level , p_entry->m_node ->GetInfoset ()}] = p_entry->m_sublevel ;
631- }
632- p_entry->m_nextMember = ComputeNextInInfoset (p_entry);
633- }
634-
635- void TreeLayout::ComputeNodeDepths () const
588+ void TreeLayout::ComputeNodeDepths (const Gambit::Layout &p_layout) const
636589{
637- std::vector<int > aggregateSublevels (m_maxLevel + 1 );
638- std::partial_sum (m_numSublevels.cbegin (), m_numSublevels.cend (), aggregateSublevels.begin ());
590+ std::vector<int > aggregateSublevels;
591+ std::partial_sum (p_layout.GetNumSublevels ().cbegin (), p_layout.GetNumSublevels ().cend (),
592+ std::back_inserter (aggregateSublevels));
639593 m_maxX = 0 ;
640594 for (const auto &entry : m_nodeList) {
641595 entry->m_x = c_leftMargin + entry->m_level * m_doc->GetStyle ().GetNodeLevelLength ();
@@ -654,43 +608,39 @@ void TreeLayout::ComputeRenderedParents() const
654608 }
655609}
656610
657- void TreeLayout::BuildNodeList (const GameNode &p_node, const BehaviorSupportProfile &p_support,
658- const int p_level)
611+ void TreeLayout::BuildNodeList (const GameNode &p_node, const BehaviorSupportProfile &p_support)
659612{
660613 const auto entry = std::make_shared<NodeEntry>(p_node);
661614 m_nodeList.push_back (entry);
662615 m_nodeMap[p_node] = entry;
663616 entry->m_size = m_doc->GetStyle ().GetNodeSize ();
664617 entry->m_branchLength = m_doc->GetStyle ().GetBranchLength ();
665- entry->m_level = p_level;
666618 if (m_doc->GetStyle ().RootReachable ()) {
667619 if (const GameInfoset infoset = p_node->GetInfoset ()) {
668620 if (infoset->GetPlayer ()->IsChance ()) {
669621 for (const auto &child : p_node->GetChildren ()) {
670- BuildNodeList (child, p_support, p_level + 1 );
622+ BuildNodeList (child, p_support);
671623 }
672624 }
673625 else {
674626 for (const auto &action : p_support.GetActions (infoset)) {
675- BuildNodeList (p_node->GetChild (action), p_support, p_level + 1 );
627+ BuildNodeList (p_node->GetChild (action), p_support);
676628 }
677629 }
678630 }
679631 }
680632 else {
681633 for (const auto &child : p_node->GetChildren ()) {
682- BuildNodeList (child, p_support, p_level + 1 );
634+ BuildNodeList (child, p_support);
683635 }
684636 }
685- m_maxLevel = std::max (p_level, m_maxLevel);
686637}
687638
688639void TreeLayout::BuildNodeList (const BehaviorSupportProfile &p_support)
689640{
690641 m_nodeList.clear ();
691642 m_nodeMap.clear ();
692- m_maxLevel = 0 ;
693- BuildNodeList (m_doc->GetGame ()->GetRoot (), p_support, 0 );
643+ BuildNodeList (m_doc->GetGame ()->GetRoot (), p_support);
694644}
695645
696646void TreeLayout::Layout (const BehaviorSupportProfile &p_support)
@@ -703,16 +653,18 @@ void TreeLayout::Layout(const BehaviorSupportProfile &p_support)
703653 BuildNodeList (p_support);
704654 }
705655
706- int maxy = c_topMargin;
707- ComputeOffsets (m_doc->GetGame ()->GetRoot (), p_support, maxy);
708- m_maxY = maxy + c_bottomMargin;
656+ auto layout = Gambit::Layout (m_doc->GetGame ());
657+ layout.LayoutTree (p_support);
709658
710- m_infosetSublevels.clear ();
711- m_numSublevels = std::vector<int >(m_maxLevel + 1 );
712- for (auto entry : m_nodeList) {
713- ComputeSublevel (entry);
659+ const auto spacing = m_doc->GetStyle ().GetTerminalSpacing ();
660+ for (auto [node, entry] : layout.GetNodeMap ()) {
661+ m_nodeMap[node]->m_level = entry->m_level ;
662+ m_nodeMap[node]->m_sublevel = entry->m_sublevel ;
663+ m_nodeMap[node]->m_y = entry->m_offset * spacing + c_topMargin;
714664 }
715- ComputeNodeDepths ();
665+ m_maxY =
666+ c_topMargin + c_bottomMargin + spacing * (layout.GetMaxOffset () - layout.GetMinOffset ());
667+ ComputeNodeDepths (layout);
716668
717669 ComputeRenderedParents ();
718670 GenerateLabels ();
@@ -773,9 +725,9 @@ void TreeLayout::RenderSubtree(wxDC &p_dc, bool p_noHints) const
773725 if (entry->GetChildNumber () == 1 ) {
774726 DrawNode (p_dc, parentEntry, m_doc->GetSelectNode (), p_noHints);
775727
776- if (parentEntry-> GetNextMember ( )) {
777- const int nextX = parentEntry-> GetNextMember () ->m_x ;
778- const int nextY = parentEntry-> GetNextMember () ->m_y ;
728+ if (auto nextMember = ComputeNextInInfoset (parentEntry )) {
729+ const int nextX = nextMember ->m_x ;
730+ const int nextY = nextMember ->m_y ;
779731
780732 if (parentEntry->m_x == nextX) {
781733#ifdef __WXGTK__
@@ -792,17 +744,15 @@ void TreeLayout::RenderSubtree(wxDC &p_dc, bool p_noHints) const
792744 parentEntry->m_x + parentEntry->GetSize (), nextY);
793745 }
794746
795- if (parentEntry-> GetNextMember () ->m_x != parentEntry->m_x ) {
747+ if (nextMember ->m_x != parentEntry->m_x ) {
796748 // Draw a little arrow in the direction of the iset.
797749 int startX, endX;
798750 if (settings.GetInfosetJoin () == GBT_INFOSET_JOIN_LINES) {
799751 startX = parentEntry->m_x ;
800- endX =
801- (startX + m_infosetSpacing *
802- ((parentEntry->GetNextMember ()->m_x > parentEntry->m_x ) ? 1 : -1 ));
752+ endX = (startX + m_infosetSpacing * ((nextMember->m_x > parentEntry->m_x ) ? 1 : -1 ));
803753 }
804754 else {
805- if (parentEntry-> GetNextMember () ->m_x < parentEntry->m_x ) {
755+ if (nextMember ->m_x < parentEntry->m_x ) {
806756 // information set is continued to the left
807757 startX = parentEntry->m_x + parentEntry->GetSize ();
808758 endX = parentEntry->m_x - m_infosetSpacing;
0 commit comments