📄 textdiagram.cpp
字号:
// TextDiagram.cpp: implementation of the CTextDiagram class.
//
//////////////////////////////////////////////////////////////////////
#include "stdafx.h"
#include "TextDiagram.h"
#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif
// rects
#define CHAR_TOP '-'
#define CHAR_RIGHT '|'
#define CHAR_BOTTOM '-'
#define CHAR_LEFT '|'
// connections
#define CHAR_HORZ '-'
#define CHAR_VERT '|'
#define CHAR_END '*'
#define CHAR_ENDUP '^'
#define CHAR_ENDDOWN 'v'
#define CHAR_ENDLEFT '<'
#define CHAR_ENDRIGHT '>'
#define CHAR_CROSSOVER '+'
#define CHAR_START '#'
// misc
#define ENDL '\n'
#define SPACE ' '
const int MAXITERATIONS = 1000;
//////////////////////////////////////////////////////////////////////
CTDRect::CTDRect(const CRect& rect, LPCTSTR szText) : CRect(rect), m_sText(szText)
{
}
CTDRect::CTDRect()
{
SetRectEmpty();
}
CTDRect::~CTDRect()
{
}
const CTDRect& CTDRect::operator=(const CTDRect& rect)
{
CopyRect(rect);
SetText(rect.GetText());
return *this;
}
/*
BOOL CTDRect::operator==(const CTDRect& rect)
{
return EqualRect(rect);
}
BOOL CTDRect::operator!=(const CTDRect& rect)
{
return !EqualRect(rect);
}
*/
int CTDRect::GetSide(CPoint point)
{
if (point.x == left)
return RECT_LEFT;
else if (point.x == right)
return RECT_RIGHT;
else if (point.y == top)
return RECT_TOP;
else if (point.y == bottom)
return RECT_BOTTOM;
else
return NONE;
}
//////////////////////////////////////////////////////////////////////
CTDConnection::CTDConnection(int nRectFrom, int nRectTo, int nSideFrom) :
m_nRectFrom(nRectFrom), m_nRectTo(nRectTo), m_nSideFrom(nSideFrom), m_ptFrom(0, 0)
{
}
CTDConnection::CTDConnection() : m_nRectFrom(0), m_nRectTo(0), m_ptFrom(0, 0)
{
}
CTDConnection::~CTDConnection()
{
}
const CTDConnection& CTDConnection::operator=(const CTDConnection& conn)
{
m_nRectFrom = conn.m_nRectFrom;
m_nRectTo = conn.m_nRectTo;
m_nSideFrom = conn.m_nSideFrom;
m_ptFrom = conn.m_ptFrom;
m_aSegments.Copy(conn.m_aSegments);
return *this;
}
/*
BOOL CTDConnection::operator==(const CTDConnection& conn)
{
// note: we don't rect indices because these can change
// instead we check the actual character positions
if (m_nSideFrom != conn.m_nSideFrom)
return FALSE;
if (m_ptFrom != conn.m_ptFrom)
return FALSE;
if (NumSegments() != conn.NumSegments())
return FALSE;
for (int nSegment = 0; nSegment < NumSegments(); nSegment++)
{
if (m_aSegments[nSegment] != conn.m_aSegments[nSegment])
return FALSE;
}
return TRUE;
}
BOOL CTDConnection::operator!=(const CTDConnection& conn)
{
return (!(*this == conn));
}
*/
CPoint CTDConnection::GetEndPos(int nNextSegment) const
{
CPoint ptEnd = GetSegmentPos(NumSegments() - 1);
int nDir = GetEndDirection(nNextSegment);
// nNextSegment
switch (nDir)
{
case CONN_UP:
case CONN_DOWN:
ptEnd.y += nNextSegment;
break;
case CONN_RIGHT:
case CONN_LEFT:
ptEnd.x += nNextSegment;
break;
}
return ptEnd;
}
CPoint CTDConnection::GetSegmentPos(int nSegment) const
{
if (nSegment < 0)
return GetStartPos();
int nDir = GetStartDirection();
CPoint ptEnd = GetStartPos();
int nLastSegment = min(nSegment, NumSegments() - 1);
for (nSegment = 0; nSegment <= nLastSegment; nSegment++)
{
switch (nDir)
{
case CONN_UP:
case CONN_DOWN:
ptEnd.y += m_aSegments[nSegment];
nDir = CONN_LEFT; // same effect as CONN_RIGHT
break;
case CONN_RIGHT:
case CONN_LEFT:
ptEnd.x += m_aSegments[nSegment];
nDir = CONN_UP; // same effect as CONN_DOWN
break;
}
}
return ptEnd;
}
int CTDConnection::GetLength() const
{
int nLength = 0;
int nSegment = NumSegments();
while (nSegment--)
nLength += abs(Segment(nSegment));
return nLength;
}
int CTDConnection::GetEndDirection(int nNextSegment) const
{
int nDir = GetStartDirection();
for (int nSegment = 1; nSegment < NumSegments(); nSegment++)
{
switch (nDir)
{
case CONN_UP:
case CONN_DOWN:
nDir = m_aSegments[nSegment] < 0 ? CONN_LEFT : CONN_RIGHT;
break;
case CONN_RIGHT:
case CONN_LEFT:
nDir = m_aSegments[nSegment] < 0 ? CONN_UP : CONN_DOWN;
break;
}
}
// nNextSegment
if (nNextSegment && NumSegments())
{
switch (nDir)
{
case CONN_UP:
case CONN_DOWN:
nDir = nNextSegment < 0 ? CONN_LEFT : CONN_RIGHT;
break;
case CONN_RIGHT:
case CONN_LEFT:
nDir = nNextSegment < 0 ? CONN_UP : CONN_DOWN;
break;
}
}
return nDir;
}
CRect CTDConnection::GetBoundingRect() const
{
CRect rBounds(m_ptFrom, CSize(0, 0));
for (int nSegment = 1; nSegment < NumSegments(); nSegment++)
{
CPoint ptEnd = GetSegmentPos(nSegment);
if (rBounds.left > ptEnd.x)
rBounds.left = ptEnd.x;
if (rBounds.right < ptEnd.x)
rBounds.right = ptEnd.x;
if (rBounds.top > ptEnd.y)
rBounds.top = ptEnd.y;
if (rBounds.bottom < ptEnd.y)
rBounds.bottom = ptEnd.y;
}
return rBounds;
}
int CTDConnection::GetStartDirection() const
{
switch (m_nSideFrom)
{
case RECT_TOP:
return CONN_UP;
case RECT_RIGHT:
return CONN_RIGHT;
case RECT_BOTTOM:
return CONN_DOWN;
case RECT_LEFT:
return CONN_LEFT;
}
return -1;
}
void CTDConnection::Increment(int& nSegment)
{
ASSERT (nSegment);
nSegment += (nSegment < 0) ? -1 : 1;
}
void CTDConnection::Decrement(int& nSegment)
{
ASSERT (nSegment);
nSegment -= (nSegment < 0) ? -1 : 1;
}
void CTDConnection::AddSegment(int nSegment)
{
m_aSegments.Add(nSegment);
}
void CTDConnection::IncrementLastSegment()
{
ASSERT (NumSegments());
if (NumSegments())
Increment(m_aSegments[NumSegments() - 1]);
}
BOOL CTDConnection::PtInConn(CPoint point, BOOL bHorz) const
{
// iterate all the segments testing each one for an overlap in the requested orientation
int nDir = GetStartDirection();
CPoint ptStart, ptEnd = GetStartPos();
for (int nSegment = 0; nSegment < NumSegments(); nSegment++)
{
ptStart = ptEnd;
switch (nDir)
{
case CONN_UP:
case CONN_DOWN:
ptEnd.y += m_aSegments[nSegment];
// test
if (!bHorz && point.x == ptEnd.x)
{
if ((point.y >= ptStart.y && point.y <= ptEnd.y) ||
(point.y >= ptEnd.y && point.y <= ptStart.y))
{
return TRUE;
}
}
nDir = CONN_LEFT; // same effect as CONN_RIGHT
break;
case CONN_RIGHT:
case CONN_LEFT:
ptEnd.x += m_aSegments[nSegment];
// test
if (bHorz && point.y == ptEnd.y)
{
if ((point.x >= ptStart.x && point.x <= ptEnd.x) ||
(point.x >= ptEnd.x && point.x <= ptStart.x))
{
return TRUE;
}
}
nDir = CONN_UP; // same effect as CONN_DOWN
break;
}
}
return FALSE;
}
//////////////////////////////////////////////////////////////////////
CTextDiagram::CTextDiagram(LPCTSTR szDiagram) : m_size(0, 0)
{
SetDiagram(szDiagram);
}
CTextDiagram::~CTextDiagram()
{
}
void CTextDiagram::ResetDiagram()
{
m_sTitle.Empty();
m_size.cx = m_size.cy = 0;
m_aRects.RemoveAll();
m_aConns.RemoveAll();
}
void CTextDiagram::SetDiagram(const CTextDiagram& diagram)
{
*this = diagram;
}
void CTextDiagram::SetDiagram(LPCTSTR szDiagram)
{
CStringArray diagram;
ConvertDiagram(szDiagram, diagram);
SetDiagram(diagram);
}
void CTextDiagram::SetDiagram(const CStringArray& diagram)
{
ResetDiagram();
ProcessDiagram(diagram);
}
int CTextDiagram::ConvertDiagram(LPCTSTR szDiagram, CStringArray& diagram)
{
// convert text into array of lines
CString sDiagram(szDiagram);
diagram.RemoveAll();
while (!sDiagram.IsEmpty())
{
int nFind = sDiagram.Find(ENDL);
CString sLine;
if (nFind != -1)
{
sLine = sDiagram.Left(nFind);
sDiagram = sDiagram.Mid(nFind + 1);
}
else
{
sLine = sDiagram;
sDiagram.Empty();
}
sLine.TrimRight();
ExpandTabs(sLine);
diagram.Add(sLine);
}
return diagram.GetSize();
}
BOOL CTextDiagram::GetDiagram(CStringArray& diagram, BOOL bVerify) const
{
if (bVerify && !VerifyDiagram())
return FALSE;
BuildDiagram(diagram);
return TRUE;
}
BOOL CTextDiagram::GetDiagram(CString& sDiagram, BOOL bVerify) const
{
CStringArray diagram;
if (!GetDiagram(diagram, bVerify))
return FALSE;
sDiagram.Empty();
for (int nLine = 0; nLine < diagram.GetSize(); nLine++)
{
sDiagram += diagram.GetAt(nLine);
sDiagram += "\r\n";
}
return TRUE;
}
void CTextDiagram::BuildDiagram(CStringArray& diagram) const
{
diagram.RemoveAll();
if (m_size.cx && m_size.cy)
{
// initialize the diagram to match the current size
for (int nY = 0; nY < m_size.cy + 1; nY++)
diagram.Add(CString(SPACE, m_size.cx + 1));
// draw each rect
CTDRect rect;
int nRect = 0;
while (GetRect(nRect, rect))
{
DrawRect(diagram, rect);
nRect++;
}
// draw each conn
CTDConnection conn;
int nConn = 0;
while (GetConnection(nConn, conn))
{
DrawConn(diagram, conn);
nConn++;
}
// add title at the end because this alters the y-coordinates
AddTitle(diagram);
// finally remove excess whitespace
int nLine = diagram.GetSize();
while (nLine--)
diagram[nLine].TrimRight();
}
}
void CTextDiagram::AddTitle(CStringArray& diagram) const
{
CStringArray aTitle;
CString sTitle(m_sTitle);
sTitle.TrimRight(); // remove trailing carriage returns
if (!sTitle.IsEmpty())
{
CString sLine(": ");
int nChar = 0, nLineChar = 0;
// walk the characters till we hit a new line character
while (nChar < sTitle.GetLength())
{
char c = sTitle[nChar];
// new line?
if (c == '\r')
{
sLine.TrimRight();
if (aTitle.GetSize() || sLine.GetLength() > 2)
aTitle.Add(sLine);
sLine = ": ";
nLineChar = 0;
nChar++; // jump '\n' too
}
else
{
sLine += c;
nLineChar++;
}
nChar++;
}
// add whatever's left
sLine.TrimRight();
if (sLine.GetLength() > 2)
aTitle.Add(sLine);
}
// insert a blank line above if we have a none empty title
if (aTitle.GetSize())
aTitle.InsertAt(0, "");
diagram.InsertAt(0, &aTitle);
}
void CTextDiagram::DrawRect(CStringArray& diagram, const CTDRect& rect) const
{
// right
DrawLine(diagram, CPoint(rect.right, rect.top), CPoint(rect.right, rect.bottom));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -