2 * evalgraph.c - Evaluation graph back-end part
4 * Author: Alessandro Scotti (Dec 2005)
6 * Copyright 2005 Alessandro Scotti
8 * Enhancments Copyright 2009, 2010, 2011, 2012, 2013, 2014, 2015,
9 * 2016 Free Software Foundation, Inc.
11 * ------------------------------------------------------------------------
13 * GNU XBoard is free software: you can redistribute it and/or modify
14 * it under the terms of the GNU General Public License as published by
15 * the Free Software Foundation, either version 3 of the License, or (at
16 * your option) any later version.
18 * GNU XBoard is distributed in the hope that it will be useful, but
19 * WITHOUT ANY WARRANTY; without even the implied warranty of
20 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
21 * General Public License for more details.
23 * You should have received a copy of the GNU General Public License
24 * along with this program. If not, see http://www.gnu.org/licenses/. *
26 *------------------------------------------------------------------------
27 ** See the file ChangeLog for a revision history. */
29 // code refactored by HGM to obtain front-end / back-end separation
38 #else /* not STDC_HEADERS */
41 # else /* not HAVE_STRING_H */
43 # endif /* not HAVE_STRING_H */
44 #endif /* not STDC_HEADERS */
49 #include "evalgraph.h"
52 ChessProgramStats_Move * currPvInfo;
68 DrawLine (int x1, int y1, int x2, int y2, int penType)
70 DrawSegment( x1, y1, NULL, NULL, PEN_NONE );
71 DrawSegment( x2, y2, NULL, NULL, penType );
76 DrawLineEx (int x1, int y1, int x2, int y2, int penType)
79 DrawSegment( x1, y1, &savX, &savY, PEN_NONE );
80 DrawSegment( x2, y2, NULL, NULL, penType );
81 DrawSegment( savX, savY, NULL, NULL, PEN_NONE );
86 GetPvScore (int index)
88 int score = currPvInfo[ index ].score;
90 if(differentialView) score = index < currLast-1 ? -currPvInfo[ index+1 ].score - score : 0;
91 if( index & 1 ) score = -score; /* Flip score for black */
97 MakeEvalTitle (char *title)
100 static char buf[MSG_SIZ];
102 if( currCurrent <0 ) return title; // currCurrent = -1 crashed WB on start without ini file!
103 score = currPvInfo[ currCurrent ].score;
104 depth = currPvInfo[ currCurrent ].depth;
106 if( depth <=0 ) return title;
107 if( currCurrent & 1 ) score = -score; /* Flip score for black */
108 snprintf(buf, MSG_SIZ, "%s {%d: %s%.2f/%-2d %d}", title, currCurrent/2+1,
109 score>0 ? "+" : " ", score/100., depth, (currPvInfo[currCurrent].time+50)/100);
116 For a centipawn value, this function returns the height of the corresponding
117 histogram, centered on the reference axis.
119 Note: height can be negative!
122 GetValueY (int value)
124 if( value < -range*700 ) value = -range*700;
125 if( value > +range*700 ) value = +range*700;
126 if(value > 100*range) value += (appData.zoom - 1)*100*range; else
127 if(value < -100*range) value -= (appData.zoom - 1)*100*range; else
128 value *= appData.zoom;
129 return (nHeightPB / 2) - (int)(value * (nHeightPB - 2*MarginH) / ((1200. + 200.*appData.zoom)*range));
132 // the brush selection is made part of the DrawLine, by passing a style argument
133 // the wrapper for doing the text output makes this back-end
135 DrawAxisSegmentHoriz (int value, Boolean drawValue)
137 int y = GetValueY( range*value*100 );
140 char buf[MSG_SIZ], *b = buf;
142 if( value > 0 ) *b++ = '+';
143 sprintf(b, "%d", range*value);
145 DrawEvalText(buf, strlen(buf), y);
147 // [HGM] counts on DrawEvalText to have select transparent background for dotted line!
148 DrawLine( MarginX, y, MarginX + MarginW, y, PEN_BLACK ); // Y-axis tick marks
149 DrawLine( MarginX + MarginW, y, nWidthPB - MarginW, y, PEN_DOTTED ); // hor grid
152 // The DrawLines again must select their own brush.
153 // the initial brush selection is useless? BkMode needed for dotted line and text
157 int cy = nHeightPB / 2, space = nHeightPB/(6 + appData.zoom);
159 DrawAxisSegmentHoriz( +5, TRUE );
160 DrawAxisSegmentHoriz( +3, space >= 20 );
161 DrawAxisSegmentHoriz( +1, space >= 20 && space*appData.zoom >= 40 );
162 DrawAxisSegmentHoriz( 0, TRUE );
163 DrawAxisSegmentHoriz( -1, space >= 20 && space*appData.zoom >= 40 );
164 DrawAxisSegmentHoriz( -3, space >= 20 );
165 DrawAxisSegmentHoriz( -5, TRUE );
167 DrawLine( MarginX + MarginW, cy, nWidthPB - MarginW, cy, PEN_BLACK ); // x-axis
168 DrawLine( MarginX + MarginW, MarginH, MarginX + MarginW, nHeightPB - MarginH, PEN_BLACK ); // y-axis
173 DrawHistogram (int x, int y, int width, int value, int side)
175 int left, top, right, bottom;
177 if( value > -appData.evalThreshold*range && value < +appData.evalThreshold*range ) return;
180 right = left + width + 1;
183 top = GetValueY( value );
188 bottom = GetValueY( value ) + 1;
192 if( width == MIN_HIST_WIDTH ) {
194 DrawRectangle( left, top, right, bottom, side, FILLED );
197 DrawRectangle( left, top, right, bottom, side, OPEN );
203 DrawSeparator (int index, int x)
206 if( index == currCurrent ) {
207 DrawLineEx( x, MarginH, x, nHeightPB - MarginH, PEN_BLUEDOTTED );
209 else if( (index % 20) == 0 ) {
210 DrawLineEx( x, MarginH, x, nHeightPB - MarginH, PEN_DOTTED );
215 // made back-end by replacing MoveToEx and LineTo by DrawSegment
216 /* Actually draw histogram as a diagram, cause there's too much data */
218 DrawHistogramAsDiagram (int cy, int paint_width, int hist_count)
223 /* Rescale the graph every few moves (as opposed to every move) */
224 hist_count -= hist_count % 8;
228 step = (double) paint_width / (hist_count + 1);
230 for( i=0; i<2; i++ ) {
231 int index = currFirst;
232 int side = (currCurrent + i + 1) & 1; /* Draw current side last */
233 double x = MarginX + MarginW;
235 if( (index & 1) != side ) {
240 DrawSegment( (int) x, cy, NULL, NULL, PEN_NONE );
244 while( index < currLast ) {
247 DrawSeparator( index, (int) x );
249 /* Extend line up to current point */
250 if( currPvInfo[index].depth > 0 ) {
251 DrawSegment((int) x, GetValueY( GetPvScore(index) ), NULL, NULL, (side==0 ? PEN_BOLDWHITE: PEN_BOLDBLACK) );
259 // back-end, delete pen selection
261 DrawHistogramFull (int cy, int hist_width, int hist_count)
265 // SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
267 for( i=0; i<hist_count; i++ ) {
268 int index = currFirst + i;
269 int x = MarginX + MarginW + index * hist_width;
271 /* Draw a separator every 10 moves */
272 DrawSeparator( index, x );
275 if( currPvInfo[i].depth > 0 ) {
276 DrawHistogram( x, cy, hist_width, GetPvScore(index), index & 1 );
290 InitVisualization (VisualizationData *vd)
292 Boolean result = FALSE;
294 vd->cy = nHeightPB / 2;
295 vd->hist_width = MIN_HIST_WIDTH;
296 vd->hist_count = currLast - currFirst;
297 vd->paint_width = nWidthPB - MarginX - 2*MarginW;
299 if( vd->hist_count > 0 ) {
303 vd->hist_width = vd->paint_width / vd->hist_count;
305 if( vd->hist_width > MAX_HIST_WIDTH ) vd->hist_width = MAX_HIST_WIDTH;
307 vd->hist_width -= vd->hist_width % 2;
317 VisualizationData vd;
318 int i; double step = 1;
320 if( InitVisualization( &vd ) ) {
321 if( vd.hist_width < MIN_HIST_WIDTH ) {
322 DrawHistogramAsDiagram( vd.cy, vd.paint_width, vd.hist_count );
323 step = 0.5*vd.paint_width / (((vd.hist_count | 7) + 1)/2 + 1.);
326 DrawHistogramFull( vd.cy, step = vd.hist_width, vd.hist_count );
329 if(!differentialView) return;
330 differentialView = 0;
331 DrawSegment( MarginX + MarginW, vd.cy, NULL, NULL, PEN_NONE );
332 for( i=0; i<vd.hist_count; i++ ) {
333 int index = currFirst + i;
334 int x = MarginX + MarginW + index * step + step/2;
335 DrawSegment((int) x, GetValueY( GetPvScore(index) ), NULL, NULL, PEN_ANY );
337 differentialView = 1;
342 GetMoveIndexFromPoint (int x, int y)
345 int start_x = MarginX + MarginW;
346 VisualizationData vd;
348 if( x >= start_x && InitVisualization( &vd ) ) {
349 /* Almost an hack here... we duplicate some of the paint logic */
350 if( vd.hist_width < MIN_HIST_WIDTH ) {
353 vd.hist_count -= vd.hist_count % 8;
357 step = (double) vd.paint_width / (vd.hist_count + 1);
360 result = (int) (0.5 + (double) (x - start_x) / step);
363 result = (x - start_x) / vd.hist_width;
367 if( result >= currLast ) {
374 // init and display part split of so they can be moved to front end
376 PaintEvalGraph (void)
378 VariantClass v = gameInfo.variant;
379 range = (gameInfo.holdingsWidth && v != VariantSuper && v != VariantGreat && v != VariantSChess) ? 2 : 1; // [HGM] double range in drop games
381 DrawRectangle(0, 0, nWidthPB, nHeightPB, 2, FILLED);