1 /****************************************************************************
2 
3  Copyright (C) 2002-2014 Gilles Debunne. All rights reserved.
4 
5  This file is part of the QGLViewer library version 2.7.2.
6 
7  http://www.libqglviewer.com - contact@libqglviewer.com
8 
9  This file may be used under the terms of the GNU General Public License
10  versions 2.0 or 3.0 as published by the Free Software Foundation and
11  appearing in the LICENSE file included in the packaging of this file.
12  In addition, as a special exception, Gilles Debunne gives you certain
13  additional rights, described in the file GPL_EXCEPTION in this package.
14 
15  libQGLViewer uses dual licensing. Commercial/proprietary software must
16  purchase a libQGLViewer Commercial License.
17 
18  This file is provided AS IS with NO WARRANTY OF ANY KIND, INCLUDING THE
19  WARRANTY OF DESIGN, MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE.
20 
21 *****************************************************************************/
22 
23 #include "drawer.h"
24 #include <QGLViewer/qglviewer.h>
25 #include <qimage.h>
26 #include <qmessagebox.h>
27 #include <qstringlist.h>
28 
29 using namespace std;
30 using namespace dvonn;
31 using namespace qglviewer;
32 
33 namespace {
34 static const float whiteColor[3] = {1.0f, 1.0f, 1.0f};
35 static const float blackColor[3] = {0.2f, 0.2f, 0.2f};
36 static const float redColor[3] = {1.0f, 0.0f, 0.0f};
37 static const float boardColor[3] = {1.0f, 1.0f, 1.0f};
38 static const float boardBorderColor[3] = {0.0f, 0.0f, 0.0f};
39 // Dimension (D=widht=height) of a case and of the inner border of the
40 // case (B)
41 const float caseD = 1.0f;
42 const float caseB = 0.2f;
43 // Dimension of a piece
44 const float pieceRMax = (caseD - 2.0f * caseB) / sqrtf(2.0f);
45 const float pieceRMin = pieceRMax / 3.0f;
46 const float pieceH = 0.14f;
47 const float pieceC = 0.25f; // curvature of normal on vring part
48 // Dimension of the label case
49 float hLabelW = caseD; // must be caseD
50 float hLabelH = caseD / 3.0f;
51 float vLabelW = caseD / 3.0f;
52 float vLabelH = caseD; // must be caseD
53 // Dimension of the board
54 const float boardB = 0.1f;
55 const float boardW =
56     Board::nbSpacesMaxOnRow() * caseD + 2 * boardB + 2 * vLabelW;
57 const float boardH = Board::nbRows() * caseD + 2 * boardB + 2 * hLabelH;
58 const float poolB = 0.1f;
tessAndTexture(float x0,float y0,float x2,float y2,float u0=0.0f,float v0=0.0f,float u2=1.0f,float v2=1.0f)59 void tessAndTexture(float x0, float y0, float x2, float y2, float u0 = 0.0f,
60                     float v0 = 0.0f, float u2 = 1.0f, float v2 = 1.0f) {
61   if (x0 > x2)
62     swap(x0, x2);
63   if (y0 > y2)
64     swap(y0, y2);
65   if (x2 - x0 > caseD) {
66     float x1 = (x0 + x2) / 2.0f;
67     float u1 = (u0 + u2) / 2.0f;
68     tessAndTexture(x0, y0, x1, y2, u0, v0, u1, v2);
69     tessAndTexture(x1, y0, x2, y2, u1, v0, u2, v2);
70   } else if (y2 - y0 > caseD) {
71     float y1 = (y0 + y2) / 2.0f;
72     float v1 = (v0 + v2) / 2.0f;
73     tessAndTexture(x0, y0, x2, y1, u0, v0, u2, v1);
74     tessAndTexture(x0, y1, x2, y2, u0, v1, u2, v2);
75   } else {
76     glMultiTexCoord2f(GL_TEXTURE0, x0 / caseD, y0 / caseD);
77     glMultiTexCoord2f(GL_TEXTURE1, u0, v0);
78     glVertex2f(x0, y0);
79     glMultiTexCoord2f(GL_TEXTURE0, x2 / caseD, y0 / caseD);
80     glMultiTexCoord2f(GL_TEXTURE1, u2, v0);
81     glVertex2f(x2, y0);
82     glMultiTexCoord2f(GL_TEXTURE0, x2 / caseD, y2 / caseD);
83     glMultiTexCoord2f(GL_TEXTURE1, u2, v2);
84     glVertex2f(x2, y2);
85     glMultiTexCoord2f(GL_TEXTURE0, x0 / caseD, y2 / caseD);
86     glMultiTexCoord2f(GL_TEXTURE1, u0, v2);
87     glVertex2f(x0, y2);
88   }
89 }
90 const Vec normal[5] = {Vec(-1.0, 0.0, 0.0), Vec(0.0, 1.0, 0.0),
91                        Vec(1.0, 0.0, 0.0), Vec(0.0, -1.0, 0.0),
92                        Vec(-1.0, 0.0, 0.0)};
drawHRing(const float rMin,const float rMax,const float height)93 void drawHRing(const float rMin, const float rMax, const float height) {
94   // TODO : tabulate the cos and sin tables
95   const int nbSteps = 24;
96 
97   glNormal3f(0.0, 0.0, 1.0);
98   glBegin(GL_QUAD_STRIP);
99   glVertex3f(rMin, 0.0, height);
100   glVertex3f(rMax, 0.0, height);
101   for (int i = 1; i <= nbSteps; ++i) {
102     const float angle = i * 2.0 * M_PI / nbSteps;
103     const float cosine = cos(angle);
104     const float sine = sin(angle);
105     glVertex3f(rMin * cosine, rMin * sine, height);
106     glVertex3f(rMax * cosine, rMax * sine, height);
107   }
108   glEnd();
109 }
drawVRing(const float hMin,const float hMax,const float r,bool in)110 void drawVRing(const float hMin, const float hMax, const float r, bool in) {
111   const int nbSteps = 24;
112   const float normalSign = (in ? -1.0 : 1.0);
113   float thMin = hMin;
114   float thMax = hMax;
115   if (in)
116     swap(thMax, thMin);
117   glBegin(GL_QUAD_STRIP);
118   glNormal3f(normalSign, 0.0, 0.0);
119   for (int i = 0; i <= nbSteps; ++i) {
120     const float angle = i * 2.0 * M_PI / nbSteps;
121     const float cosine = cos(angle);
122     const float sine = sin(angle);
123     glNormal3fv(Vec(normalSign * cosine, normalSign * sine, +pieceC).unit());
124     glVertex3f(r * cosine, r * sine, thMax);
125     glNormal3fv(Vec(normalSign * cosine, normalSign * sine, -pieceC).unit());
126     glVertex3f(r * cosine, r * sine, thMin);
127   }
128   glEnd();
129 }
drawPiece(const Color & p,float a=1.0f)130 void drawPiece(const Color &p, float a = 1.0f) {
131   static const float *colors[3] = {redColor, whiteColor, blackColor};
132   const float *c = colors[static_cast<int>(p)];
133   glColor4f(c[0] * a, c[1] * a, c[2] * a, a);
134   drawHRing(pieceRMin, pieceRMax, pieceH);
135   drawVRing(0.0f, pieceH, pieceRMax, false);
136   drawVRing(0.0f, pieceH, pieceRMin, true);
137 }
drawHLabel(unsigned int i)138 void drawHLabel(unsigned int i) {
139   glBegin(GL_QUADS);
140   unsigned int d = Board::nbRows() / 2;
141   if (i < Board::nbSpacesMaxOnRow() - d) // Bottom
142   {
143     float x = boardB + vLabelW + (d + 1) * (caseD / 2.0f) + i * hLabelW;
144     float y = boardB;
145     tessAndTexture(x, y, x + hLabelH, y + hLabelH, 0.0f, 0.0f, 1.0f, 1.0f);
146     tessAndTexture(x + hLabelH, y, x + hLabelW, y + hLabelH, 2.0f, 2.0f, 2.0f,
147                    2.0f);
148   }
149   if (i >= d) {
150     float x = boardB + vLabelW - (d + 1) * (caseD / 2.0f) + i * hLabelW;
151     float y = boardB + hLabelH + Board::nbRows() * caseD;
152     tessAndTexture(x, y, x + hLabelW - hLabelH, y + hLabelH, 2.0f, 2.0f, 2.0f,
153                    2.0f);
154     tessAndTexture(x + hLabelW - hLabelH, y, x + hLabelW, y + hLabelH, 0.0f,
155                    0.0f, 1.0f, 1.0f);
156   }
157   glEnd();
158 }
drawVLabel(int i)159 void drawVLabel(int i) {
160   glBegin(GL_QUADS);
161   int d = static_cast<int>(Board::nbRows() / 2);
162   int e = abs(d - i);
163   float shift = e * caseD / 2.0f;
164   float x = boardB + shift;
165   float y = boardB + hLabelH + i * vLabelH;
166   tessAndTexture(x, y, x + vLabelW, y + vLabelH / 3.0f, 2.0f, 2.0f, 2.0f, 2.0f);
167   tessAndTexture(x, y + vLabelH / 3.0f, x + vLabelW, y + 2.0f * vLabelH / 3.0f,
168                  0.0f, 0.0f, 1.0f, 1.0f);
169   tessAndTexture(x, y + 2.0f * vLabelH / 3.0f, x + vLabelW, y + vLabelH, 2.0f,
170                  2.0f, 2.0f, 2.0f);
171   x = boardB + shift + vLabelW + (Board::nbSpacesMaxOnRow() - e) * caseD;
172   y = boardB + hLabelH + i * vLabelH;
173   tessAndTexture(x, y, x + vLabelW, y + vLabelH / 3.0f, 2.0f, 2.0f, 2.0f, 2.0f);
174   tessAndTexture(x, y + vLabelH / 3.0f, x + vLabelW, y + 2.0f * vLabelH / 3.0f,
175                  0.0f, 0.0f, 1.0f, 1.0f);
176   tessAndTexture(x, y + 2.0f * vLabelH / 3.0f, x + vLabelW, y + vLabelH, 2.0f,
177                  2.0f, 2.0f, 2.0f);
178   glEnd();
179 }
loadTexture(const QString fileName)180 GLuint loadTexture(const QString fileName) {
181   QImage img("images/" + fileName);
182   if (img.isNull()) {
183     QMessageBox::critical(NULL, "Image not found",
184                           "Unable to load texture from " + fileName);
185     exit(1); // TODO: be nicer!
186   }
187   // 1E-3 needed. Just try with width=128 and see !
188   if ((img.width() != 1 << (int)(1 + log(img.width() - 1 + 1E-3) / log(2.0))) ||
189       (img.height() !=
190        1 << (int)(1 + log(img.height() - 1 + 1E-3) / log(2.0)))) {
191     QMessageBox::critical(NULL, "Wrong image dimensions",
192                           "Texture dimensions are not powers of 2 in " +
193                               fileName);
194     exit(1); // TODO: be nicer!
195   }
196   // cout << "Loaded "<<fileName<<endl;
197   QImage glImg = QGLWidget::convertToGLFormat(img); // flipped 32bit RGBA
198   // Create a texture object
199   GLuint to;
200   glGenTextures(1, &to);
201   glBindTexture(GL_TEXTURE_2D, to);
202   gluBuild2DMipmaps(GL_TEXTURE_2D, GL_RGBA, glImg.width(), glImg.height(),
203                     GL_RGBA, GL_UNSIGNED_BYTE, glImg.bits());
204   return to;
205 }
206 } // namespace
207 //************************************************************
208 // Implementation of Drawer
209 //************************************************************
Drawer()210 Drawer::Drawer() : showTextures_(true) {}
toggleTexture(bool b)211 void Drawer::toggleTexture(bool b) { showTextures_ = b; }
~Drawer()212 Drawer::~Drawer() {}
init()213 void Drawer::init() {
214   static const QStringList texFilenames = QStringList() << "board.png"
215                                                         << "case.png";
216   for (QStringList::const_iterator it = texFilenames.begin();
217        it != texFilenames.end(); ++it) {
218     textures_[*it] = loadTexture(*it);
219   }
220   for (unsigned int i = 0; i < Board::nbSpacesMaxOnRow(); ++i) {
221     static const char *letter = "ABCDEFGHIJK";
222     hLabels_.push_back(loadTexture(QString("label%1.png").arg(letter[i])));
223   }
224   for (unsigned int i = 0; i < Board::nbRows(); ++i) {
225     vLabels_.push_back(loadTexture(QString("label%1.png").arg(i + 1)));
226   }
227   glActiveTexture(GL_TEXTURE0);
228   glEnable(GL_TEXTURE_2D);
229   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
230   glActiveTexture(GL_TEXTURE1);
231   glEnable(GL_TEXTURE_2D);
232   glTexEnvi(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_MODULATE);
233 }
startTexture(const QString & t) const234 void Drawer::startTexture(const QString &t) const {
235   glPushAttrib(GL_ENABLE_BIT);
236   glActiveTexture(GL_TEXTURE0);
237   map<QString, GLuint>::const_iterator fter = textures_.find(t);
238   if (showTextures_ && fter != textures_.end()) {
239     glEnable(GL_TEXTURE_2D);
240     glBindTexture(GL_TEXTURE_2D, fter->second);
241     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
242     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
243   } else {
244     glDisable(GL_TEXTURE_2D);
245   }
246   glActiveTexture(GL_TEXTURE1);
247   glDisable(GL_TEXTURE_2D);
248 }
startTexture(const QString & t,GLuint to) const249 void Drawer::startTexture(const QString &t, GLuint to) const {
250   glPushAttrib(GL_ENABLE_BIT);
251   glActiveTexture(GL_TEXTURE0);
252   map<QString, GLuint>::const_iterator fter = textures_.find(t);
253   if (showTextures_ && fter != textures_.end()) {
254     glEnable(GL_TEXTURE_2D);
255     glBindTexture(GL_TEXTURE_2D, fter->second);
256     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT);
257     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT);
258   } else {
259     glDisable(GL_TEXTURE_2D);
260   }
261   glActiveTexture(GL_TEXTURE1);
262   if (showTextures_) {
263     glBindTexture(GL_TEXTURE_2D, to);
264     glEnable(GL_TEXTURE_2D);
265     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP);
266     glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP);
267   } else {
268     glDisable(GL_TEXTURE_2D);
269   }
270 }
startTexture() const271 void Drawer::startTexture() const {
272   glPushAttrib(GL_ENABLE_BIT);
273   glActiveTexture(GL_TEXTURE0);
274   glDisable(GL_TEXTURE_2D);
275   glActiveTexture(GL_TEXTURE1);
276   glDisable(GL_TEXTURE_2D);
277 }
endTexture() const278 void Drawer::endTexture() const { glPopAttrib(); }
translateTo(Board::Coord c,float h=0.0f)279 void translateTo(Board::Coord c, float h = 0.0f) {
280   static const float shifts[5] = {1.0f, 0.5f, 0.0f, -0.5f, -1.0f};
281   glTranslatef(boardB + vLabelW + c.x() * caseD + shifts[c.y()] * caseD,
282                boardB + hLabelH + c.y() * caseD, h);
283 }
drawPieces(const Board::ConstStackHandle & s) const284 void Drawer::drawPieces(const Board::ConstStackHandle &s) const {
285   startTexture();
286   glPushMatrix();
287   translateTo(s.stackCoord());
288   glTranslatef(0.5f * caseD, 0.5f * caseD, 0.0f);
289   for (Stack::const_iterator iter = s->begin(), istop = s->end(); iter != istop;
290        ++iter) {
291     drawPiece((*iter)->color());
292     glTranslatef(0.0f, 0.0f, pieceH);
293   }
294   glPopMatrix();
295   endTexture();
296 }
drawStatus(const Board::ConstStackHandle & s,QGLViewer * v) const297 void Drawer::drawStatus(const Board::ConstStackHandle &s, QGLViewer *v) const {
298   startTexture();
299   glPushMatrix();
300   translateTo(s.stackCoord(), s->height() * pieceH + pieceH / 2.0f);
301   glColor3f(1.0f, 1.0f, 0.0f);
302   v->renderText(0.5f * caseD, 0.5f * caseD, QString("%1").arg(s.stackStatus()),
303                 QFont());
304   glPopMatrix();
305   endTexture();
306 }
drawTransparentPiece(Color col,const Board::ConstStackHandle & c,float a) const307 void Drawer::drawTransparentPiece(Color col, const Board::ConstStackHandle &c,
308                                   float a) const {
309   startTexture();
310   glPushMatrix();
311   translateTo(c.stackCoord(), c->height() * pieceH);
312   glTranslatef(0.5f * caseD, 0.5f * caseD, 0.0f);
313   drawPiece(col, a);
314   glPopMatrix();
315   endTexture();
316 }
drawTransparentPieces(Stack::const_iterator first,Stack::const_iterator last,const Board::Coord & c,float h,float a) const317 void Drawer::drawTransparentPieces(Stack::const_iterator first,
318                                    Stack::const_iterator last,
319                                    const Board::Coord &c, float h,
320                                    float a) const {
321   startTexture();
322   glPushMatrix();
323   translateTo(c, h * pieceH);
324   glTranslatef(0.5f * caseD, 0.5f * caseD, 0.0f);
325   while (first != last) {
326     drawPiece((*first++)->color(), a);
327     glTranslatef(0.0f, 0.0f, pieceH);
328   }
329   glPopMatrix();
330   endTexture();
331 }
draw(const Board::ConstStackHandle & c) const332 void Drawer::draw(const Board::ConstStackHandle &c) const {
333   glPushMatrix();
334   translateTo(c.stackCoord());
335   startTexture("case.png");
336   glColor3fv(boardColor);
337   glNormal3fv(boardUpVector());
338   glBegin(GL_QUADS);
339   tessAndTexture(0.0f, 0.0f, caseD, caseD);
340   glEnd();
341   endTexture();
342   glPopMatrix();
343 }
drawComplement(bool showLabels) const344 void Drawer::drawComplement(bool showLabels) const {
345   glPushAttrib(GL_ALL_ATTRIB_BITS);
346   glNormal3fv(boardUpVector());
347   glColor3fv(boardColor);
348   for (unsigned int i = 0; i < Board::nbSpacesMaxOnRow(); ++i) {
349     if (showLabels)
350       startTexture("board.png", hLabels_[i]);
351     else
352       startTexture("board.png");
353     drawHLabel(i);
354     endTexture();
355   }
356   glColor3fv(boardColor);
357   for (unsigned int i = 0; i < Board::nbRows(); ++i) {
358     if (showLabels)
359       startTexture("board.png", vLabels_[i]);
360     else
361       startTexture("board.png");
362     drawVLabel(i);
363     endTexture();
364   }
365   startTexture("board.png");
366   glBegin(GL_QUADS);
367   if (boardB > 0.0f) {
368     glColor3fv(boardBorderColor);
369     tessAndTexture(0.0f, 0.0f, boardW, boardB);
370     tessAndTexture(0.0f, boardH - boardB, boardW, boardH);
371     tessAndTexture(0.0f, boardB, boardB, boardH - boardB);
372     tessAndTexture(boardW - boardB, boardB, boardW, boardH - boardB);
373   }
374   glColor3fv(boardColor);
375   int d = Board::nbRows() / 2;
376   tessAndTexture(boardB, boardB, boardB + vLabelW + (d + 1) * caseD / 2.0f,
377                  boardB + hLabelH);
378   tessAndTexture(boardB, boardH - boardB - hLabelH,
379                  boardB + vLabelW + (d + 1) * caseD / 2.0f - hLabelW,
380                  boardH - boardB);
381   tessAndTexture(boardW - (boardB + vLabelW + (d + 1) * caseD / 2.0f) + hLabelW,
382                  boardB, boardW - boardB, boardB + hLabelH);
383   tessAndTexture(boardW - (boardB + vLabelW + (d + 1) * caseD / 2.0f),
384                  boardH - boardB - hLabelH, boardW - boardB, boardH - boardB);
385   for (int i = 0; i < d; ++i) {
386     tessAndTexture(boardB, boardB + hLabelH + i * vLabelH,
387                    boardB + (d - i) * caseD / 2.0f,
388                    boardB + hLabelH + i * vLabelH + vLabelH);
389     tessAndTexture(boardB, boardH - (boardB + hLabelH + i * vLabelH + vLabelH),
390                    boardB + (d - i) * caseD / 2.0f,
391                    boardH - (boardB + hLabelH + i * vLabelH));
392     tessAndTexture(boardW - (boardB + (d - i) * caseD / 2.0f),
393                    boardB + hLabelH + i * vLabelH, boardW - boardB,
394                    boardB + hLabelH + i * vLabelH + vLabelH);
395     tessAndTexture(boardW - (boardB + (d - i) * caseD / 2.0f),
396                    boardH - (boardB + hLabelH + i * vLabelH + vLabelH),
397                    boardW - boardB, boardH - (boardB + hLabelH + i * vLabelH));
398   }
399   glEnd();
400   endTexture();
401   glPopAttrib();
402 }
drawWhitePiecePools(const Board & b,bool lastTransparent) const403 void Drawer::drawWhitePiecePools(const Board &b, bool lastTransparent) const {
404   unsigned int d = Board::nbRows() / 2;
405   unsigned int e = Board::nbSpacesMaxOnRow() - d;
406   startTexture();
407   // Draw the whites and its up to two reds
408   unsigned int nbR = b.nbUnplacedPieces(Red);
409   unsigned int nbW = b.nbUnplacedPieces(White);
410   unsigned int nbRed4White = nbR / 2 + nbR % 2;
411   for (unsigned int istop = nbW + nbRed4White, i = 0; i < istop; ++i) {
412     const float x = boardB + vLabelW + (d + 1) * caseD / 2.0f + (i % e) * caseD;
413     static const float y = -poolB - caseD / 2.0f;
414     const float z = (i / e) * pieceH;
415     glPushMatrix();
416     glTranslatef(x, y, z);
417     drawPiece((i < nbW) ? White : Red,
418               (lastTransparent && i == nbW + nbRed4White - 1) ? 0.5f : 1.0f);
419     glPopMatrix();
420   }
421   endTexture();
422 }
drawBlackPiecePools(const Board & b,bool lastTransparent) const423 void Drawer::drawBlackPiecePools(const Board &b, bool lastTransparent) const {
424   unsigned int d = Board::nbRows() / 2;
425   unsigned int e = Board::nbSpacesMaxOnRow() - d;
426   startTexture();
427   // Draw the blacks and its up to one red
428   unsigned int nbR = b.nbUnplacedPieces(Red);
429   unsigned int nbB = b.nbUnplacedPieces(Black);
430   unsigned int nbRed4Black = nbR / 2;
431   for (unsigned int istop = nbB + nbRed4Black, i = 0; i < istop; ++i) {
432     const float x =
433         boardW - (boardB + vLabelW + (d + 1) * caseD / 2.0f + (i % e) * caseD);
434     static const float y = boardH + poolB + caseD / 2.0f;
435     const float z = (i / e) * pieceH;
436     glPushMatrix();
437     glTranslatef(x, y, z);
438     drawPiece((i < nbB) ? Black : Red,
439               (lastTransparent && i == nbB + nbRed4Black - 1) ? 0.4f : 1.0f);
440     glPopMatrix();
441   }
442   endTexture();
443 }
highlight(const Board::ConstStackHandle & c) const444 void Drawer::highlight(const Board::ConstStackHandle &c) const {
445   glPushAttrib(GL_ENABLE_BIT);
446   glDisable(GL_LIGHTING);
447   startTexture();
448   glPushMatrix();
449   translateTo(c.stackCoord(), 0.05f * pieceH + pieceH / 2.0f);
450   glScalef(caseD, caseD, pieceH);
451   // glutWireCube(1.0f);
452   glPopMatrix();
453   endTexture();
454   glPopAttrib();
455 }
highlightPieces(const Board::ConstStackHandle & c) const456 void Drawer::highlightPieces(const Board::ConstStackHandle &c) const {
457   glPushAttrib(GL_ENABLE_BIT);
458   glDisable(GL_LIGHTING);
459   startTexture();
460   glPushMatrix();
461   translateTo(c.stackCoord(), 0.01f);
462   glTranslatef(0.5f * caseD, 0.5f * caseD, 0.0f);
463   glColor4f(0.0f, 0.0f, 0.3f, 0.3f);
464   drawHRing(pieceRMin, 1.2f * pieceRMax, 0.0f);
465   glPopMatrix();
466   endTexture();
467   glPopAttrib();
468 }
drawMove(const Board & b,const Game::Move m,float t) const469 void Drawer::drawMove(const Board &b, const Game::Move m, float t) const {
470   const float L = caseD * estimateDrawMoveLength(b, m);
471   const float t0 =
472       (b.heightMax() + 1 - b.stackAt(m.src)->height()) * pieceH / L;
473   const float t1 =
474       1.0f - (b.heightMax() + 1 - b.stackAt(m.dst)->height()) * pieceH / L;
475   Board::ConstStackHandle src = b.stackAt(m.src);
476   glPushMatrix();
477   startTexture();
478   if (t <= t0) {
479     t /= t0;
480     float srcH = src->height() * pieceH;
481     float dstH = b.heightMax() * pieceH + pieceH;
482     translateTo(m.src);
483     glTranslatef(0.5f * caseD, 0.5f * caseD, (1 - t) * srcH + t * dstH);
484     for (Stack::const_iterator iter = src->begin(), istop = src->end();
485          iter != istop; ++iter) {
486       drawPiece((*iter)->color());
487       glTranslatef(0.0f, 0.0f, pieceH);
488     }
489   } else if (t <= t1) {
490     t = (t - t0) / (t1 - t0);
491     static const float shifts[5] = {1.0f, 0.5f, 0.0f, -0.5f, -1.0f};
492     float srcX =
493         boardB + vLabelW + m.src.x() * caseD + shifts[m.src.y()] * caseD;
494     float srcY = boardB + hLabelH + m.src.y() * caseD;
495     float dstX =
496         boardB + vLabelW + m.dst.x() * caseD + shifts[m.dst.y()] * caseD;
497     float dstY = boardB + hLabelH + m.dst.y() * caseD;
498     glTranslatef((1.0f - t) * srcX + t * dstX + 0.5f * caseD,
499                  (1.0f - t) * srcY + t * dstY + 0.5f * caseD,
500                  b.heightMax() * pieceH + pieceH);
501     for (Stack::const_iterator iter = src->begin(), istop = src->end();
502          iter != istop; ++iter) {
503       drawPiece((*iter)->color());
504       glTranslatef(0.0f, 0.0f, pieceH);
505     }
506   } else {
507     t = (t - t1) / (1.0f - t1);
508     float srcH = b.heightMax() * pieceH + pieceH;
509     float dstH = b.stackAt(m.dst)->height() * pieceH;
510     translateTo(m.dst);
511     glTranslatef(0.5f * caseD, 0.5f * caseD, (1 - t) * srcH + t * dstH);
512     for (Stack::const_iterator iter = src->begin(), istop = src->end();
513          iter != istop; ++iter) {
514       drawPiece((*iter)->color());
515       glTranslatef(0.0f, 0.0f, pieceH);
516     }
517   }
518   endTexture();
519   glPopMatrix();
520 }
estimateDrawMoveLength(const Board & b,const Game::Move m) const521 float Drawer::estimateDrawMoveLength(const Board &b, const Game::Move m) const {
522   static const float shifts[5] = {1.0f, 0.5f, 0.0f, -0.5f, -1.0f};
523   float srcX = boardB + vLabelW + m.src.x() * caseD + shifts[m.src.y()] * caseD;
524   float srcY = boardB + hLabelH + m.src.y() * caseD;
525   float dstX = boardB + vLabelW + m.dst.x() * caseD + shifts[m.dst.y()] * caseD;
526   float dstY = boardB + hLabelH + m.dst.y() * caseD;
527   return (Vec(dstX - srcX, dstY - srcY, 0.0f).norm() +
528           (b.heightMax() + 1 - b.stackAt(m.src)->height()) * pieceH +
529           (b.heightMax() + 1 - b.stackAt(m.dst)->height()) * pieceH) /
530          caseD;
531 }
boardCenter() const532 Vec Drawer::boardCenter() const {
533   return Vec(boardW / 2.0f, boardH / 2.0f, 0.0f);
534 }
boardUpVector() const535 Vec Drawer::boardUpVector() const { return Vec(0.0f, 0.0f, 1.0f); }
boardRadius() const536 float Drawer::boardRadius() const {
537   return Vec(boardW / 2.0f, boardH / 2.0f, 0.0).norm();
538 }
defaultEyePosition() const539 Vec Drawer::defaultEyePosition() const {
540   return boardCenter() + boardRadius() * Vec(0.0, -1.7f, 1.0);
541 }
542