2 * evalgraph.c - Evaluation graph back-end part
4 * Author: Alessandro Scotti (Dec 2005)
6 * Copyright 2005 Alessandro Scotti
8 * ------------------------------------------------------------------------
10 * GNU XBoard is free software: you can redistribute it and/or modify
11 * it under the terms of the GNU General Public License as published by
12 * the Free Software Foundation, either version 3 of the License, or (at
13 * your option) any later version.
15 * GNU XBoard is distributed in the hope that it will be useful, but
16 * WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
18 * General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program. If not, see http://www.gnu.org/licenses/. *
23 *------------------------------------------------------------------------
24 ** See the file ChangeLog for a revision history. */
26 // code refactored by HGM to obtain front-end / back-end separation
35 #else /* not STDC_HEADERS */
38 # else /* not HAVE_STRING_H */
40 # endif /* not HAVE_STRING_H */
41 #endif /* not STDC_HEADERS */
46 #include "evalgraph.h"
49 ChessProgramStats_Move * currPvInfo;
63 static void DrawLine( int x1, int y1, int x2, int y2, int penType )
65 DrawSegment( x1, y1, NULL, NULL, PEN_NONE );
66 DrawSegment( x2, y2, NULL, NULL, penType );
70 static void DrawLineEx( int x1, int y1, int x2, int y2, int penType )
73 DrawSegment( x1, y1, &savX, &savY, PEN_NONE );
74 DrawSegment( x2, y2, NULL, NULL, penType );
75 DrawSegment( savX, savY, NULL, NULL, PEN_NONE );
79 static int GetPvScore( int index )
81 int score = currPvInfo[ index ].score;
83 if( index & 1 ) score = -score; /* Flip score for black */
90 For a centipawn value, this function returns the height of the corresponding
91 histogram, centered on the reference axis.
93 Note: height can be negative!
95 static int GetValueY( int value )
97 if( value < -range*700 ) value = -range*700;
98 if( value > +range*700 ) value = +range*700;
100 return (nHeightPB / 2) - (int)(value * (nHeightPB - 2*MarginH) / (1400.*range));
103 // the brush selection is made part of the DrawLine, by passing a style argument
104 // the wrapper for doing the text output makes this back-end
105 static void DrawAxisSegmentHoriz( int value, Boolean drawValue )
107 int y = GetValueY( range*value*100 );
110 char buf[MSG_SIZ], *b = buf;
112 if( value > 0 ) *b++ = '+';
113 sprintf(b, "%d", range*value);
115 DrawEvalText(buf, strlen(buf), y);
117 // [HGM] counts on DrawEvalText to have select transparent background for dotted line!
118 DrawLine( MarginX, y, MarginX + MarginW, y, PEN_BLACK ); // Y-axis tick marks
119 DrawLine( MarginX + MarginW, y, nWidthPB - MarginW, y, PEN_DOTTED ); // hor grid
122 // The DrawLines again must select their own brush.
123 // the initial brush selection is useless? BkMode needed for dotted line and text
124 static void DrawAxis()
126 int cy = nHeightPB / 2;
128 // SelectObject( hdcPB, GetStockObject(NULL_BRUSH) );
130 // SetBkMode( hdcPB, TRANSPARENT );
132 DrawAxisSegmentHoriz( +5, TRUE );
133 DrawAxisSegmentHoriz( +3, FALSE );
134 DrawAxisSegmentHoriz( +1, FALSE );
135 DrawAxisSegmentHoriz( 0, TRUE );
136 DrawAxisSegmentHoriz( -1, FALSE );
137 DrawAxisSegmentHoriz( -3, FALSE );
138 DrawAxisSegmentHoriz( -5, TRUE );
140 DrawLine( MarginX + MarginW, cy, nWidthPB - MarginW, cy, PEN_BLACK ); // x-axis
141 DrawLine( MarginX + MarginW, MarginH, MarginX + MarginW, nHeightPB - MarginH, PEN_BLACK ); // y-axis
145 static void DrawHistogram( int x, int y, int width, int value, int side )
147 int left, top, right, bottom;
149 if( value > -25 && value < +25 ) return;
152 right = left + width + 1;
155 top = GetValueY( value );
160 bottom = GetValueY( value ) + 1;
164 if( width == MIN_HIST_WIDTH ) {
166 DrawRectangle( left, top, right, bottom, side, FILLED );
169 DrawRectangle( left, top, right, bottom, side, OPEN );
174 static void DrawSeparator( int index, int x )
177 if( index == currCurrent ) {
178 DrawLineEx( x, MarginH, x, nHeightPB - MarginH, PEN_BLUEDOTTED );
180 else if( (index % 20) == 0 ) {
181 DrawLineEx( x, MarginH, x, nHeightPB - MarginH, PEN_DOTTED );
186 // made back-end by replacing MoveToEx and LineTo by DrawSegment
187 /* Actually draw histogram as a diagram, cause there's too much data */
188 static void DrawHistogramAsDiagram( int cy, int paint_width, int hist_count )
193 /* Rescale the graph every few moves (as opposed to every move) */
194 hist_count -= hist_count % 8;
198 step = (double) paint_width / (hist_count + 1);
200 for( i=0; i<2; i++ ) {
201 int index = currFirst;
202 int side = (currCurrent + i + 1) & 1; /* Draw current side last */
203 double x = MarginX + MarginW;
205 if( (index & 1) != side ) {
210 DrawSegment( (int) x, cy, NULL, NULL, PEN_NONE );
214 while( index < currLast ) {
217 DrawSeparator( index, (int) x );
219 /* Extend line up to current point */
220 if( currPvInfo[index].depth > 0 ) {
221 DrawSegment((int) x, GetValueY( GetPvScore(index) ), NULL, NULL, PEN_BOLD + side );
229 // back-end, delete pen selection
230 static void DrawHistogramFull( int cy, int hist_width, int hist_count )
234 // SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
236 for( i=0; i<hist_count; i++ ) {
237 int index = currFirst + i;
238 int x = MarginX + MarginW + index * hist_width;
240 /* Draw a separator every 10 moves */
241 DrawSeparator( index, x );
244 if( currPvInfo[i].depth > 0 ) {
245 DrawHistogram( x, cy, hist_width, GetPvScore(index), index & 1 );
258 static Boolean InitVisualization( VisualizationData * vd )
260 Boolean result = FALSE;
262 vd->cy = nHeightPB / 2;
263 vd->hist_width = MIN_HIST_WIDTH;
264 vd->hist_count = currLast - currFirst;
265 vd->paint_width = nWidthPB - MarginX - 2*MarginW;
267 if( vd->hist_count > 0 ) {
271 vd->hist_width = vd->paint_width / vd->hist_count;
273 if( vd->hist_width > MAX_HIST_WIDTH ) vd->hist_width = MAX_HIST_WIDTH;
275 vd->hist_width -= vd->hist_width % 2;
282 static void DrawHistograms()
284 VisualizationData vd;
286 if( InitVisualization( &vd ) ) {
287 if( vd.hist_width < MIN_HIST_WIDTH ) {
288 DrawHistogramAsDiagram( vd.cy, vd.paint_width, vd.hist_count );
291 DrawHistogramFull( vd.cy, vd.hist_width, vd.hist_count );
297 int GetMoveIndexFromPoint( int x, int y )
300 int start_x = MarginX + MarginW;
301 VisualizationData vd;
303 if( x >= start_x && InitVisualization( &vd ) ) {
304 /* Almost an hack here... we duplicate some of the paint logic */
305 if( vd.hist_width < MIN_HIST_WIDTH ) {
308 vd.hist_count -= vd.hist_count % 8;
312 step = (double) vd.paint_width / (vd.hist_count + 1);
315 result = (int) (0.5 + (double) (x - start_x) / step);
318 result = (x - start_x) / vd.hist_width;
322 if( result >= currLast ) {
329 // init and display part split of so they can be moved to front end
330 void PaintEvalGraph( void )
332 VariantClass v = gameInfo.variant;
333 range = (gameInfo.holdingsWidth && v != VariantSuper && v != VariantGreat && v != VariantSChess) ? 2 : 1; // [HGM] double range in drop games
335 DrawRectangle(0, 0, nWidthPB, nHeightPB, 2, FILLED);