『啊,葱爆羊肉真是香啊。』一边回忆着这个令人感动的美食,一边心不在焉地写了个程序。这个小程序实现了一个很简单的功能,将一棵树转换成bmp图片并自动排版。
思路非常简单。我们将树节点分为叶子节点和非叶子节点。一个叶子节点可以简单的处理为一个加了矩形边框的字符串,一个非叶子节点可以处理为一个加了矩形边框的字符串下面连接一堆树。先看看结果吧。下面有两幅图片,表示的都是使用
这篇文章的程序产生的两棵语法树。如果由于图片太大而看不清楚,就另存为到硬盘上慢慢观赏吧。
这个算法并没有通过什么计算来得到一个紧凑的图片。
1:(1+2)*(3+4)
2:(1+sin(2))*(sin(log(2,3))+4)
上图可以将元素分为树、文字以及线条。构造这种图的要领是每一个节点的坐标信息都使用父节点的坐标空间。这样的话不仅可以很快计算出图片所需要的尺寸,而且还可以很方便的绘制。绘制的时候递归一下,每加深一层就修改一下坐标空间。
下面给出代码。首先是头文件。其中类“GrammarSimulatorNode”是语法树的数据结构,包含了字符串和子节点。
1 #ifndef COMPRE_PAINTER
2 #define COMPRE_PAINTER
3
4 #include "GrammarAlgorithms.h"
5 #include "..\..\..\Library\Windows\VL_WinGDI.h"
6
7 using namespace vl::windows;
8
9 namespace compiler
10 {
11
12 /*********************************************************************************************************
13 GrammarSimulateNodePainter
14 *********************************************************************************************************/
15
16 class GrammarSimulateNodePainter : public VL_Base
17 {
18 public:
19 class Line
20 {
21 public:
22 typedef VL_List<Line , true> List;
23
24 VInt StartX;
25 VInt StartY;
26 VInt EndX;
27 VInt EndY;
28 };
29
30 class Rect
31 {
32 public:
33 typedef VL_List<Rect , false> List;
34
35 VInt Left;
36 VInt Top;
37 VInt Width;
38 VInt Height;
39 VInt TextOffsetX;
40 VInt TextOffsetY;
41 VUnicodeString Text;
42 };
43
44 class Graph
45 {
46 public:
47 typedef VL_AutoPtr<Graph> Ptr;
48 typedef VL_List<Ptr , false , Graph*> List;
49
50 VInt OffsetX;
51 VInt OffsetY;
52 VInt Width;
53 VInt Height;
54 VInt LineReceiverX;
55 Line::List Lines;
56 Rect::List Rects;
57 Graph::List Children;
58 };
59 public:
60 VL_WinBitmap::Ptr Bitmap;
61 VL_WinDC* BitmapDC;
62
63 GrammarSimulateNodePainter();
64
65 void InitBitmap();
66 Graph::Ptr Build(GrammarSimulatorNode::Ptr Node);
67 void ResizeBitmap(VInt Width , VInt Height);
68 void ResizeBitmap(Graph::Ptr g);
69 void Paint(Graph::Ptr g , VInt OffsetX , VInt OffsetY);
70 void Paint(GrammarSimulatorNode::Ptr Node);
71 };
72 }
73
74 #endif
然后是实现:
1 #include "Painter.h"
2
3 namespace compiler
4 {
5
6 /*********************************************************************************************************
7 GrammarSimulateNodePainter
8 *********************************************************************************************************/
9
10 const VInt GraphTextPadding=5;
11 const VInt GraphPadding=10;
12 const VInt GraphLevelPadding=24;
13
14 GrammarSimulateNodePainter::GrammarSimulateNodePainter()
15 {
16 Bitmap=new VL_WinBitmap(32,32,VL_WinBitmap::vbb32Bits,true);
17 InitBitmap();
18 }
19
20 void GrammarSimulateNodePainter::InitBitmap()
21 {
22 BitmapDC=Bitmap->GetWinDC();
23 BitmapDC->SetBackTransparent(true);
24 BitmapDC->FillRect(0,0,Bitmap->GetWidth(),Bitmap->GetHeight());
25 }
26
27 GrammarSimulateNodePainter::Graph::Ptr GrammarSimulateNodePainter::Build(GrammarSimulatorNode::Ptr Node)
28 {
29 VUnicodeString Text;
30 if(Node->SubExpressions.GetCount())
31 {
32 Text=Node->TerminatorName;
33 }
34 else
35 {
36 Text=Node->TerminatorName+L" : "+Node->Value;
37 }
38
39 SIZE TextSize=BitmapDC->MeasureString(Text);
40 Graph::Ptr g=new Graph;
41
42 VInt TotalWidth=0;
43 VInt TotalHeight=0;
44 for(VInt i=0;i<Node->SubExpressions.GetCount();i++)
45 {
46 Graph::Ptr sg=Build(Node->SubExpressions[i]);
47 sg->OffsetX=TotalWidth;
48 TotalWidth+=sg->Width;
49 if(i!=Node->SubExpressions.GetCount()-1)
50 {
51 TotalWidth+=GraphPadding;
52 }
53 if(sg->Height>TotalHeight)
54 {
55 TotalHeight=sg->Height;
56 }
57 g->Children.Add(sg);
58 }
59
60 Rect r;
61 r.Left=0;
62 r.Top=0;
63 r.Width=TextSize.cx+GraphTextPadding*2;
64 r.Height=TextSize.cy+GraphTextPadding*2;
65 r.TextOffsetX=GraphTextPadding;
66 r.TextOffsetY=GraphTextPadding;
67 r.Text=Text;
68
69 g->OffsetX=0;
70 g->OffsetY=0;
71 g->Width=r.Width;
72 g->Height=r.Height;
73
74 if(g->Width<TotalWidth)
75 {
76 r.Left=(TotalWidth-g->Width)/2;
77 g->Width=TotalWidth;
78 }
79 g->LineReceiverX=r.Left+r.Width/2;
80 g->Rects.Add(r);
81
82 if(g->Children.GetCount())
83 {
84 for(VInt i=0;i<g->Children.GetCount();i++)
85 {
86 Graph::Ptr sg=g->Children[i];
87
88 Line l;
89 l.StartX=g->LineReceiverX;
90 l.StartY=r.Height;
91 l.EndX=sg->LineReceiverX+sg->OffsetX;
92 l.EndY=l.StartY+GraphLevelPadding;
93 sg->OffsetY=l.EndY;
94 g->Lines.Add(l);
95 }
96 g->Height+=GraphLevelPadding+TotalHeight;
97 }
98
99 return g;
100 }
101
102 void GrammarSimulateNodePainter::ResizeBitmap(VInt Width , VInt Height)
103 {
104 VL_WinBitmap::Ptr NewBitmap=new VL_WinBitmap(Width,Height,VL_WinBitmap::vbb32Bits,true);
105 NewBitmap->GetWinDC()->SetFont(BitmapDC->GetFont());
106 NewBitmap->GetWinDC()->SetBrush(BitmapDC->GetBrush());
107 NewBitmap->GetWinDC()->SetPen(BitmapDC->GetPen());
108 Bitmap=NewBitmap;
109 InitBitmap();
110 }
111
112 void GrammarSimulateNodePainter::ResizeBitmap(Graph::Ptr g)
113 {
114 ResizeBitmap(g->Width+2*GraphPadding,g->Height+2*GraphPadding);
115 }
116
117 void GrammarSimulateNodePainter::Paint(Graph::Ptr g , VInt OffsetX , VInt OffsetY)
118 {
119 OffsetX+=g->OffsetX;
120 OffsetY+=g->OffsetY;
121 for(VInt i=0;i<g->Lines.GetCount();i++)
122 {
123 Line l=g->Lines[i];
124 BitmapDC->MoveTo(l.StartX+OffsetX,l.StartY+OffsetY);
125 BitmapDC->LineTo(l.EndX+OffsetX,l.EndY+OffsetY);
126 }
127 for(VInt i=0;i<g->Rects.GetCount();i++)
128 {
129 Rect r=g->Rects[i];
130 BitmapDC->Rectangle(
131 r.Left+OffsetX,
132 r.Top+OffsetY,
133 r.Left+r.Width+OffsetX,
134 r.Top+r.Height+OffsetY);
135 BitmapDC->DrawString(
136 r.Left+r.TextOffsetX+OffsetX,
137 r.Top+r.TextOffsetY+OffsetY,
138 r.Text);
139 }
140 for(VInt i=0;i<g->Children.GetCount();i++)
141 {
142 Paint(g->Children[i],OffsetX,OffsetY);
143 }
144 }
145
146 void GrammarSimulateNodePainter::Paint(GrammarSimulatorNode::Ptr Node)
147 {
148 Graph::Ptr g=Build(Node);
149 ResizeBitmap(g);
150 Paint(g,GraphPadding,GraphPadding);
151 }
152 }
posted on 2008-09-07 04:18
陈梓瀚(vczh) 阅读(2858)
评论(3) 编辑 收藏 引用 所属分类:
2D