界面部分基本完成。程序的架构实在是恶心到不行,明天从头再来。
main.cpp:
C++语言:
Codee#26094
01
#include <QtGui>
02 #include <QtCore>
03 #include "pathfinding.h"
04
05 int main( int argc , char * argv [])
06 {
07 QApplication a( argc , argv);
08 Widget * window = new Widget;
09
10 QRect frect = window -> frameGeometry();
11 frect . moveCenter( QDesktopWidget (). availableGeometry (). center());
12 window -> move( frect . topLeft());
13 window -> show();
14
15 return a . exec();
16 }
02 #include <QtCore>
03 #include "pathfinding.h"
04
05 int main( int argc , char * argv [])
06 {
07 QApplication a( argc , argv);
08 Widget * window = new Widget;
09
10 QRect frect = window -> frameGeometry();
11 frect . moveCenter( QDesktopWidget (). availableGeometry (). center());
12 window -> move( frect . topLeft());
13 window -> show();
14
15 return a . exec();
16 }
path-finding.h:
C++语言:
Codee#26095
01
#ifndef PATHFINDING_H
02 #define PATHFINDING_H
03
04 #include <QtGui>
05 #include <QtCore>
06
07 #include "control.h"
08
09 class Widget : public QWidget
10 {
11 Q_OBJECT
12
13 public :
14 Widget( QWidget * parent = 0);
15 void paintEvent( QPaintEvent * event);
16 void mousePressEvent( QMouseEvent * event);
17 void mouseMoveEvent( QMouseEvent * event);
18 void keyPressEvent( QKeyEvent * event);
19 void setWindowPixel( const QPoint & pos , bool opaque);
20
21 private :
22 enum { LENGTH = 910 };
23 enum { WIDTH = 700 };
24 enum { BLOCKSIZE = 35 };
25 enum { MES_Y_OFFSET = 16 };
26
27 bool blocks [ LENGTH / BLOCKSIZE ][ WIDTH / BLOCKSIZE ];
28 bool onDrag;
29 QPoint helpImagePos;
30 QPoint helpMessagePos;
31 QPoint controlImagePos;
32 QPoint controlMessagePos;
33 QPoint cursorpos;
34 QPoint startBlock;
35 QPoint endBlock;
36 Control controlMessage [ 5 ];
37 bool checkPosition( const QPoint & pos);
38 int controlSelection;
39 int speed;
40 };
41
42 #endif // PATHFINDING_H
02 #define PATHFINDING_H
03
04 #include <QtGui>
05 #include <QtCore>
06
07 #include "control.h"
08
09 class Widget : public QWidget
10 {
11 Q_OBJECT
12
13 public :
14 Widget( QWidget * parent = 0);
15 void paintEvent( QPaintEvent * event);
16 void mousePressEvent( QMouseEvent * event);
17 void mouseMoveEvent( QMouseEvent * event);
18 void keyPressEvent( QKeyEvent * event);
19 void setWindowPixel( const QPoint & pos , bool opaque);
20
21 private :
22 enum { LENGTH = 910 };
23 enum { WIDTH = 700 };
24 enum { BLOCKSIZE = 35 };
25 enum { MES_Y_OFFSET = 16 };
26
27 bool blocks [ LENGTH / BLOCKSIZE ][ WIDTH / BLOCKSIZE ];
28 bool onDrag;
29 QPoint helpImagePos;
30 QPoint helpMessagePos;
31 QPoint controlImagePos;
32 QPoint controlMessagePos;
33 QPoint cursorpos;
34 QPoint startBlock;
35 QPoint endBlock;
36 Control controlMessage [ 5 ];
37 bool checkPosition( const QPoint & pos);
38 int controlSelection;
39 int speed;
40 };
41
42 #endif // PATHFINDING_H
path-finding.cpp:
C++语言:
Codee#26096
001
#include "pathfinding.h"
002
003 Widget :: Widget( QWidget * parent)
004 : QWidget( parent)
005 {
006 for ( int i = 0; i < LENGTH / BLOCKSIZE; ++ i)
007 for ( int j = 0; j < WIDTH / BLOCKSIZE; ++ j)
008 blocks [ i ][ j ] = true;
009 startBlock = QPoint( 0 , 0);
010 blocks [ 0 ][ 0 ] = false;
011
012 endBlock = QPoint( 10 , 10);
013 blocks [ 10 ][ 10 ] = false;
014 onDrag = false;
015
016 controlMessage [ 0 ] = Control( QString( "Dijkstra" ),
017 QColor( Qt :: darkGreen));
018 controlMessage [ 1 ] = Control( QString( "Bi-Directional BFS" ),
019 QColor( Qt :: darkGreen));
020
021
022 helpImagePos = QPoint( 5 , 5);
023 helpMessagePos = QPoint( 10 , 10);
024 controlImagePos = QPoint( 790 , 5);
025 controlMessagePos = QPoint( 795 , 10);
026 controlSelection = 0;
027 speed = 1;
028
029 setAttribute( Qt :: WA_StaticContents);
030 setSizePolicy( QSizePolicy :: Minimum , QSizePolicy :: Minimum);
031 this -> setMouseTracking( true);
032 this -> setFixedSize( LENGTH , WIDTH);
033
034 /*
035 QImage* image=new QImage;
036 image->load(":/image/layer2.png");
037
038 QImage fixedImage(image->size(), QImage::Format_ARGB32_Premultiplied);
039 QPainter imagePainter(&fixedImage);
040 imagePainter.setCompositionMode(QPainter::CompositionMode_Source);
041 imagePainter.fillRect(fixedImage.rect(), Qt::transparent);
042 imagePainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
043 imagePainter.drawImage(QPoint(10,10), *image);
044 imagePainter.end();
045 */
046
047 //QImage pic(":/image/test.jpg");
048 /*
049 I try to use QLabel to show the help message
050 but failed,picture and text can't show at the
051 same time
052 label->setPixmap(QPixmap::convertFromImage(pic));
053 */
054 /*
055 button->setText(controlMessage[0].GetString() + "\n" +
056 controlMessage[1].GetString());
057 label->setPixmap(QPixmap::fromImage(pic));
058 label->show();
059 */
060 /*
061 QHBoxLayout* layout = new QHBoxLayout;
062 layout->addWidget(label);
063 this->setLayout(layout);
064 */
065 }
066
067 void Widget :: paintEvent( QPaintEvent * event)
068 {
069 QPainter painter( this);
070 painter . setOpacity( 0.8);
071 QColor color = Qt :: red;
072
073 /* draw the start block */
074 /*
075 painter.fillRect(QRect(BLOCKSIZE*startBlock.x(), BLOCKSIZE*startBlock.y(),
076 BLOCKSIZE, BLOCKSIZE), color);
077 */
078
079 //qDebug("The startblock pos:%d,%d", startBlock.x(), startBlock.y());
080 /* draw the simple blocks */
081 for ( int i = 0; i < LENGTH / BLOCKSIZE; ++ i)
082 for ( int j = 0; j < WIDTH / BLOCKSIZE; ++ j)
083 {
084 QPoint cur( i , j);
085 if ( cur == startBlock)
086 color = Qt :: red;
087 else if ( cur == endBlock)
088 color = Qt :: green;
089
090 else if ( ! blocks [ i ][ j ])
091 color = Qt :: gray;
092 else
093 color = Qt :: white;
094 painter . fillRect( QRect( BLOCKSIZE * i , BLOCKSIZE * j ,
095 BLOCKSIZE , BLOCKSIZE ), color);
096 }
097
098 painter . setPen( QPen( Qt :: gray));
099 /* draw the grids */
100 for ( int i = 0; i < LENGTH; i += BLOCKSIZE)
101 painter . drawLine( i , 0 ,
102 i , WIDTH);
103 for ( int j = 0; j <= WIDTH; j += BLOCKSIZE)
104 painter . drawLine( 0 , j ,
105 LENGTH , j);
106
107 painter . setFont( QFont( "Arial" , 50));
108 painter . drawText( rect (), Qt :: AlignCenter , "path-finding demo");
109
110 QImage * image = new QImage;
111 image -> load( ":/image/layer1.png");
112 /*
113 qDebug("imagesize:\t%d,%d", image->size().width(),
114 image->size().height());
115 */
116 * image = image -> scaled( 330 , 100);
117
118
119 //QImage fixedImage(image->size(), QImage::Format_ARGB32_Premultiplied);
120
121 QPainter imagePainter( this);
122 //imagePainter.setCompositionMode(QPainter::CompositionMode_Source);
123 imagePainter . setCompositionMode( QPainter :: CompositionMode_SourceOver);
124 imagePainter . drawImage( helpImagePos , * image);
125
126
127 QPainter textPainter( this);
128 QFont font( "Arial" , 8);
129 font . setWeight( QFont :: Bold);
130 //font.setStretch(1);
131 QString string [] =
132 {
133 "Drag Green and Red block to set source and target" ,
134 "Hold down mouse button to draw maze" ,
135 "Press <W> and <S> to select algorithm" ,
136 "Press <A> and <D> to set demo speed" ,
137 "Press <SPACE> to start or stop demo" ,
138 "Press <R> to reset"
139 };
140
141 textPainter . setFont( font);
142 textPainter . setPen( QPen( Qt :: white));
143 for ( int i = 0; i < 6; ++ i)
144 textPainter . drawText( 10 , 20 + MES_Y_OFFSET * i , string [ i ]);
145
146 QString controlstr [] =
147 {
148 "A* (Manhattan)" ,
149 "A* (Euclidean)" ,
150 "A* (Chebyshev)" ,
151 "Dijkstra" ,
152 "Bi-Directional BFS" ,
153 };
154 image -> load( ":/image/layer1.png");
155 imagePainter . drawImage( controlImagePos , * image);
156 for ( int i = 0; i < 5; ++ i)
157 {
158 if ( i == controlSelection)
159 textPainter . setPen( QPen( Qt :: red));
160 else
161 textPainter . setPen( QPen( Qt :: white));
162 textPainter . drawText( 795 , 20 + MES_Y_OFFSET * i , controlstr [ i ]);
163 }
164 textPainter . drawText( 795 , 20 + MES_Y_OFFSET * 5 ,
165 QString( "Speed:") + QString :: number( 8 * speed) + QString( "X"));
166
167
168 /*
169 qDebug("helpimagepos:%d,%d\nmes:%d,%d",helpImagePos.x(),
170 helpImagePos.y(),
171 helpMessagePos.x(),
172 helpMessagePos.y());
173 */
174 /*
175 painter.drawText(rect(),Qt::AlignRight &Qt::AlignTop,
176 controlMessage[1].GetString());
177 */
178
179
180 }
181
182 /* 1. check the cursor's position,if on the start blocks,go to 2,
183 * else go to 3;
184 * 2. drag the start block,set onDrag=true
185 * 3. just draw the current block
186 */
187 void Widget :: mousePressEvent( QMouseEvent * event)
188 {
189 cursorpos = QPoint( event -> pos (). x() / BLOCKSIZE ,
190 event -> pos (). y() / BLOCKSIZE);
191 if ( cursorpos == startBlock)
192 onDrag = true;
193 else
194 {
195 setWindowPixel( event -> pos (), false);
196 onDrag = false;
197 }
198 }
199
200 void Widget :: mouseMoveEvent( QMouseEvent * event)
201 {
202 /*
203 * check the cursor's position
204 * if out of the main window
205 * the application will faile
206 */
207 if ( checkPosition( event -> pos()))
208 {
209 QPoint tmppos = QPoint( event -> pos (). x() / BLOCKSIZE ,
210 event -> pos (). y() / BLOCKSIZE);
211 if ( cursorpos != tmppos &&
212 ( event -> buttons() & Qt :: LeftButton) ||
213 ( event -> buttons() & Qt :: RightButton))
214 // event->buttons() & Qt::RightButton)
215 {
216 //qDebug("Time elapsed: %d ms", time.msecsTo(curTime));
217 //setWindowPixel(event->pos(), true);
218 /* is on drag ,use this to change the start
219 * block's position,clear the old pos
220 * then take the new pos
221 */
222 if ( onDrag)
223 {
224 //qDebug("tmppos:%d,%d", tmppos.x(), tmppos.y());
225 blocks [ startBlock . x ()][ startBlock . y ()] = true;
226 startBlock = tmppos;
227 blocks [ tmppos . x ()][ tmppos . y ()] = false;
228 }
229 setWindowPixel( event -> pos (), onDrag);
230 }
231 }
232 //cursorpos=tmppos;
233 }
234
235 void Widget :: setWindowPixel( const QPoint & pos , bool opaque)
236 {
237 int x = pos . x() / BLOCKSIZE;
238 int y = pos . y() / BLOCKSIZE;
239 cursorpos = QPoint( x , y);
240
241 if ( ! opaque)
242 blocks [ x ][ y ] = ! blocks [ x ][ y ];
243 update();
244 }
245
246 bool Widget :: checkPosition( const QPoint & pos)
247 {
248 QRect rect( 0 , 0 , LENGTH , WIDTH);
249 return rect . contains( pos);
250 }
251
252 void Widget :: keyPressEvent( QKeyEvent * event)
253 {
254 int keyPressed = toupper( event -> key());
255 switch ( keyPressed)
256 {
257 case 'W' :
258 if ( -- controlSelection < 0)
259 controlSelection = 4;
260 break;
261 case 'S' :
262 if ( ++ controlSelection > 4)
263 controlSelection = 0;
264 break;
265 case 'A' :
266 if ( -- speed < 0)
267 speed = 0;
268 break;
269 case 'D' :
270 if ( ++ speed > 5)
271 speed = 5;
272 break;
273 case 'R' :
274 for ( int i = 0; i < LENGTH / BLOCKSIZE; ++ i)
275 for ( int j = 0; j < WIDTH / BLOCKSIZE; ++ j)
276 blocks [ i ][ j ] = true;
277 blocks [ startBlock . x ()][ startBlock . y ()] = false;
278 blocks [ endBlock . x ()][ endBlock . y ()] = false;
279 break;
280 case ' ' :
281 break;
282 default :
283 return ;
284
285 }
286
287 if ( keyPressed == 'W' || keyPressed == 'S' ||
288 keyPressed == 'A' || keyPressed == 'D' ||
289 keyPressed == 'R' || keyPressed == ' ')
290 {
291 qDebug( "keypressed: %c;sel: %d" , keyPressed , controlSelection);
292 repaint();
293 }
294 }
002
003 Widget :: Widget( QWidget * parent)
004 : QWidget( parent)
005 {
006 for ( int i = 0; i < LENGTH / BLOCKSIZE; ++ i)
007 for ( int j = 0; j < WIDTH / BLOCKSIZE; ++ j)
008 blocks [ i ][ j ] = true;
009 startBlock = QPoint( 0 , 0);
010 blocks [ 0 ][ 0 ] = false;
011
012 endBlock = QPoint( 10 , 10);
013 blocks [ 10 ][ 10 ] = false;
014 onDrag = false;
015
016 controlMessage [ 0 ] = Control( QString( "Dijkstra" ),
017 QColor( Qt :: darkGreen));
018 controlMessage [ 1 ] = Control( QString( "Bi-Directional BFS" ),
019 QColor( Qt :: darkGreen));
020
021
022 helpImagePos = QPoint( 5 , 5);
023 helpMessagePos = QPoint( 10 , 10);
024 controlImagePos = QPoint( 790 , 5);
025 controlMessagePos = QPoint( 795 , 10);
026 controlSelection = 0;
027 speed = 1;
028
029 setAttribute( Qt :: WA_StaticContents);
030 setSizePolicy( QSizePolicy :: Minimum , QSizePolicy :: Minimum);
031 this -> setMouseTracking( true);
032 this -> setFixedSize( LENGTH , WIDTH);
033
034 /*
035 QImage* image=new QImage;
036 image->load(":/image/layer2.png");
037
038 QImage fixedImage(image->size(), QImage::Format_ARGB32_Premultiplied);
039 QPainter imagePainter(&fixedImage);
040 imagePainter.setCompositionMode(QPainter::CompositionMode_Source);
041 imagePainter.fillRect(fixedImage.rect(), Qt::transparent);
042 imagePainter.setCompositionMode(QPainter::CompositionMode_SourceOver);
043 imagePainter.drawImage(QPoint(10,10), *image);
044 imagePainter.end();
045 */
046
047 //QImage pic(":/image/test.jpg");
048 /*
049 I try to use QLabel to show the help message
050 but failed,picture and text can't show at the
051 same time
052 label->setPixmap(QPixmap::convertFromImage(pic));
053 */
054 /*
055 button->setText(controlMessage[0].GetString() + "\n" +
056 controlMessage[1].GetString());
057 label->setPixmap(QPixmap::fromImage(pic));
058 label->show();
059 */
060 /*
061 QHBoxLayout* layout = new QHBoxLayout;
062 layout->addWidget(label);
063 this->setLayout(layout);
064 */
065 }
066
067 void Widget :: paintEvent( QPaintEvent * event)
068 {
069 QPainter painter( this);
070 painter . setOpacity( 0.8);
071 QColor color = Qt :: red;
072
073 /* draw the start block */
074 /*
075 painter.fillRect(QRect(BLOCKSIZE*startBlock.x(), BLOCKSIZE*startBlock.y(),
076 BLOCKSIZE, BLOCKSIZE), color);
077 */
078
079 //qDebug("The startblock pos:%d,%d", startBlock.x(), startBlock.y());
080 /* draw the simple blocks */
081 for ( int i = 0; i < LENGTH / BLOCKSIZE; ++ i)
082 for ( int j = 0; j < WIDTH / BLOCKSIZE; ++ j)
083 {
084 QPoint cur( i , j);
085 if ( cur == startBlock)
086 color = Qt :: red;
087 else if ( cur == endBlock)
088 color = Qt :: green;
089
090 else if ( ! blocks [ i ][ j ])
091 color = Qt :: gray;
092 else
093 color = Qt :: white;
094 painter . fillRect( QRect( BLOCKSIZE * i , BLOCKSIZE * j ,
095 BLOCKSIZE , BLOCKSIZE ), color);
096 }
097
098 painter . setPen( QPen( Qt :: gray));
099 /* draw the grids */
100 for ( int i = 0; i < LENGTH; i += BLOCKSIZE)
101 painter . drawLine( i , 0 ,
102 i , WIDTH);
103 for ( int j = 0; j <= WIDTH; j += BLOCKSIZE)
104 painter . drawLine( 0 , j ,
105 LENGTH , j);
106
107 painter . setFont( QFont( "Arial" , 50));
108 painter . drawText( rect (), Qt :: AlignCenter , "path-finding demo");
109
110 QImage * image = new QImage;
111 image -> load( ":/image/layer1.png");
112 /*
113 qDebug("imagesize:\t%d,%d", image->size().width(),
114 image->size().height());
115 */
116 * image = image -> scaled( 330 , 100);
117
118
119 //QImage fixedImage(image->size(), QImage::Format_ARGB32_Premultiplied);
120
121 QPainter imagePainter( this);
122 //imagePainter.setCompositionMode(QPainter::CompositionMode_Source);
123 imagePainter . setCompositionMode( QPainter :: CompositionMode_SourceOver);
124 imagePainter . drawImage( helpImagePos , * image);
125
126
127 QPainter textPainter( this);
128 QFont font( "Arial" , 8);
129 font . setWeight( QFont :: Bold);
130 //font.setStretch(1);
131 QString string [] =
132 {
133 "Drag Green and Red block to set source and target" ,
134 "Hold down mouse button to draw maze" ,
135 "Press <W> and <S> to select algorithm" ,
136 "Press <A> and <D> to set demo speed" ,
137 "Press <SPACE> to start or stop demo" ,
138 "Press <R> to reset"
139 };
140
141 textPainter . setFont( font);
142 textPainter . setPen( QPen( Qt :: white));
143 for ( int i = 0; i < 6; ++ i)
144 textPainter . drawText( 10 , 20 + MES_Y_OFFSET * i , string [ i ]);
145
146 QString controlstr [] =
147 {
148 "A* (Manhattan)" ,
149 "A* (Euclidean)" ,
150 "A* (Chebyshev)" ,
151 "Dijkstra" ,
152 "Bi-Directional BFS" ,
153 };
154 image -> load( ":/image/layer1.png");
155 imagePainter . drawImage( controlImagePos , * image);
156 for ( int i = 0; i < 5; ++ i)
157 {
158 if ( i == controlSelection)
159 textPainter . setPen( QPen( Qt :: red));
160 else
161 textPainter . setPen( QPen( Qt :: white));
162 textPainter . drawText( 795 , 20 + MES_Y_OFFSET * i , controlstr [ i ]);
163 }
164 textPainter . drawText( 795 , 20 + MES_Y_OFFSET * 5 ,
165 QString( "Speed:") + QString :: number( 8 * speed) + QString( "X"));
166
167
168 /*
169 qDebug("helpimagepos:%d,%d\nmes:%d,%d",helpImagePos.x(),
170 helpImagePos.y(),
171 helpMessagePos.x(),
172 helpMessagePos.y());
173 */
174 /*
175 painter.drawText(rect(),Qt::AlignRight &Qt::AlignTop,
176 controlMessage[1].GetString());
177 */
178
179
180 }
181
182 /* 1. check the cursor's position,if on the start blocks,go to 2,
183 * else go to 3;
184 * 2. drag the start block,set onDrag=true
185 * 3. just draw the current block
186 */
187 void Widget :: mousePressEvent( QMouseEvent * event)
188 {
189 cursorpos = QPoint( event -> pos (). x() / BLOCKSIZE ,
190 event -> pos (). y() / BLOCKSIZE);
191 if ( cursorpos == startBlock)
192 onDrag = true;
193 else
194 {
195 setWindowPixel( event -> pos (), false);
196 onDrag = false;
197 }
198 }
199
200 void Widget :: mouseMoveEvent( QMouseEvent * event)
201 {
202 /*
203 * check the cursor's position
204 * if out of the main window
205 * the application will faile
206 */
207 if ( checkPosition( event -> pos()))
208 {
209 QPoint tmppos = QPoint( event -> pos (). x() / BLOCKSIZE ,
210 event -> pos (). y() / BLOCKSIZE);
211 if ( cursorpos != tmppos &&
212 ( event -> buttons() & Qt :: LeftButton) ||
213 ( event -> buttons() & Qt :: RightButton))
214 // event->buttons() & Qt::RightButton)
215 {
216 //qDebug("Time elapsed: %d ms", time.msecsTo(curTime));
217 //setWindowPixel(event->pos(), true);
218 /* is on drag ,use this to change the start
219 * block's position,clear the old pos
220 * then take the new pos
221 */
222 if ( onDrag)
223 {
224 //qDebug("tmppos:%d,%d", tmppos.x(), tmppos.y());
225 blocks [ startBlock . x ()][ startBlock . y ()] = true;
226 startBlock = tmppos;
227 blocks [ tmppos . x ()][ tmppos . y ()] = false;
228 }
229 setWindowPixel( event -> pos (), onDrag);
230 }
231 }
232 //cursorpos=tmppos;
233 }
234
235 void Widget :: setWindowPixel( const QPoint & pos , bool opaque)
236 {
237 int x = pos . x() / BLOCKSIZE;
238 int y = pos . y() / BLOCKSIZE;
239 cursorpos = QPoint( x , y);
240
241 if ( ! opaque)
242 blocks [ x ][ y ] = ! blocks [ x ][ y ];
243 update();
244 }
245
246 bool Widget :: checkPosition( const QPoint & pos)
247 {
248 QRect rect( 0 , 0 , LENGTH , WIDTH);
249 return rect . contains( pos);
250 }
251
252 void Widget :: keyPressEvent( QKeyEvent * event)
253 {
254 int keyPressed = toupper( event -> key());
255 switch ( keyPressed)
256 {
257 case 'W' :
258 if ( -- controlSelection < 0)
259 controlSelection = 4;
260 break;
261 case 'S' :
262 if ( ++ controlSelection > 4)
263 controlSelection = 0;
264 break;
265 case 'A' :
266 if ( -- speed < 0)
267 speed = 0;
268 break;
269 case 'D' :
270 if ( ++ speed > 5)
271 speed = 5;
272 break;
273 case 'R' :
274 for ( int i = 0; i < LENGTH / BLOCKSIZE; ++ i)
275 for ( int j = 0; j < WIDTH / BLOCKSIZE; ++ j)
276 blocks [ i ][ j ] = true;
277 blocks [ startBlock . x ()][ startBlock . y ()] = false;
278 blocks [ endBlock . x ()][ endBlock . y ()] = false;
279 break;
280 case ' ' :
281 break;
282 default :
283 return ;
284
285 }
286
287 if ( keyPressed == 'W' || keyPressed == 'S' ||
288 keyPressed == 'A' || keyPressed == 'D' ||
289 keyPressed == 'R' || keyPressed == ' ')
290 {
291 qDebug( "keypressed: %c;sel: %d" , keyPressed , controlSelection);
292 repaint();
293 }
294 }