4 * Author: Alessandro Scotti (Dec 2005)
6 * ------------------------------------------------------------------------
7 * This program is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
20 * ------------------------------------------------------------------------
24 #include <windows.h> /* required for all Windows applications */
39 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo );
40 VOID EvalGraphPopUp();
41 VOID EvalGraphPopDown();
44 #define WM_REFRESH_GRAPH (WM_USER + 1)
46 /* Imports from backend.c */
47 char * SavePart(char *str);
49 /* Imports from winboard.c */
50 extern HWND evalGraphDialog;
51 extern BOOLEAN evalGraphDialogUp;
53 extern HINSTANCE hInst;
56 extern WindowPlacement wpEvalGraph;
59 static ChessProgramStats_Move * currPvInfo;
60 static int currFirst = 0;
61 static int currLast = 0;
62 static int currCurrent = -1;
64 static COLORREF crWhite = RGB( 0xFF, 0xFF, 0xB0 );
65 static COLORREF crBlack = RGB( 0xAD, 0x5D, 0x3D );
67 static HDC hdcPB = NULL;
68 static HBITMAP hbmPB = NULL;
69 static int nWidthPB = 0;
70 static int nHeightPB = 0;
71 static HPEN hpenDotted = NULL;
72 static HPEN hpenBlueDotted = NULL;
73 static HPEN hpenBold[2] = { NULL, NULL };
74 static HBRUSH hbrHist[2] = { NULL, NULL };
76 static int MarginX = 18;
77 static int MarginW = 4;
78 static int MarginH = 4;
80 #define MIN_HIST_WIDTH 4
81 #define MAX_HIST_WIDTH 10
83 static int GetPvScore( int index )
85 int score = currPvInfo[ index ].score;
87 if( index & 1 ) score = -score; /* Flip score for black */
92 static VOID DrawLine( int x1, int y1, int x2, int y2 )
94 MoveToEx( hdcPB, x1, y1, NULL );
96 LineTo( hdcPB, x2, y2 );
99 static VOID DrawLineEx( int x1, int y1, int x2, int y2 )
103 MoveToEx( hdcPB, x1, y1, &stPT );
105 LineTo( hdcPB, x2, y2 );
107 MoveToEx( hdcPB, stPT.x, stPT.y, NULL );
110 static HBRUSH CreateBrush( UINT style, COLORREF color )
114 stLB.lbStyle = style;
115 stLB.lbColor = color;
118 return CreateBrushIndirect( &stLB );
122 For a centipawn value, this function returns the height of the corresponding
123 histogram, centered on the reference axis.
125 Note: height can be negative!
127 static int GetValueY( int value )
129 if( value < -700 ) value = -700;
130 if( value > +700 ) value = +700;
132 return (nHeightPB / 2) - (int)(value * (nHeightPB - 2*MarginH) / 1400.0);
135 static VOID DrawAxisSegmentHoriz( int value, BOOL drawValue )
137 int y = GetValueY( value*100 );
139 SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
140 DrawLine( MarginX, y, MarginX + MarginW, y );
141 SelectObject( hdcPB, hpenDotted );
142 DrawLine( MarginX + MarginW, y, nWidthPB - MarginW, y );
151 itoa( value, buf+1, 10 );
154 itoa( value, buf, 10 );
157 cbBuf = strlen( buf );
159 GetTextExtentPoint32( hdcPB, buf, cbBuf, &stSize );
161 TextOut( hdcPB, MarginX - stSize.cx - 2, y - stSize.cy / 2, buf, cbBuf );
165 static VOID DrawAxis()
167 int cy = nHeightPB / 2;
169 SelectObject( hdcPB, GetStockObject(NULL_BRUSH) );
171 SetBkMode( hdcPB, TRANSPARENT );
173 DrawAxisSegmentHoriz( +5, TRUE );
174 DrawAxisSegmentHoriz( +3, FALSE );
175 DrawAxisSegmentHoriz( +1, FALSE );
176 DrawAxisSegmentHoriz( 0, TRUE );
177 DrawAxisSegmentHoriz( -1, FALSE );
178 DrawAxisSegmentHoriz( -3, FALSE );
179 DrawAxisSegmentHoriz( -5, TRUE );
181 SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
183 DrawLine( MarginX + MarginW, cy, nWidthPB - MarginW, cy );
184 DrawLine( MarginX + MarginW, MarginH, MarginX + MarginW, nHeightPB - MarginH );
187 static VOID DrawHistogram( int x, int y, int width, int value, int side )
191 if( value > -25 && value < +25 ) return;
194 rc.right = rc.left + width + 1;
197 rc.top = GetValueY( value );
202 rc.bottom = GetValueY( value ) + 1;
206 if( width == MIN_HIST_WIDTH ) {
208 FillRect( hdcPB, &rc, hbrHist[side] );
211 SelectObject( hdcPB, hbrHist[side] );
212 Rectangle( hdcPB, rc.left, rc.top, rc.right, rc.bottom );
216 static VOID DrawSeparator( int index, int x )
219 if( index == currCurrent ) {
220 HPEN hp = SelectObject( hdcPB, hpenBlueDotted );
221 DrawLineEx( x, MarginH, x, nHeightPB - MarginH );
222 SelectObject( hdcPB, hp );
224 else if( (index % 20) == 0 ) {
225 HPEN hp = SelectObject( hdcPB, hpenDotted );
226 DrawLineEx( x, MarginH, x, nHeightPB - MarginH );
227 SelectObject( hdcPB, hp );
232 /* Actually draw histogram as a diagram, cause there's too much data */
233 static VOID DrawHistogramAsDiagram( int cy, int paint_width, int hist_count )
238 /* Rescale the graph every few moves (as opposed to every move) */
239 hist_count -= hist_count % 8;
243 step = (double) paint_width / (hist_count + 1);
245 for( i=0; i<2; i++ ) {
246 int index = currFirst;
247 int side = (currCurrent + i + 1) & 1; /* Draw current side last */
248 double x = MarginX + MarginW;
250 if( (index & 1) != side ) {
255 SelectObject( hdcPB, hpenBold[side] );
257 MoveToEx( hdcPB, (int) x, cy, NULL );
261 while( index < currLast ) {
264 DrawSeparator( index, (int) x );
266 /* Extend line up to current point */
267 if( currPvInfo[index].depth > 0 ) {
268 LineTo( hdcPB, (int) x, GetValueY( GetPvScore(index) ) );
276 static VOID DrawHistogramFull( int cy, int hist_width, int hist_count )
280 SelectObject( hdcPB, GetStockObject(BLACK_PEN) );
282 for( i=0; i<hist_count; i++ ) {
283 int index = currFirst + i;
284 int x = MarginX + MarginW + index * hist_width;
286 /* Draw a separator every 10 moves */
287 DrawSeparator( index, x );
290 if( currPvInfo[i].depth > 0 ) {
291 DrawHistogram( x, cy, hist_width, GetPvScore(index), index & 1 );
303 static BOOL InitVisualization( VisualizationData * vd )
307 vd->cy = nHeightPB / 2;
308 vd->hist_width = MIN_HIST_WIDTH;
309 vd->hist_count = currLast - currFirst;
310 vd->paint_width = nWidthPB - MarginX - 2*MarginW;
312 if( vd->hist_count > 0 ) {
316 vd->hist_width = vd->paint_width / vd->hist_count;
318 if( vd->hist_width > MAX_HIST_WIDTH ) vd->hist_width = MAX_HIST_WIDTH;
320 vd->hist_width -= vd->hist_width % 2;
326 static VOID DrawHistograms()
328 VisualizationData vd;
330 if( InitVisualization( &vd ) ) {
331 if( vd.hist_width < MIN_HIST_WIDTH ) {
332 DrawHistogramAsDiagram( vd.cy, vd.paint_width, vd.hist_count );
335 DrawHistogramFull( vd.cy, vd.hist_width, vd.hist_count );
340 static int GetMoveIndexFromPoint( int x, int y )
343 int start_x = MarginX + MarginW;
344 VisualizationData vd;
346 if( x >= start_x && InitVisualization( &vd ) ) {
347 /* Almost an hack here... we duplicate some of the paint logic */
348 if( vd.hist_width < MIN_HIST_WIDTH ) {
351 vd.hist_count -= vd.hist_count % 8;
355 step = (double) vd.paint_width / (vd.hist_count + 1);
358 result = (int) (0.5 + (double) (x - start_x) / step);
361 result = (x - start_x) / vd.hist_width;
365 if( result >= currLast ) {
372 static VOID DrawBackground()
377 hbr = CreateBrush( BS_SOLID, GetSysColor( COLOR_3DFACE ) );
382 rc.bottom = nHeightPB;
384 FillRect( hdcPB, &rc, hbr );
389 static VOID PaintEvalGraph( HWND hWnd, HDC hDC )
395 /* Get client area */
396 GetClientRect( hWnd, &rcClient );
398 width = rcClient.right - rcClient.left;
399 height = rcClient.bottom - rcClient.top;
401 /* Create or recreate paint box if needed */
402 if( hbmPB == NULL || width != nWidthPB || height != nHeightPB ) {
403 if( hpenDotted == NULL ) {
404 hpenDotted = CreatePen( PS_DOT, 0, RGB(0xA0,0xA0,0xA0) );
405 hpenBlueDotted = CreatePen( PS_DOT, 0, RGB(0x00,0x00,0xFF) );
406 hpenBold[0] = CreatePen( PS_SOLID, 2, crWhite );
407 hpenBold[1] = CreatePen( PS_SOLID, 2, crBlack );
408 hbrHist[0] = CreateBrush( BS_SOLID, crWhite );
409 hbrHist[1] = CreateBrush( BS_SOLID, crBlack );
412 if( hdcPB != NULL ) {
417 if( hbmPB != NULL ) {
418 DeleteObject( hbmPB );
422 hdcPB = CreateCompatibleDC( hDC );
426 hbmPB = CreateCompatibleBitmap( hDC, nWidthPB, nHeightPB );
428 SelectObject( hdcPB, hbmPB );
436 /* Copy bitmap into destination DC */
437 BitBlt( hDC, 0, 0, width, height, hdcPB, 0, 0, SRCCOPY );
440 LRESULT CALLBACK EvalGraphProc( HWND hDlg, UINT message, WPARAM wParam, LPARAM lParam )
449 if( evalGraphDialog == NULL ) {
450 evalGraphDialog = hDlg;
452 RestoreWindowPlacement( hDlg, &wpEvalGraph ); /* Restore window placement */
458 switch (LOWORD(wParam)) {
460 EndDialog(hDlg, TRUE);
464 EndDialog(hDlg, FALSE);
477 hDC = BeginPaint( hDlg, &stPS );
478 PaintEvalGraph( hDlg, hDC );
479 EndPaint( hDlg, &stPS );
482 case WM_REFRESH_GRAPH:
484 PaintEvalGraph( hDlg, hDC );
485 ReleaseDC( hDlg, hDC );
488 case WM_LBUTTONDBLCLK:
489 if( wParam == 0 || wParam == MK_LBUTTON ) {
490 int index = GetMoveIndexFromPoint( LOWORD(lParam), HIWORD(lParam) );
492 if( index >= 0 && index < currLast ) {
493 ToNrEvent( index + 1 );
499 InvalidateRect( hDlg, NULL, FALSE );
502 case WM_GETMINMAXINFO:
504 MINMAXINFO * mmi = (MINMAXINFO *) lParam;
506 mmi->ptMinTrackSize.x = 100;
507 mmi->ptMinTrackSize.y = 100;
511 /* Support for captionless window */
513 case WM_NCLBUTTONDBLCLK:
514 if( wParam == HTCAPTION ) {
517 POINTS pts = MAKEPOINTS(lParam);
521 ScreenToClient( hDlg, &mouse_xy );
523 index = GetMoveIndexFromPoint( mouse_xy.x, mouse_xy.y );
525 if( index >= 0 && index < currLast ) {
526 ToNrEvent( index + 1 );
533 LRESULT res = DefWindowProc( hDlg, message, wParam, lParam );
535 if( res == HTCLIENT ) res = HTCAPTION;
537 SetWindowLong( hDlg, DWL_MSGRESULT, res );
548 case WM_ENTERSIZEMOVE:
549 return OnEnterSizeMove( &sd, hDlg, wParam, lParam );
552 return OnSizing( &sd, hDlg, wParam, lParam );
555 return OnMoving( &sd, hDlg, wParam, lParam );
557 case WM_EXITSIZEMOVE:
558 return OnExitSizeMove( &sd, hDlg, wParam, lParam );
564 VOID EvalGraphPopUp()
568 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_CHECKED);
570 if( evalGraphDialog ) {
571 SendMessage( evalGraphDialog, WM_INITDIALOG, 0, 0 );
573 if( ! evalGraphDialogUp ) {
574 ShowWindow(evalGraphDialog, SW_SHOW);
578 crWhite = appData.evalHistColorWhite;
579 crBlack = appData.evalHistColorBlack;
581 lpProc = MakeProcInstance( (FARPROC) EvalGraphProc, hInst );
583 /* Note to self: dialog must have the WS_VISIBLE style set, otherwise it's not shown! */
584 CreateDialog( hInst, MAKEINTRESOURCE(DLG_EvalGraph), hwndMain, (DLGPROC)lpProc );
586 FreeProcInstance(lpProc);
589 evalGraphDialogUp = TRUE;
592 VOID EvalGraphPopDown()
594 CheckMenuItem(GetMenu(hwndMain), IDM_ShowEvalGraph, MF_UNCHECKED);
596 if( evalGraphDialog ) {
597 ShowWindow(evalGraphDialog, SW_HIDE);
600 evalGraphDialogUp = FALSE;
603 VOID EvalGraphSet( int first, int last, int current, ChessProgramStats_Move * pvInfo )
605 /* [AS] Danger! For now we rely on the pvInfo parameter being a static variable! */
609 currCurrent = current;
612 if( evalGraphDialog ) {
613 SendMessage( evalGraphDialog, WM_REFRESH_GRAPH, 0, 0 );
619 return evalGraphDialogUp;