00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020 #include "config.h"
00021 #include "arena.h"
00022
00023 #include "core.h"
00024 #include "coresync.h"
00025 #include "physics.h"
00026 #include "robIO.h"
00027 #include "trilobot.h"
00028
00029 #ifndef BUILDING_DOX
00030 # include <assert.h>
00031 # include <QPainter>
00032 # include <QTimer>
00033 #endif
00034
00035 using std::string;
00036 using namespace StreamDecorator;
00037
00038 static const int dtRepaint = 50;
00039
00040 static const qreal botPathGranulatiry = 0.1;
00041
00042 static const qreal botPenWidth = 2.0;
00043 static const qreal objPenWidth = 1.0;
00044 static const qreal envPenWidth = 3.0;
00045
00046
00047
00048 struct Arena::Private {
00049 Arena *arena;
00050 CoreSync *cs;
00051 QTimer timer;
00052 unsigned ticks;
00053 QSizeF envSize;
00054 qreal botSize;
00055 qreal zoomRatio;
00056 Position botPosition;
00057 QPointF botLastPosition;
00058 BotPath botPath;
00059 CoreInfo coreInfo;
00060 bool drawSurface;
00061
00062 void render(QPainter &, LockedCore &);
00063 void renderBot(QPainter &, LockedCore &);
00064 QPointF cToA(Point);
00065 QPointF botPosF();
00066 QPoint botPosPx();
00067 void drawCircle(QPainter &, const IObject &);
00068 };
00069 Arena::Arena(CoreSync *cs, QWidget *parent):
00070 QWidget(parent),
00071 d(new Private)
00072 {
00073 QWidget::setBackgroundRole(QPalette::Light);
00074
00075
00076 d->arena = this;
00077 d->cs = cs;
00078 d->ticks = 0;
00079 d->drawSurface = false;
00080
00081
00082 LockedCore core(cs);
00083
00084
00085 const Point &rightTop = core->GetEnviroment().RightTop();
00086 d->envSize.setWidth(rightTop.x);
00087 d->envSize.setHeight(rightTop.y);
00088 assert(d->envSize.isValid());
00089 this->zoom(1.0);
00090
00091
00092 d->botPosition = core->GetTrilobot().Position();
00093 d->botLastPosition = d->botPosF();
00094 d->botPath.addPos(d->botLastPosition);
00095 d->botSize = core->GetTrilobot().Size();
00096 assert(0.0 < d->botSize);
00097 readCoreInfo(core, d->coreInfo);
00098
00099
00100 QTimer &timer = d->timer;
00101 connect(&timer, SIGNAL(timeout()), this, SLOT(animate()));
00102 timer.start(dtRepaint);
00103 }
00104 Arena::~Arena() {
00105 d->timer.stop();
00106 delete d;
00107 }
00108 QSize Arena::envSize() const {
00109 QSize size(
00110 static_cast<int>(d->envSize.width()),
00111 static_cast<int>(d->envSize.height()));
00112 assert(size.isValid());
00113 return size;
00114 }
00115 int Arena::botSize() const {
00116 return static_cast<int>(d->botSize * d->zoomRatio);
00117 }
00118 const CoreInfo& Arena::coreInfo() const {
00119 return d->coreInfo;
00120 }
00121 void Arena::animate() {
00122 d->ticks ++;
00123 this->repaint();
00124 }
00125 void Arena::zoom(qreal ratio) {
00126 assert(0.0 < ratio);
00127 d->zoomRatio = ratio;
00128
00129
00130 QSizeF size(d->envSize);
00131 size += QSizeF(envPenWidth*2.0, envPenWidth*2.0);
00132 size *= ratio;
00133
00134
00135 QSize pxSize(
00136 static_cast<int>(size.width()),
00137 static_cast<int>(size.height())
00138 );
00139 assert(pxSize.isValid());
00140 this->setFixedSize(pxSize);
00141
00142
00143 emit preferredPosChanged(d->botPosPx());
00144 }
00145 void Arena::setShrinkPathEnabled(bool enabled) {
00146 d->botPath.setShrinkEnabled(enabled);
00147
00148 }
00149 void Arena::setDrawSurfaceEnabled(bool enabled) {
00150 d->drawSurface = enabled;
00151
00152 }
00153 void Arena::paintEvent(QPaintEvent *) {
00154
00155 LockedCore core(d->cs);
00156 d->botPosition = core->GetTrilobot().Position();
00157 readCoreInfo(core, d->coreInfo);
00158
00159
00160 QPainter painter;
00161 painter.begin(this);
00162 if (d->zoomRatio < 3.0)
00163 painter.setRenderHints(QPainter::Antialiasing|QPainter::SmoothPixmapTransform);
00164 else
00165 painter.setRenderHint(QPainter::Antialiasing);
00166
00167
00168 QMatrix matrix;
00169 matrix.translate(0, this->size().height());
00170 matrix.scale(d->zoomRatio, -d->zoomRatio);
00171 painter.setMatrix(matrix);
00172
00173
00174 d->render(painter, core);
00175
00176
00177 painter.end();
00178
00179 if (d->botLastPosition != d->botPosF()) {
00180 d->botLastPosition = d->botPosF();
00181
00182
00183 QTimer::singleShot(0, this, SLOT(botPosChanged()));
00184 }
00185 }
00186 void Arena::botPosChanged() {
00187 d->botPath.addPos(d->botLastPosition);
00188 emit coreInfoChanged();
00189 emit preferredPosChanged(d->botPosPx());
00190 }
00191 void Arena::Private::render(QPainter &painter, LockedCore &core) {
00192
00193 const qreal envOffset = envPenWidth/2.0;
00194 QRectF envRect(envOffset, envOffset,
00195 envSize.width() + envPenWidth,
00196 envSize.height() + envPenWidth);
00197 QPen envPen(QBrush(Qt::black), envPenWidth, Qt::SolidLine, Qt::SquareCap, Qt::MiterJoin);
00198 painter.setPen(envPen);
00199 painter.drawRect(envRect);
00200
00201
00202 QMatrix matrix;
00203 matrix.translate(envPenWidth, envPenWidth);
00204 painter.setMatrix(matrix, true);
00205
00206
00207 const IObjectContainer &objs = core->GetEnviroment().Objects();
00208 for(unsigned i=0; i<objs.size(); i++)
00209 drawCircle(painter, *(objs[i]));
00210
00211
00212 botPath.drawPath(painter);
00213
00214
00215 QMatrix botMatrix;
00216 QPointF botPos(cToA(botPosition));
00217 botMatrix.translate(botPos.x(), botPos.y());
00218 botMatrix.rotate(- botPosition.angle);
00219
00220
00221 QMatrix mSaved(painter.matrix());
00222 painter.setMatrix(botMatrix, true);
00223 renderBot(painter, core);
00224 painter.setMatrix(mSaved);
00225 }
00226 void Arena::Private::renderBot(QPainter &painter, LockedCore &core) {
00227 Trilobot &bot = core->GetTrilobot();
00228 const qreal size = bot.Size() - botPenWidth/2.0;
00229
00230
00231 const QColor fgColor("#444");
00232 const QColor bgColor("#CCC");
00233 QRadialGradient gradient(QPointF(), bot.Size());
00234 gradient.setColorAt(0, bgColor);
00235 gradient.setColorAt(1, fgColor);
00236
00237
00238 QBrush brush(gradient);
00239 if (!(ticks%16))
00240 brush = QBrush(Qt::green);
00241 QPen botPen(brush, botPenWidth, Qt::SolidLine, Qt::RoundCap);
00242
00243
00244 painter.setPen(botPen);
00245 painter.setBrush(QBrush());
00246 painter.drawEllipse(QPointF(), size, size);
00247 painter.drawLine(QPointF(0, 0), QPointF(0, size));
00248 }
00249 QPointF Arena::Private::cToA(Point p) {
00250 return QPointF(p.x, p.y);
00251 }
00252 void Arena::Private::drawCircle(QPainter &painter, const IObject &cObj) {
00253
00254 IObject &obj = const_cast<IObject &>(cObj);
00255 const qreal size = obj.Size() - objPenWidth/2.0;
00256 const QPointF center = cToA(obj.Position());
00257
00258
00259 QMatrix matrix;
00260 matrix.translate(center.x(), center.y());
00261 matrix.scale(size, size);
00262
00263
00264 const QColor color("#C00");
00265 QBrush brush;
00266 if (drawSurface) {
00267 brush = QBrush(color, Qt::HorPattern);
00268 QMatrix brushMatrix(matrix.inverted());
00269 brushMatrix.scale(0.5, 0.5);
00270 brush.setMatrix(brushMatrix);
00271 }
00272 painter.setPen(QPen(color, objPenWidth/size));
00273
00274
00275 QMatrix mSaved(painter.matrix());
00276 matrix.rotate(-45);
00277 painter.setMatrix(matrix, true);
00278 painter.setBrush(QBrush(Qt::white));
00279 painter.drawEllipse(QPointF(), 1.0, 1.0);
00280 if (drawSurface) {
00281 painter.setBrush(brush);
00282 painter.drawEllipse(QPointF(), 1.0, 1.0);
00283 }
00284 painter.setMatrix(mSaved);
00285 }
00286 QPointF Arena::Private::botPosF() {
00287 return cToA(botPosition);
00288 }
00289 QPoint Arena::Private::botPosPx() {
00290 QMatrix matrix;
00291 matrix.translate(0, arena->size().height());
00292 matrix.scale(zoomRatio, -zoomRatio);
00293 matrix.translate(envPenWidth, envPenWidth);
00294
00295 QPointF pos(matrix.map(botLastPosition));
00296 return QPoint(
00297 static_cast<int>(pos.x()),
00298 static_cast<int>(pos.y()));
00299 }
00300
00301
00302
00303 struct BotPath::Private {
00304 bool first;
00305 bool shrinkEnabled;
00306 QPointF last;
00307 QPainterPath path;
00308 QPainterPath fullPath;
00309
00310 bool farFromLast(QPointF);
00311 void shrink();
00312 };
00313 BotPath::BotPath():
00314 d(new Private)
00315 {
00316 d->first = true;
00317 d->shrinkEnabled = true;
00318 }
00319 BotPath::~BotPath() {
00320 delete d;
00321 }
00322 void BotPath::addPos(QPointF pos) {
00323 if (d->first) {
00324 d->first = false;
00325 d->last = pos;
00326 d->path.moveTo(pos);
00327 d->fullPath.moveTo(pos);
00328 return;
00329 }
00330 if (d->farFromLast(pos)) {
00331 d->last = pos;
00332 d->path.lineTo(pos);
00333 d->fullPath.lineTo(pos);
00334 if (256 <= d->path.elementCount())
00335 d->shrink();
00336 }
00337 }
00338 void BotPath::drawPath(QPainter &painter) {
00339 QPen pen(Qt::black, 0.5, Qt::DotLine);
00340 painter.setPen(pen);
00341 painter.setBrush(QBrush());
00342 painter.drawPath(d->shrinkEnabled
00343 ? d->path
00344 : d->fullPath
00345 );
00346 }
00347 void BotPath::setShrinkEnabled(bool enabled) {
00348 d->shrinkEnabled = enabled;
00349 }
00350 bool BotPath::Private::farFromLast(QPointF pos) {
00351 const qreal dx = last.x() - pos.x();
00352 const qreal dy = last.y() - pos.y();
00353 const qreal squareOfDist = dx*dx + dy*dy;
00354 return botPathGranulatiry < squareOfDist;
00355 }
00356 void BotPath::Private::shrink() {
00357 #if DEBUG_SHRINK_PATH
00358 std::cerr << "BotPath: "
00359 << Color(C_YELLOW) << "shrinking path"
00360 << Color(C_NO_COLOR) << ": " << path.elementCount() << " -> "
00361 << std::flush;
00362 #endif
00363 QPainterPath shrinked;
00364 shrinked.moveTo(path.elementAt(4));
00365
00366 const unsigned size = path.elementCount();
00367 for (unsigned i = 5; i < size; i++) {
00368 QPointF current(path.elementAt(i));
00369 if (!(i % 64))
00370 continue;
00371 shrinked.lineTo(current);
00372 }
00373
00374 path = shrinked;
00375 #if DEBUG_SHRINK_PATH
00376 std::cerr << path.elementCount() << std::endl;
00377 #endif
00378 }