|
|
|
/*==LICENSE==*
|
|
|
|
|
|
|
|
CyanWorlds.com Engine - MMOG client, server and tools
|
|
|
|
Copyright (C) 2011 Cyan Worlds, Inc.
|
|
|
|
|
|
|
|
This program is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Additional permissions under GNU GPL version 3 section 7
|
|
|
|
|
|
|
|
If you modify this Program, or any covered work, by linking or
|
|
|
|
combining it with any of RAD Game Tools Bink SDK, Autodesk 3ds Max SDK,
|
|
|
|
NVIDIA PhysX SDK, Microsoft DirectX SDK, OpenSSL library, Independent
|
|
|
|
JPEG Group JPEG library, Microsoft Windows Media SDK, or Apple QuickTime SDK
|
|
|
|
(or a modified version of those libraries),
|
|
|
|
containing parts covered by the terms of the Bink SDK EULA, 3ds Max EULA,
|
|
|
|
PhysX SDK EULA, DirectX SDK EULA, OpenSSL and SSLeay licenses, IJG
|
|
|
|
JPEG Library README, Windows Media SDK EULA, or QuickTime SDK EULA, the
|
|
|
|
licensors of this Program grant you additional
|
|
|
|
permission to convey the resulting work. Corresponding Source for a
|
|
|
|
non-source form of such a combination shall include the source code for
|
|
|
|
the parts of OpenSSL and IJG JPEG Library used as well as that of the covered
|
|
|
|
work.
|
|
|
|
|
|
|
|
You can contact Cyan Worlds, Inc. by email legal@cyan.com
|
|
|
|
or by snail mail at:
|
|
|
|
Cyan Worlds, Inc.
|
|
|
|
14617 N Newport Hwy
|
|
|
|
Mead, WA 99021
|
|
|
|
|
|
|
|
*==LICENSE==*/
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// plDetailCurveCtrl Class Functions //
|
|
|
|
// Custom Win32 Control class for drawing the detail map opacity curve so //
|
|
|
|
// the artists can figure out what the hell is going on. //
|
|
|
|
// Cyan, Inc. //
|
|
|
|
// //
|
|
|
|
// To use: //
|
|
|
|
// 1. Create a new plDetailCurveCtrl, giving it a parent window and a //
|
|
|
|
// client rect. //
|
|
|
|
// 2. Set the start and end percentages, along with the start and end //
|
|
|
|
// opacities. //
|
|
|
|
// //
|
|
|
|
//// Version History //////////////////////////////////////////////////////////
|
|
|
|
// //
|
|
|
|
// 10.1.2001 mcn - Created. //
|
|
|
|
// //
|
|
|
|
///////////////////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
#include "HeadSpin.h"
|
|
|
|
#include "hsWindows.h"
|
|
|
|
|
|
|
|
#include "resource.h"
|
|
|
|
#pragma hdrstop
|
|
|
|
|
|
|
|
#include "plDetailCurveCtrl.h"
|
|
|
|
|
|
|
|
|
|
|
|
//// Static Stuff /////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
int plDetailCurveCtrl::fClassRefCnt = 0;
|
|
|
|
HINSTANCE plDetailCurveCtrl::fInstance = NULL;
|
|
|
|
HBITMAP plDetailCurveCtrl::fBgndImage = NULL;
|
|
|
|
HFONT plDetailCurveCtrl::fFont = NULL;
|
|
|
|
|
|
|
|
#ifdef MCN_TWO_GRAPH_MODE
|
|
|
|
HBITMAP plDetailCurveCtrl::fBgndImage2 = NULL;
|
|
|
|
bool plDetailCurveCtrl::fXAsMipmapLevel = false;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
const char gCtrlClassName[] = "DetailCurveClass";
|
|
|
|
|
|
|
|
#define kHiResStep 0.01f
|
|
|
|
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::IRegisterCtrl( HINSTANCE instance )
|
|
|
|
{
|
|
|
|
if( fClassRefCnt == 0 )
|
|
|
|
{
|
|
|
|
fInstance = instance;
|
|
|
|
|
|
|
|
WNDCLASSEX clInfo;
|
|
|
|
|
|
|
|
memset( &clInfo, 0, sizeof( clInfo ) );
|
|
|
|
clInfo.cbSize = sizeof( clInfo );
|
|
|
|
clInfo.style = CS_OWNDC | CS_NOCLOSE;
|
|
|
|
clInfo.lpfnWndProc = (WNDPROC)IWndProc;
|
|
|
|
clInfo.cbClsExtra = 0;
|
|
|
|
clInfo.cbWndExtra = 0;
|
|
|
|
clInfo.hInstance = fInstance;
|
|
|
|
clInfo.hIcon = NULL;
|
|
|
|
clInfo.hCursor = LoadCursor( NULL, IDC_CROSS );
|
|
|
|
clInfo.hbrBackground = NULL;
|
|
|
|
clInfo.lpszMenuName = NULL;
|
|
|
|
clInfo.lpszClassName = gCtrlClassName;
|
|
|
|
clInfo.hIconSm = NULL;
|
|
|
|
|
|
|
|
RegisterClassEx( &clInfo );
|
|
|
|
|
|
|
|
fBgndImage = (HBITMAP)LoadImage( fInstance, MAKEINTRESOURCE( IDB_DETAILBGND ), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR );
|
|
|
|
#ifdef MCN_TWO_GRAPH_MODE
|
|
|
|
fBgndImage2 = (HBITMAP)LoadImage( fInstance, MAKEINTRESOURCE( IDB_DETAILBGND2 ), IMAGE_BITMAP, 0, 0, LR_DEFAULTCOLOR );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
HDC hDC = GetDC( NULL );
|
|
|
|
fFont = CreateFont( -MulDiv( 8, GetDeviceCaps( hDC, LOGPIXELSY ), 72 ), 0, 0, 0, 0, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS,
|
|
|
|
CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH, "Arial" );
|
|
|
|
ReleaseDC( NULL, hDC );
|
|
|
|
}
|
|
|
|
|
|
|
|
fClassRefCnt++;
|
|
|
|
}
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::IUnregisterCtrl( void )
|
|
|
|
{
|
|
|
|
fClassRefCnt--;
|
|
|
|
if( fClassRefCnt == 0 )
|
|
|
|
{
|
|
|
|
UnregisterClass( gCtrlClassName, fInstance );
|
|
|
|
if( fFont != NULL )
|
|
|
|
DeleteObject( fFont );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// Constructor & Destructor /////////////////////////////////////////////////
|
|
|
|
|
|
|
|
plDetailCurveCtrl::plDetailCurveCtrl( HWND parentWnd, WPARAM id, RECT *clientRect, HINSTANCE instance )
|
|
|
|
{
|
|
|
|
// Class init
|
|
|
|
if( instance == NULL )
|
|
|
|
instance = (HINSTANCE)GetWindowLong( parentWnd, GWL_HINSTANCE );
|
|
|
|
IRegisterCtrl( instance );
|
|
|
|
|
|
|
|
// Per-object init
|
|
|
|
fDblDC = NULL;
|
|
|
|
fDblBitmap = NULL;
|
|
|
|
fStartPercent = 0;
|
|
|
|
fStartOpac = 0;
|
|
|
|
fEndPercent = 1.f;
|
|
|
|
fEndOpac = 1.f;
|
|
|
|
fNumLevels = 8;
|
|
|
|
fDraggingStart = fDraggingEnd = false;
|
|
|
|
fCanDragStart = fCanDragEnd = false;
|
|
|
|
|
|
|
|
// Note: we create originally as disabled since the default detail setting is disabled.
|
|
|
|
// The MAX Update stuff should change this if necessary after we're created
|
|
|
|
fHWnd = ::CreateWindowEx( WS_EX_CLIENTEDGE, gCtrlClassName, "Detail Curve", WS_CHILD | WS_VISIBLE | WS_BORDER | WS_DISABLED,
|
|
|
|
clientRect->left, clientRect->top, clientRect->right - clientRect->left,
|
|
|
|
clientRect->bottom - clientRect->top,
|
|
|
|
parentWnd, (HMENU)id, instance, 0 );
|
|
|
|
if( fHWnd == NULL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
SetWindowLong( fHWnd, GWL_USERDATA, (LONG)this );
|
|
|
|
}
|
|
|
|
|
|
|
|
plDetailCurveCtrl::~plDetailCurveCtrl()
|
|
|
|
{
|
|
|
|
if( fDblDC != NULL )
|
|
|
|
{
|
|
|
|
SelectObject( fDblDC, (HBITMAP)NULL );
|
|
|
|
DeleteObject( fDblBitmap );
|
|
|
|
DeleteDC( fDblDC );
|
|
|
|
}
|
|
|
|
|
|
|
|
if( fWhiteBrush != NULL )
|
|
|
|
DeleteObject( fWhiteBrush );
|
|
|
|
if( fBluePen != NULL )
|
|
|
|
DeleteObject( fBluePen );
|
|
|
|
if( fLiteBluePen != NULL )
|
|
|
|
DeleteObject( fLiteBluePen );
|
|
|
|
if( fBlueBrush != NULL )
|
|
|
|
DeleteObject( fBlueBrush );
|
|
|
|
|
|
|
|
// DestroyWindow( fHWnd );
|
|
|
|
IUnregisterCtrl();
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IInitDblBuffer ///////////////////////////////////////////////////////////
|
|
|
|
// Note: For some strange reason, grabbing the HDC of the window doesn't do
|
|
|
|
// any good, 'cause it's black-and-white (right, THAT makes sense). Grabbing
|
|
|
|
// the desktop DC works, however.
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::IInitDblBuffer( void )
|
|
|
|
{
|
|
|
|
if( fDblDC == NULL )
|
|
|
|
{
|
|
|
|
int width, height;
|
|
|
|
RECT r;
|
|
|
|
HDC desk = GetDC( NULL );
|
|
|
|
|
|
|
|
GetClientRect( fHWnd, &r );
|
|
|
|
width = r.right - r.left;
|
|
|
|
height = r.bottom - r.top;
|
|
|
|
|
|
|
|
fDblDC = CreateCompatibleDC( desk );
|
|
|
|
fDblBitmap = CreateCompatibleBitmap( desk/*fDblDC*/, width, height );
|
|
|
|
SelectObject( fDblDC, fDblBitmap );
|
|
|
|
ReleaseDC( NULL, desk );
|
|
|
|
|
|
|
|
fWhiteBrush = CreateSolidBrush( RGB( 255, 255, 255 ) );
|
|
|
|
fBluePen = CreatePen( PS_SOLID, 1, RGB( 0, 0, 255 ) );
|
|
|
|
fLiteBluePen = CreatePen( PS_SOLID, 1, RGB( 127, 127, 255 ) );
|
|
|
|
fBlueBrush = CreateSolidBrush( RGB( 0, 0, 255 ) );
|
|
|
|
|
|
|
|
IRefreshDblBuffer();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IRefreshDblBuffer ////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::IRefreshDblBuffer( void )
|
|
|
|
{
|
|
|
|
HDC hBgndDC;
|
|
|
|
RECT clientRect, r;
|
|
|
|
SIZE bgndSize;
|
|
|
|
int width, height, x, y;
|
|
|
|
HPEN oldPen;
|
|
|
|
BITMAPINFO bmpInfo;
|
|
|
|
POINT pt1, pt2;
|
|
|
|
|
|
|
|
|
|
|
|
IInitDblBuffer();
|
|
|
|
|
|
|
|
GetClientRect( fHWnd, &clientRect );
|
|
|
|
width = clientRect.right - clientRect.left;
|
|
|
|
height = clientRect.bottom - clientRect.top;
|
|
|
|
|
|
|
|
if( fDblBitmap != NULL )
|
|
|
|
{
|
|
|
|
FillRect( fDblDC, &clientRect, fWhiteBrush );
|
|
|
|
|
|
|
|
if( fBgndImage != NULL )
|
|
|
|
{
|
|
|
|
|
|
|
|
// Draw bgnd
|
|
|
|
hBgndDC = CreateCompatibleDC( fDblDC );
|
|
|
|
#ifdef MCN_TWO_GRAPH_MODE
|
|
|
|
SelectObject( hBgndDC, fXAsMipmapLevel ? fBgndImage2 : fBgndImage );
|
|
|
|
#else
|
|
|
|
SelectObject( hBgndDC, fBgndImage );
|
|
|
|
#endif
|
|
|
|
|
|
|
|
bmpInfo.bmiHeader.biSize = sizeof( bmpInfo.bmiHeader );
|
|
|
|
bmpInfo.bmiHeader.biBitCount = 0;
|
|
|
|
GetDIBits( hBgndDC, fBgndImage, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS );
|
|
|
|
bgndSize.cx = bmpInfo.bmiHeader.biWidth;
|
|
|
|
bgndSize.cy = bmpInfo.bmiHeader.biHeight;
|
|
|
|
x = ( width - bgndSize.cx ) >> 1;
|
|
|
|
y = ( height - bgndSize.cy ) >> 1;
|
|
|
|
|
|
|
|
BitBlt( fDblDC, x, y, bgndSize.cx, bgndSize.cy, hBgndDC, 0, 0, SRCCOPY );
|
|
|
|
SelectObject( hBgndDC, (HBITMAP)NULL );
|
|
|
|
DeleteDC( hBgndDC );
|
|
|
|
|
|
|
|
/// Draw graph
|
|
|
|
if( IsWindowEnabled( fHWnd ) )
|
|
|
|
{
|
|
|
|
bgndSize.cx -= 8;
|
|
|
|
oldPen = (HPEN)SelectObject( fDblDC, fLiteBluePen );
|
|
|
|
|
|
|
|
/// This line draws the light blue "actual" curve, which shows what happens
|
|
|
|
/// when you actually interpolate the curve across the mipmap levels. It's
|
|
|
|
/// more accurate in that it shows the actual values computed for each level,
|
|
|
|
/// but less accurate because it doesn't take into account mipmap sampling
|
|
|
|
/// and such. Given the latter, we leave it out (for now) to avoid confusing
|
|
|
|
/// the artists.
|
|
|
|
// IDrawCurve( fDblDC, true, x, y, &bgndSize );
|
|
|
|
|
|
|
|
SelectObject( fDblDC, fBluePen );
|
|
|
|
IDrawCurve( fDblDC, false, x, y, &bgndSize );
|
|
|
|
|
|
|
|
SelectObject( fDblDC, oldPen );
|
|
|
|
|
|
|
|
if( fStartPercent == 0.07f && fStartOpac == 0.23f && fEndPercent == 0.19f && fEndOpac == 0.79f )
|
|
|
|
{
|
|
|
|
const char str[] = "\x48\x61\x70\x70\x79\x20\x62\x64\x61\x79\x20\x74\x6f\x20\x6d\x63\x6e\x21";
|
|
|
|
SetBkMode( fDblDC, TRANSPARENT );
|
|
|
|
SetTextColor( fDblDC, RGB( 0, 0, 255 ) );
|
|
|
|
SelectObject( fDblDC, fFont );
|
|
|
|
TextOut( fDblDC, x, y + bgndSize.cy - 10, str, strlen( str ) );
|
|
|
|
}
|
|
|
|
|
|
|
|
// Draw our two markers
|
|
|
|
IXlateValuesToClientPt( fStartPercent, fStartOpac, &pt1, x, y, &bgndSize );
|
|
|
|
SetRect( &fStartDragPt, pt1.x - 4, pt1.y - 4, pt1.x + 4, pt1.y + 4 );
|
|
|
|
r = fStartDragPt;
|
|
|
|
if( !fCanDragStart )
|
|
|
|
InflateRect( &r, -2, -2 );
|
|
|
|
FillRect( fDblDC, &r, fBlueBrush );
|
|
|
|
|
|
|
|
IXlateValuesToClientPt( fEndPercent, fEndOpac, &pt2, x, y, &bgndSize );
|
|
|
|
SetRect( &fEndDragPt, pt2.x - 4, pt2.y - 4, pt2.x + 4, pt2.y + 4 );
|
|
|
|
r = fEndDragPt;
|
|
|
|
if( !fCanDragEnd )
|
|
|
|
InflateRect( &r, -2, -2 );
|
|
|
|
FillRect( fDblDC, &r, fBlueBrush );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IDrawCurve ///////////////////////////////////////////////////////////////
|
|
|
|
// Draw the damned curve.
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::IDrawCurve( HDC hDC, bool clampToInts, int cornerX, int cornerY, SIZE *bgndSize )
|
|
|
|
{
|
|
|
|
float dist, penX, penBaseY, penXStep, penYScale;
|
|
|
|
POINT pt1;
|
|
|
|
|
|
|
|
|
|
|
|
// Calc stuff
|
|
|
|
penX = (float)cornerX;
|
|
|
|
penBaseY = (float)( cornerY + bgndSize->cy );
|
|
|
|
penXStep = (float)bgndSize->cx * kHiResStep;
|
|
|
|
penYScale = (float)bgndSize->cy;
|
|
|
|
|
|
|
|
// Draw curve
|
|
|
|
pt1.x = (int)penX;
|
|
|
|
pt1.y = (int)( penBaseY - penYScale * fStartOpac );
|
|
|
|
|
|
|
|
float artificialBias = 1.f / (float)fNumLevels; // So we never get a howFar less than 0
|
|
|
|
float artificialMaxDist = 1.f - artificialBias;
|
|
|
|
|
|
|
|
for( dist = 0.f; dist <= 1.f; dist += kHiResStep )
|
|
|
|
{
|
|
|
|
float opac = IXlateDistToValue( dist, clampToInts );
|
|
|
|
|
|
|
|
if( dist == 0.f )
|
|
|
|
MoveToEx( hDC, (int)penX, (int)( penBaseY - penYScale * opac ), NULL );
|
|
|
|
else
|
|
|
|
LineTo( hDC, (int)penX, (int)( penBaseY - penYScale * opac ) );
|
|
|
|
|
|
|
|
penX += penXStep;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IXlateDistToValue ////////////////////////////////////////////////////////
|
|
|
|
// I.E. from distance across graph to distance up on graph (percentage-wise)
|
|
|
|
|
|
|
|
float plDetailCurveCtrl::IXlateDistToValue( float dist, bool clampToInts )
|
|
|
|
{
|
|
|
|
const float artificialBias = 1.f / (float)fNumLevels; // So we never get a howFar less than 0
|
|
|
|
const float artificialMaxDist = 1.f - artificialBias;
|
|
|
|
float howFar, opac;
|
|
|
|
|
|
|
|
|
|
|
|
howFar = IXlateDistToX( dist, clampToInts );
|
|
|
|
|
|
|
|
if( howFar < fStartPercent )
|
|
|
|
opac = fStartOpac;
|
|
|
|
else if( howFar > fEndPercent )
|
|
|
|
opac = fEndOpac;
|
|
|
|
else
|
|
|
|
opac = ( howFar - fStartPercent ) * ( fEndOpac - fStartOpac ) / ( fEndPercent - fStartPercent ) + fStartOpac;
|
|
|
|
|
|
|
|
return opac;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IXlateDistToX ////////////////////////////////////////////////////////////
|
|
|
|
// I.E. from the distance in percentage across the graph to the actual x
|
|
|
|
// value on the graph
|
|
|
|
|
|
|
|
float plDetailCurveCtrl::IXlateDistToX( float dist, bool clampToInts )
|
|
|
|
{
|
|
|
|
const float artificialBias = 1.f / (float)fNumLevels; // So we never get a howFar less than 0
|
|
|
|
const float artificialMaxDist = 1.f - artificialBias;
|
|
|
|
float howFar;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef MCN_TWO_GRAPH_MODE
|
|
|
|
if( fXAsMipmapLevel )
|
|
|
|
{
|
|
|
|
howFar = dist * (float)fNumLevels;
|
|
|
|
if( clampToInts )
|
|
|
|
howFar = (float)( (int)howFar );
|
|
|
|
|
|
|
|
howFar /= (float)fNumLevels;
|
|
|
|
return howFar;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if( dist == 0.f )
|
|
|
|
howFar = 0.f;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
howFar = 1.f - ( ( 1.f - dist ) * artificialMaxDist );
|
|
|
|
howFar = ( (float)fNumLevels - 1.f / howFar );
|
|
|
|
if( howFar < 0.f )
|
|
|
|
howFar = 0.f;
|
|
|
|
else if( howFar > (float)fNumLevels - 1.f )
|
|
|
|
howFar = (float)fNumLevels - 1.f;
|
|
|
|
|
|
|
|
if( clampToInts )
|
|
|
|
howFar = (float)( (int)howFar );
|
|
|
|
|
|
|
|
howFar /= (float)fNumLevels - 1.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return howFar;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IXlateXToDist ////////////////////////////////////////////////////////////
|
|
|
|
// I.E. from the actual x value of the graph to the actual distance in
|
|
|
|
// percentage across the graph.
|
|
|
|
|
|
|
|
float plDetailCurveCtrl::IXlateXToDist( float howFar )
|
|
|
|
{
|
|
|
|
const float artificialBias = 1.f / (float)fNumLevels; // So we never get a howFar less than 0
|
|
|
|
const float artificialMaxDist = 1.f - artificialBias;
|
|
|
|
float dist;
|
|
|
|
|
|
|
|
|
|
|
|
#ifdef MCN_TWO_GRAPH_MODE
|
|
|
|
if( fXAsMipmapLevel )
|
|
|
|
{
|
|
|
|
return howFar;
|
|
|
|
}
|
|
|
|
#endif
|
|
|
|
|
|
|
|
if( howFar == 0.f )
|
|
|
|
dist = 0.f;
|
|
|
|
else
|
|
|
|
{
|
|
|
|
howFar *= (float)fNumLevels - 1.f;
|
|
|
|
howFar = 1.f / ( (float)fNumLevels - howFar );
|
|
|
|
howFar = ( ( howFar - 1.f ) / artificialMaxDist ) + 1.f;
|
|
|
|
}
|
|
|
|
|
|
|
|
return howFar;
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IXlateValuesToClientPt ///////////////////////////////////////////////////
|
|
|
|
// I.E. from graph x,y values to client coordinates
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::IXlateValuesToClientPt( float x, float y, POINT *pt, int cornerX, int cornerY, SIZE *bgndSize )
|
|
|
|
{
|
|
|
|
pt->x = cornerX + (int)( IXlateXToDist( x ) * (float)bgndSize->cx );
|
|
|
|
pt->y = cornerY + bgndSize->cy;
|
|
|
|
|
|
|
|
pt->y -= (int)( (float)bgndSize->cy * y );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IMapMouseToValues ////////////////////////////////////////////////////////
|
|
|
|
// Map mouse x,y coordinates in clientspace to graph values. If the last param
|
|
|
|
// is true, maps to the start point, else maps to the end point
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::IMapMouseToValues( int x, int y, bool mapToStart )
|
|
|
|
{
|
|
|
|
BITMAPINFO bmpInfo;
|
|
|
|
int cX, cY, width, height;
|
|
|
|
RECT clientRect;
|
|
|
|
float vX, vY;
|
|
|
|
SIZE bgndSize;
|
|
|
|
|
|
|
|
|
|
|
|
if( fBgndImage == NULL || fDblDC == NULL || !IsWindowEnabled( fHWnd ) )
|
|
|
|
return;
|
|
|
|
|
|
|
|
GetClientRect( fHWnd, &clientRect );
|
|
|
|
width = clientRect.right - clientRect.left;
|
|
|
|
height = clientRect.bottom - clientRect.top;
|
|
|
|
|
|
|
|
bmpInfo.bmiHeader.biSize = sizeof( bmpInfo.bmiHeader );
|
|
|
|
bmpInfo.bmiHeader.biBitCount = 0;
|
|
|
|
GetDIBits( fDblDC, fBgndImage, 0, 0, NULL, &bmpInfo, DIB_RGB_COLORS );
|
|
|
|
bgndSize.cx = bmpInfo.bmiHeader.biWidth;
|
|
|
|
bgndSize.cy = bmpInfo.bmiHeader.biHeight;
|
|
|
|
cX = ( width - bgndSize.cx ) >> 1;
|
|
|
|
cY = ( height - bgndSize.cy ) >> 1;
|
|
|
|
|
|
|
|
bgndSize.cx -= 8;
|
|
|
|
|
|
|
|
// Xlate to graph space and clamp
|
|
|
|
x -= cX;
|
|
|
|
y = bgndSize.cy - ( y - cY );
|
|
|
|
if( x < 0 )
|
|
|
|
x = 0;
|
|
|
|
else if( x > bgndSize.cx )
|
|
|
|
x = bgndSize.cx;
|
|
|
|
if( y < 0 )
|
|
|
|
y = 0;
|
|
|
|
else if( y > bgndSize.cy )
|
|
|
|
y = bgndSize.cy;
|
|
|
|
|
|
|
|
vX = IXlateDistToX( (float)x / (float)bgndSize.cx, false );
|
|
|
|
vY = (float)y / (float)bgndSize.cy;
|
|
|
|
|
|
|
|
if( mapToStart )
|
|
|
|
{
|
|
|
|
fStartPercent = vX;
|
|
|
|
fStartOpac = vY;
|
|
|
|
if( fEndPercent < fStartPercent )
|
|
|
|
{
|
|
|
|
fEndPercent = fStartPercent;
|
|
|
|
ISendDraggedMessage( false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
|
|
|
fEndPercent = vX;
|
|
|
|
fEndOpac = vY;
|
|
|
|
if( fEndPercent < fStartPercent )
|
|
|
|
{
|
|
|
|
fStartPercent = fEndPercent;
|
|
|
|
ISendDraggedMessage( true );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
IRefreshDblBuffer();
|
|
|
|
InvalidateRect( fHWnd, NULL, false );
|
|
|
|
RedrawWindow( fHWnd, NULL, NULL, RDW_UPDATENOW );
|
|
|
|
|
|
|
|
ISendDraggedMessage( mapToStart );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// ISendDraggedMessage //////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::ISendDraggedMessage( bool itWasTheStartPoint )
|
|
|
|
{
|
|
|
|
HWND parent = GetParent( fHWnd );
|
|
|
|
|
|
|
|
|
|
|
|
if( parent == NULL )
|
|
|
|
return;
|
|
|
|
|
|
|
|
SendMessage( parent, PL_DC_POINT_DRAGGED, itWasTheStartPoint ? PL_DC_START_POINT : PL_DC_END_POINT,
|
|
|
|
(LPARAM)this );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// SetStart/EndPoint ////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::SetStartPoint( float percentLevel, float opacity )
|
|
|
|
{
|
|
|
|
fStartPercent = percentLevel;
|
|
|
|
fStartOpac = opacity;
|
|
|
|
IRefreshDblBuffer();
|
|
|
|
InvalidateRect( fHWnd, NULL, false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::SetEndPoint( float percentLevel, float opacity )
|
|
|
|
{
|
|
|
|
fEndPercent = percentLevel;
|
|
|
|
fEndOpac = opacity;
|
|
|
|
IRefreshDblBuffer();
|
|
|
|
InvalidateRect( fHWnd, NULL, false );
|
|
|
|
}
|
|
|
|
|
|
|
|
void plDetailCurveCtrl::SetNumLevels( int numLevels )
|
|
|
|
{
|
|
|
|
fNumLevels = numLevels;
|
|
|
|
IRefreshDblBuffer();
|
|
|
|
InvalidateRect( fHWnd, NULL, false );
|
|
|
|
}
|
|
|
|
|
|
|
|
//// IWndProc /////////////////////////////////////////////////////////////////
|
|
|
|
|
|
|
|
LRESULT CALLBACK plDetailCurveCtrl::IWndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
|
|
|
|
{
|
|
|
|
HDC hDC;
|
|
|
|
RECT clientRect;
|
|
|
|
int width, height;
|
|
|
|
PAINTSTRUCT pInfo;
|
|
|
|
POINT pt;
|
|
|
|
|
|
|
|
|
|
|
|
plDetailCurveCtrl *ctrl = (plDetailCurveCtrl *)GetWindowLong( hWnd, GWL_USERDATA );
|
|
|
|
GetClientRect( hWnd, &clientRect );
|
|
|
|
width = clientRect.right - clientRect.left;
|
|
|
|
height = clientRect.bottom - clientRect.top;
|
|
|
|
|
|
|
|
switch( msg )
|
|
|
|
{
|
|
|
|
case WM_CREATE:
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_ENABLE:
|
|
|
|
if( ctrl != NULL )
|
|
|
|
ctrl->IRefreshDblBuffer();
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_PAINT:
|
|
|
|
BeginPaint( hWnd, &pInfo );
|
|
|
|
hDC = (HDC)pInfo.hdc;
|
|
|
|
|
|
|
|
if( ctrl != NULL )
|
|
|
|
{
|
|
|
|
if( ctrl->fDblDC == NULL )
|
|
|
|
ctrl->IInitDblBuffer();
|
|
|
|
|
|
|
|
BitBlt( hDC, 0, 0, width, height, ctrl->fDblDC, 0, 0, SRCCOPY );
|
|
|
|
}
|
|
|
|
|
|
|
|
EndPaint( hWnd, &pInfo );
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_ERASEBKGND:
|
|
|
|
return TRUE;
|
|
|
|
|
|
|
|
case WM_LBUTTONDOWN:
|
|
|
|
if( ctrl != NULL && !ctrl->fDraggingStart && !ctrl->fDraggingEnd )
|
|
|
|
{
|
|
|
|
pt.x = LOWORD( lParam );
|
|
|
|
pt.y = HIWORD( lParam );
|
|
|
|
if( PtInRect( &ctrl->fStartDragPt, pt ) )
|
|
|
|
{
|
|
|
|
if( !ctrl->fCanDragStart && !ctrl->fCanDragEnd )
|
|
|
|
SetCapture( hWnd );
|
|
|
|
ctrl->fDraggingStart = true;
|
|
|
|
}
|
|
|
|
else if( PtInRect( &ctrl->fEndDragPt, pt ) )
|
|
|
|
{
|
|
|
|
if( !ctrl->fCanDragStart && !ctrl->fCanDragEnd )
|
|
|
|
SetCapture( hWnd );
|
|
|
|
ctrl->fDraggingEnd = true;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_MOUSEMOVE:
|
|
|
|
if( ctrl != NULL )
|
|
|
|
{
|
|
|
|
pt.x = LOWORD( lParam );
|
|
|
|
pt.y = HIWORD( lParam );
|
|
|
|
|
|
|
|
if( ctrl->fDraggingStart || ctrl->fDraggingEnd )
|
|
|
|
{
|
|
|
|
ctrl->IMapMouseToValues( (short)LOWORD( lParam ), (short)HIWORD( lParam ), ctrl->fDraggingStart );
|
|
|
|
}
|
|
|
|
else if( PtInRect( &ctrl->fStartDragPt, pt ) )
|
|
|
|
{
|
|
|
|
if( !ctrl->fCanDragStart )
|
|
|
|
{
|
|
|
|
ctrl->fCanDragStart = true;
|
|
|
|
ctrl->fCanDragEnd = false;
|
|
|
|
SetCapture( hWnd );
|
|
|
|
ctrl->IRefreshDblBuffer();
|
|
|
|
InvalidateRect( hWnd, NULL, false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( PtInRect( &ctrl->fEndDragPt, pt ) )
|
|
|
|
{
|
|
|
|
if( !ctrl->fCanDragEnd )
|
|
|
|
{
|
|
|
|
ctrl->fCanDragEnd = true;
|
|
|
|
ctrl->fCanDragStart = false;
|
|
|
|
SetCapture( hWnd );
|
|
|
|
ctrl->IRefreshDblBuffer();
|
|
|
|
InvalidateRect( hWnd, NULL, false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else if( ctrl->fCanDragStart || ctrl->fCanDragEnd )
|
|
|
|
{
|
|
|
|
ctrl->fCanDragStart = false;
|
|
|
|
ctrl->fCanDragEnd = false;
|
|
|
|
ReleaseCapture();
|
|
|
|
ctrl->IRefreshDblBuffer();
|
|
|
|
InvalidateRect( hWnd, NULL, false );
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
case WM_LBUTTONUP:
|
|
|
|
if( ctrl != NULL && ( ctrl->fDraggingStart || ctrl->fDraggingEnd ) )
|
|
|
|
{
|
|
|
|
if( !ctrl->fCanDragStart && !ctrl->fCanDragEnd )
|
|
|
|
ReleaseCapture();
|
|
|
|
ctrl->fDraggingStart = false;
|
|
|
|
ctrl->fDraggingEnd = false;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
#ifdef MCN_TWO_GRAPH_MODE
|
|
|
|
case WM_RBUTTONDOWN:
|
|
|
|
fXAsMipmapLevel = !fXAsMipmapLevel;
|
|
|
|
ctrl->IRefreshDblBuffer();
|
|
|
|
InvalidateRect( hWnd, NULL, false );
|
|
|
|
return 0;
|
|
|
|
#endif
|
|
|
|
|
|
|
|
case WM_DESTROY:
|
|
|
|
delete ctrl;
|
|
|
|
SetWindowLong( hWnd, GWL_USERDATA, 0 );
|
|
|
|
return 0;
|
|
|
|
|
|
|
|
default:
|
|
|
|
return DefWindowProc( hWnd, msg, wParam, lParam );
|
|
|
|
}
|
|
|
|
}
|