📄 efglayout.cc
字号:
//// $Source: /home/gambit/CVS/gambit/sources/gui/efglayout.cc,v $// $Date: 2002/09/14 23:24:39 $// $Revision: 1.34.2.1 $//// DESCRIPTION:// Implementation of tree layout representation//// This file is part of Gambit// Copyright (c) 2002, The Gambit Project//// This program is free software; you can redistribute it and/or modify// it under the terms of the GNU General Public License as published by// the Free Software Foundation; either version 2 of the License, or// (at your option) any later version.//// This program is distributed in the hope that it will be useful,// but WITHOUT ANY WARRANTY; without even the implied warranty of// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the// GNU General Public License for more details.//// You should have received a copy of the GNU General Public License// along with this program; if not, write to the Free Software// Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.//#include <math.h>#include "wx/wxprec.h"#ifndef WX_PRECOMP#include "wx/wx.h"#endif // WX_PRECOMP#include "math/gmath.h"#include "game/efg.h"#include "treewin.h"#include "efgshow.h"//-----------------------------------------------------------------------// class NodeEntry: Member functions//-----------------------------------------------------------------------NodeEntry::NodeEntry(Node *p_node) : m_node(p_node), m_parent(0), m_x(-1), m_y(-1), m_nextMember(0), m_inSupport(true), m_selected(false), m_cursor(false), m_cut(false), m_subgameRoot(false), m_subgameMarked(false), m_size(20), m_token(NODE_TOKEN_CIRCLE), m_branchStyle(BRANCH_STYLE_LINE), m_branchLabel(BRANCH_LABEL_HORIZONTAL), m_branchLength(0), m_sublevel(0), m_actionProb(0){ }int NodeEntry::GetChildNumber(void) const{ if (m_node->GetParent()) { return m_node->GetAction()->GetNumber(); } else { return 0; }}void NodeEntry::SetCursor(bool p_cursor){ m_cursor = p_cursor; if (m_cursor) { m_selected = true; }}//// Draws the node token itself, as well as the incoming branch// (if not the root node)//void NodeEntry::Draw(wxDC &p_dc) const{ if (m_node->GetParent() && m_inSupport) { DrawIncomingBranch(p_dc); } if (m_cut) { p_dc.SetPen(*wxLIGHT_GREY_PEN); } else { p_dc.SetPen(*wxThePenList->FindOrCreatePen(m_color, (IsSelected()) ? 4 : 2, wxSOLID)); } if (m_token == NODE_TOKEN_LINE) { p_dc.DrawLine(m_x, m_y, m_x + m_size, m_y); if (m_branchStyle == BRANCH_STYLE_FORKTINE) { // "classic" Gambit style: draw a small 'token' to separate // the fork from the node p_dc.DrawEllipse(m_x - 1, m_y - 1, 3, 3); } } else if (m_token == NODE_TOKEN_BOX) { p_dc.SetBrush(*wxWHITE_BRUSH); p_dc.DrawRectangle(m_x, m_y - m_size / 2, m_size, m_size); } else if (m_token == NODE_TOKEN_DIAMOND) { wxPoint points[4] = { wxPoint(m_x + m_size / 2, m_y - m_size / 2), wxPoint(m_x, m_y), wxPoint(m_x + m_size / 2, m_y + m_size / 2), wxPoint(m_x + m_size, m_y) }; p_dc.SetBrush(*wxWHITE_BRUSH); p_dc.DrawPolygon(4, points); } else { // Default: draw circles p_dc.SetBrush(*wxWHITE_BRUSH); p_dc.DrawEllipse(m_x, m_y - m_size / 2, m_size, m_size); } int textWidth, textHeight; p_dc.SetFont(m_nodeAboveFont); p_dc.GetTextExtent(m_nodeAboveLabel, &textWidth, &textHeight); p_dc.DrawText(m_nodeAboveLabel, m_x + (m_size - textWidth) / 2, m_y - textHeight - 9); p_dc.SetFont(m_nodeBelowFont); p_dc.GetTextExtent(m_nodeBelowLabel, &textWidth, &textHeight); p_dc.DrawText(m_nodeBelowLabel, m_x + (m_size - textWidth) / 2, m_y + 9); p_dc.SetFont(m_nodeRightFont); p_dc.GetTextExtent(m_nodeRightLabel, &textWidth, &textHeight); p_dc.DrawText(m_nodeRightLabel, m_x + GetSize() + 10, m_y - textHeight/2); if (m_subgameRoot) { if (m_subgameMarked) { p_dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 2, wxSOLID)); } else { p_dc.SetPen(*wxThePenList->FindOrCreatePen(*wxLIGHT_GREY, 2, wxSOLID)); } p_dc.DrawLine(m_x - m_size / 2, m_y, m_x + 2 * m_size, m_y + 2 * m_size); p_dc.DrawLine(m_x - m_size / 2, m_y, m_x + 2 * m_size, m_y - 2 * m_size); }}void NodeEntry::DrawIncomingBranch(wxDC &p_dc) const{ int xStart = m_parent->m_x + m_parent->m_size; int xEnd = m_x; int yStart = m_parent->m_y; int yEnd = m_y; if (m_parent->m_cut) { p_dc.SetPen(*wxLIGHT_GREY_PEN); } else { p_dc.SetPen(*wxThePenList->FindOrCreatePen(m_parent->m_color, 2, wxSOLID)); } if (m_branchStyle == BRANCH_STYLE_LINE) { p_dc.DrawLine(xStart, yStart, xEnd, yEnd); // Draw in the highlight indicating action probabilities if (m_actionProb >= gNumber(0)) { p_dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 2, wxSOLID)); p_dc.DrawLine(xStart, yStart, xStart + (int) ((double) (xEnd - xStart) * (double) m_actionProb), yStart + (int) ((double) (yEnd - yStart) * (double) m_actionProb)); } int textWidth, textHeight; p_dc.SetFont(m_branchAboveFont); p_dc.GetTextExtent(m_branchAboveLabel, &textWidth, &textHeight); // The angle of the branch double theta = -atan((double) (yEnd - yStart) / (double) (xEnd - xStart)); // The "centerpoint" of the branch int xbar = (xStart + xEnd) / 2; int ybar = (yStart + yEnd) / 2; if (m_branchLabel == BRANCH_LABEL_HORIZONTAL) { if (yStart >= yEnd) { p_dc.DrawText(m_branchAboveLabel, xbar - textWidth / 2, ybar - textHeight + textWidth / 2 * (yEnd - yStart) / (xEnd - xStart)); } else { p_dc.DrawText(m_branchAboveLabel, xbar - textWidth / 2, ybar - textHeight - textWidth / 2 * (yEnd - yStart) / (xEnd - xStart)); } } else { // Draw the text rotated appropriately p_dc.DrawRotatedText(m_branchAboveLabel, (int) ((double) xbar - (double) textHeight * sin(theta) - (double) textWidth * cos(theta) / 2.0), (int) ((double) ybar - (double) textHeight * cos(theta) + (double) textWidth * sin(theta) / 2.0), theta * 180.0 / 3.14159); } p_dc.SetFont(m_branchBelowFont); p_dc.GetTextExtent(m_branchBelowLabel, &textWidth, &textHeight); if (m_branchLabel == BRANCH_LABEL_HORIZONTAL) { if (yStart >= yEnd) { p_dc.DrawText(m_branchBelowLabel, xbar - textWidth / 2, ybar - textWidth/2 * (yEnd - yStart) / (xEnd - xStart)); } else { p_dc.DrawText(m_branchBelowLabel, xbar - textWidth / 2, ybar + textWidth/2 * (yEnd - yStart) / (xEnd - xStart)); } } else { // Draw the text rotated appropriately p_dc.DrawRotatedText(m_branchBelowLabel, (int) ((double) xbar - (double) textWidth * cos(theta) / 2.0), (int) ((double) ybar + (double) textWidth * sin(theta) / 2.0), theta * 180.0 / 3.14159); } } else { // Old style fork-tine p_dc.DrawLine(xStart, yStart, xStart + m_branchLength, yEnd); p_dc.DrawLine(xStart + m_branchLength, yEnd, xEnd, yEnd); // Draw in the highlight indicating action probabilities if (m_actionProb >= gNumber(0)) { p_dc.SetPen(*wxThePenList->FindOrCreatePen(*wxBLACK, 2, wxSOLID)); p_dc.DrawLine(xStart, yStart, xStart + (int) ((double) m_branchLength * (double) m_actionProb), yStart + (int) ((double) (yEnd - yStart) * (double) m_actionProb)); } int textWidth, textHeight; p_dc.SetFont(m_branchAboveFont); p_dc.GetTextExtent(m_branchAboveLabel, &textWidth, &textHeight); p_dc.DrawText(m_branchAboveLabel, xStart + m_branchLength + 3, yEnd - textHeight - 3); p_dc.SetFont(m_branchBelowFont); p_dc.GetTextExtent(m_branchBelowLabel, &textWidth, &textHeight); p_dc.DrawText(m_branchBelowLabel, xStart + m_branchLength + 3, yEnd + 3); }}bool NodeEntry::NodeHitTest(int p_x, int p_y) const{ if (p_x < m_x || p_x >= m_x + m_size) { return false; } if (m_token == NODE_TOKEN_LINE) { const int DELTA = 8; // a fudge factor for "almost" hitting the node return (p_y >= m_y - DELTA && p_y <= m_y + DELTA); } else { return (p_y >= m_y - m_size / 2 && p_y <= m_y + m_size / 2); }}//-----------------------------------------------------------------------// class efgTreeLayout: Member functions//-----------------------------------------------------------------------efgTreeLayout::efgTreeLayout(efgGame &p_efg, TreeWindow *p_parent) : m_efg(p_efg), m_parent(p_parent), m_infosetSpacing(40), c_leftMargin(20), c_topMargin(40){ }Node *efgTreeLayout::NodeHitTest(int p_x, int p_y) const{ for (int i = 1; i <= m_nodeList.Length(); i++) { if (m_nodeList[i]->NodeHitTest(p_x, p_y)) { return m_nodeList[i]->GetNode(); } } return 0;}Node *efgTreeLayout::BranchHitTest(int p_x, int p_y) const{ for (int i = 1; i <= m_nodeList.Length(); i++) { NodeEntry *entry = m_nodeList[i]; NodeEntry *parent_entry = GetNodeEntry(entry->GetNode()->GetParent()); if (parent_entry) { if (p_x > (parent_entry->X() + m_parent->DrawSettings().NodeSize() + parent_entry->GetSublevel() * m_infosetSpacing + 10) && p_x < (parent_entry->X() + m_parent->DrawSettings().NodeSize() + m_parent->DrawSettings().BranchLength() + parent_entry->GetSublevel() * m_infosetSpacing)) { // Good old slope/intercept method for finding a point on a line int y0 = (parent_entry->Y() + (int) (p_x - parent_entry->X() - m_parent->DrawSettings().NodeSize()) * (entry->Y() - parent_entry->Y()) / m_parent->DrawSettings().BranchLength()); if (p_y > y0-2 && p_y < y0+2) { return entry->GetNode(); } } } } return 0;}Node *efgTreeLayout::InfosetHitTest(int p_x, int p_y) const{ for (int i = 1; i <= m_nodeList.Length(); i++) { NodeEntry *entry = m_nodeList[i]; if (entry->GetNextMember() && entry->GetNode()->GetInfoset()) { if (p_x > entry->X() + entry->GetSublevel() * m_infosetSpacing - 2 && p_x < entry->X() + entry->GetSublevel() * m_infosetSpacing + 2) { if (p_y > entry->Y() && p_y < entry->GetNextMember()->Y()) { // next iset is below this one return entry->GetNode(); } else if (p_y > entry->GetNextMember()->Y() && p_y < entry->Y()) { // next iset is above this one return entry->GetNode(); } } } } return 0;}wxString efgTreeLayout::CreateNodeAboveLabel(const NodeEntry *p_entry) const{ const Node *n = p_entry->GetNode(); switch (m_parent->DrawSettings().NodeAboveLabel()) { case NODE_ABOVE_NOTHING: return ""; case NODE_ABOVE_LABEL: return (const char *) n->GetName(); case NODE_ABOVE_PLAYER: return ((const char *) ((n->GetPlayer()) ? n->GetPlayer()->GetName() : gText(""))); case NODE_ABOVE_ISETLABEL: return ((const char *) ((n->GetInfoset()) ? n->GetInfoset()->GetName() : gText(""))); case NODE_ABOVE_ISETID: return ((const char *) ((n->GetInfoset()) ? ("(" + ToText(n->GetPlayer()->GetNumber()) + "," + ToText(n->GetInfoset()->GetNumber()) + ")") : gText(""))); case NODE_ABOVE_OUTCOME: return (const char *) m_parent->OutcomeAsString(n); case NODE_ABOVE_REALIZPROB: return (const char *) m_parent->Parent()->GetRealizProb(n); case NODE_ABOVE_BELIEFPROB: return (const char *) m_parent->Parent()->GetBeliefProb(n); case NODE_ABOVE_VALUE: return (const char *) m_parent->Parent()->GetNodeValue(n); default: return ""; }} wxString efgTreeLayout::CreateNodeBelowLabel(const NodeEntry *p_entry) const{ const Node *n = p_entry->GetNode(); switch (m_parent->DrawSettings().NodeBelowLabel()) { case NODE_BELOW_NOTHING: return ""; case NODE_BELOW_LABEL: return (const char *) n->GetName(); case NODE_BELOW_PLAYER: return ((const char *) ((n->GetPlayer()) ? n->GetPlayer()->GetName() : gText(""))); case NODE_BELOW_ISETLABEL: return ((const char *) ((n->GetInfoset()) ? n->GetInfoset()->GetName() : gText(""))); case NODE_BELOW_ISETID: return ((const char *) ((n->GetInfoset()) ? ("(" + ToText(n->GetPlayer()->GetNumber()) + "," + ToText(n->GetInfoset()->GetNumber()) + ")") : gText(""))); case NODE_BELOW_OUTCOME: return (const char *) m_parent->OutcomeAsString(n); case NODE_BELOW_REALIZPROB: return (const char *) m_parent->Parent()->GetRealizProb(n); case NODE_BELOW_BELIEFPROB: return (const char *) m_parent->Parent()->GetBeliefProb(n); case NODE_BELOW_VALUE: return (const char *) m_parent->Parent()->GetNodeValue(n); default: return ""; }}wxString efgTreeLayout::CreateNodeRightLabel(const NodeEntry *p_entry) const{ const Node *node = p_entry->GetNode(); switch (m_parent->DrawSettings().NodeRightLabel()) { case NODE_RIGHT_NOTHING: return ""; case NODE_RIGHT_OUTCOME: return (const char *) m_parent->OutcomeAsString(node); case NODE_RIGHT_NAME: if (node->Game()->GetOutcome(node)) { return (const char *) m_efg.GetOutcomeName(node->Game()->GetOutcome(node)); } else { return ""; } default: return ""; }}wxString efgTreeLayout::CreateBranchAboveLabel(const NodeEntry *p_entry) const{ const Node *parent = p_entry->GetParent()->GetNode(); switch (m_parent->DrawSettings().BranchAboveLabel()) { case BRANCH_ABOVE_NOTHING: return ""; case BRANCH_ABOVE_LABEL: return (const char *) parent->GetInfoset()->GetActionName(p_entry->GetChildNumber()); case BRANCH_ABOVE_PROBS: return (const char *) m_parent->Parent()->GetActionProb(parent, p_entry->GetChildNumber()); case BRANCH_ABOVE_VALUE: return (const char *) m_parent->Parent()->GetActionValue(parent, p_entry->GetChildNumber()); default: return ""; }}wxString efgTreeLayout::CreateBranchBelowLabel(const NodeEntry *p_entry) const{ const Node *parent = p_entry->GetParent()->GetNode(); switch (m_parent->DrawSettings().BranchBelowLabel()) { case BRANCH_BELOW_NOTHING: return ""; case BRANCH_BELOW_LABEL: return (const char *) parent->GetInfoset()->GetActionName(p_entry->GetChildNumber()); case BRANCH_BELOW_PROBS: return (const char *) m_parent->Parent()->GetActionProb(parent,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -