khanat-opennel-code/code/nel/tools/3d/object_viewer/curve_edit.cpp

471 lines
10 KiB
C++

// NeL - MMORPG Framework <http://dev.ryzom.com/projects/nel/>
// Copyright (C) 2010 Winch Gate Property Limited
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU Affero 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 Affero General Public License for more details.
//
// You should have received a copy of the GNU Affero General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
#include "std_afx.h"
#include "object_viewer.h"
#include "curve_edit.h"
#include "editable_range.h"
#include "nel/3d/ps_float.h"
#include "nel/misc/common.h"
#include "nel/misc/fast_floor.h"
static const uint CtrlPointSize = 3;
CurveEdit::CurveEdit(NL3D::CPSFloatCurveFunctor *curve, CParticleWorkspace::CNode *ownerNode, IPopupNotify *pn, CWnd* pParent /*=NULL*/)
: CDialog(CurveEdit::IDD, pParent),
_Node(ownerNode),
_PN(pn),
Curve(curve),
_State(Create),
_X(10),
_Y(10),
_Width(350),
_Height(200),
_SelectedCtrlPoint(-1),
_NumSamplesDlg(NULL)
{
nlassert(Curve);
//{{AFX_DATA_INIT(CurveEdit)
m_DisplayInterpolation = FALSE;
m_SmoothingOn = FALSE;
//}}AFX_DATA_INIT
scaleMinMax();
}
CurveEdit::~CurveEdit()
{
if (_NumSamplesDlg)
{
_NumSamplesDlg->DestroyWindow();
delete _NumSamplesDlg;
}
}
void CurveEdit::DoDataExchange(CDataExchange* pDX)
{
CDialog::DoDataExchange(pDX);
//{{AFX_DATA_MAP(CurveEdit)
DDX_Check(pDX, IDC_DISPLAY_INTERPOLATION, m_DisplayInterpolation);
DDX_Check(pDX, IDC_SMOOTHING_ON, m_SmoothingOn);
//}}AFX_DATA_MAP
}
BEGIN_MESSAGE_MAP(CurveEdit, CDialog)
//{{AFX_MSG_MAP(CurveEdit)
ON_WM_MOUSEMOVE()
ON_WM_LBUTTONUP()
ON_WM_LBUTTONDOWN()
ON_WM_PAINT()
ON_BN_CLICKED(IDC_ZOOM_OUT, OnZoomOut)
ON_BN_CLICKED(IDC_ZOOM_IN, OnZoomIn)
ON_BN_CLICKED(IDC_GO_UP, OnGoUp)
ON_BN_CLICKED(IDC_GO_DOWN, OnGoDown)
ON_BN_CLICKED(IDC_MOVE_POINT, OnMovePoint)
ON_BN_CLICKED(IDC_ADD_POINT, OnAddPoint)
ON_BN_CLICKED(IDC_REMOVE_POINT, OnRemovePoint)
ON_BN_CLICKED(IDC_DISPLAY_INTERPOLATION, OnDisplayInterpolation)
ON_BN_CLICKED(IDC_SMOOTHING_ON, OnSmoothingOn)
ON_BN_CLICKED(IDC_LAST_EQUAL_FIRST, OnLastEqualFirst)
ON_BN_CLICKED(IDC_CENTER_CURVE, OnCenterCurve)
ON_BN_CLICKED(IDC_FIRST_EQUAL_LAST, OnFirstEqualLast)
ON_WM_DESTROY()
ON_WM_CLOSE()
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
/////////////////////////////////////////////////////////////////////////////
// CurveEdit message handlers
void CurveEdit::OnMouseMove(UINT nFlags, CPoint point)
{
switch (_State)
{
case Move:
case Remove:
{
sint cpIndex = intersectCtrlPoint(point.x, point.y);
if (cpIndex != _SelectedCtrlPoint)
{
_SelectedCtrlPoint = cpIndex;
Invalidate();
}
}
break;
case Moving:
{
Curve->setCtrlPoint(_SelectedCtrlPoint,coordsFromScreen(point.x, point.y));
_SelectedCtrlPoint = intersectCtrlPoint(point.x, point.y); // the index of the point we are moving may have changed
if (_SelectedCtrlPoint == -1) _State = Move;
Invalidate();
}
break;
}
CDialog::OnMouseMove(nFlags, point);
}
void CurveEdit::OnLButtonUp(UINT nFlags, CPoint point)
{
switch (_State)
{
case Moving:
_State = Move;
_SelectedCtrlPoint = intersectCtrlPoint(point.x, point.y);
Invalidate();
break;
case Removing:
_State = Remove;
break;
case Created:
_State = Create;
break;
}
CDialog::OnLButtonUp(nFlags, point);
}
void CurveEdit::OnLButtonDown(UINT nFlags, CPoint point)
{
switch (_State)
{
case Move:
{
if (_SelectedCtrlPoint != -1) _State = Moving;
}
break;
case Create:
{
Curve->addControlPoint(coordsFromScreen(point.x, point.y));
_State = Created;
invalidate();
}
case Remove:
{
if ( _SelectedCtrlPoint == -1 || Curve->getNumCtrlPoints() == 2 ) return;
Curve->removeCtrlPoint(_SelectedCtrlPoint);
_State= Removing;
_SelectedCtrlPoint = -1;
invalidate();
}
break;
}
CDialog::OnLButtonDown(nFlags, point);
}
void CurveEdit::OnPaint()
{
UpdateData();
CPaintDC dc(this); // device context for painting
drawBackGround(dc);
drawUnits(dc);
drawCurve(dc);
if (m_DisplayInterpolation) drawInterpolatingCurve(dc);
drawCtrlPoints(dc);
}
void CurveEdit::setupBoundRect(CDC &dc)
{
CRgn rgn;
rgn.CreateRectRgn(_X, _X, _X + _Width, _Y + _Height);
dc.SelectClipRgn(&rgn, RGN_COPY );
}
void CurveEdit::drawBackGround(CDC &dc)
{
setupBoundRect(dc);
dc.FillSolidRect(_X, _Y, _Width, _Height, 0xffffff);
}
POINT CurveEdit::makePoint(float date, float value) const
{
POINT p;
p.x = (int) (_X + date * _Width);
p.y = (int) (_Y + _Height - _Scale * _Height * (value - _Origin));
return p;
}
sint CurveEdit::intersectCtrlPoint(sint x, sint y)
{
uint numPoints = Curve->getNumCtrlPoints();
for (uint k = 0; k < numPoints; ++k)
{
const CCtrlPoint &cp = Curve->getControlPoint(k);
POINT p = makePoint(cp.Date, cp.Value);
if ( x >= (sint) (p.x - CtrlPointSize)
&& x <= (sint) (p.x + CtrlPointSize)
&& y >= (sint) (p.y - CtrlPointSize)
&& y <= (sint) (p.y + CtrlPointSize) )
{
return k;
}
}
return -1;
}
CurveEdit::CCtrlPoint CurveEdit::coordsFromScreen(sint x, sint y) const
{
float date =(x - _X) / (float) _Width;
NLMISC::clamp(date, 0.f, 1.f);
CCtrlPoint pos(
date,
_Origin + (_Y + _Height - y) / (_Scale * _Height)
);
return pos;
}
void CurveEdit::drawCurve(CDC &dc)
{
setupBoundRect(dc);
CPen pen;
pen.CreatePen(PS_SOLID, 1, (COLORREF) 0x000000);
CGdiObject *oldPen = dc.SelectObject(&pen);
dc.MoveTo(makePoint(0, Curve->getValue(0)));
for (sint x = 0; x < _Width; ++x)
{
const float date = x / (float) _Width;
dc.LineTo(_X + x, makePoint(date, Curve->getValue(date)).y);
}
dc.SelectObject(oldPen);
}
void CurveEdit::drawInterpolatingCurve(CDC &dc)
{
setupBoundRect(dc);
CPen pen;
pen.CreatePen(PS_SOLID, 1, (COLORREF) 0x772211);
CGdiObject *oldPen = dc.SelectObject(&pen);
dc.MoveTo(makePoint(0, getSampledValue(0)));
for (sint x = 0; x < _Width; ++x)
{
const float date = x / (float) _Width;
dc.LineTo(_X + x, makePoint(date, getSampledValue(date)).y);
}
dc.SelectObject(oldPen);
}
void CurveEdit::scaleMinMax()
{
float minValue, maxValue;
maxValue = minValue = getSampledValue(0);
for (sint x = 1; x < _Width; ++x)
{
const float date = x / (float) _Width;
const float value = getSampledValue(date);
minValue = std::min(minValue, value);
maxValue = std::max(maxValue, value);
}
_Origin = (maxValue == minValue) ? minValue - .5f : minValue;
_Scale = (maxValue == minValue) ? 1.f : 1.f / (maxValue - minValue);
}
void CurveEdit::drawCtrlPoints(CDC &dc)
{
setupBoundRect(dc);
CPen pens[2];
pens[0].CreatePen(PS_SOLID, 1, (COLORREF) 0x00ff00);
pens[1].CreatePen(PS_SOLID, 1, (COLORREF) 0x0000ff);
uint numPoints = Curve->getNumCtrlPoints();
for (uint k = 0; k < numPoints; ++k)
{
CGdiObject *oldPen = dc.SelectObject(&pens[k == (uint) _SelectedCtrlPoint ? 1 : 0]); // slect the red pen if thi ctrl point is selected
const CCtrlPoint &cp = Curve->getControlPoint(k);
POINT p = makePoint(cp.Date, cp.Value);
dc.MoveTo(p.x- CtrlPointSize, p.y - CtrlPointSize);
dc.LineTo(p.x + CtrlPointSize, p.y - CtrlPointSize);
dc.LineTo(p.x + CtrlPointSize, p.y + CtrlPointSize);
dc.LineTo(p.x - CtrlPointSize, p.y + CtrlPointSize);
dc.LineTo(p.x - CtrlPointSize, p.y - CtrlPointSize);
dc.SelectObject(oldPen);
}
}
void CurveEdit::OnZoomOut()
{
_Scale *= 1.5f;
Invalidate();
}
void CurveEdit::OnZoomIn()
{
_Scale *= 0.5f;
Invalidate();
}
void CurveEdit::OnGoUp()
{
_Origin += (1.f / _Scale) * 0.25f;
Invalidate();
}
void CurveEdit::OnGoDown()
{
_Origin -= (1.f / _Scale) * 0.25f;
Invalidate();
}
void CurveEdit::drawUnits(CDC &dc)
{
CPen pens[2];
pens[0].CreatePen(PS_SOLID, 1, (COLORREF) 0xaaaaaa);
pens[1].CreatePen(PS_SOLID, 1, (COLORREF) 0xff0011);
sint upVal = (sint) floorf(coordsFromScreen(0, _X).Value);
sint downVal = (sint) floorf(coordsFromScreen(0, _X + _Width).Value);
nlassert(upVal >= downVal);
for (sint k = downVal ; k <= upVal ; ++k)
{
CGdiObject *oldPen = dc.SelectObject(&pens[k == 0 ? 1 : 0]);
dc.MoveTo(makePoint(0, (float) k));
dc.LineTo(makePoint(1, (float) k));
dc.SelectObject(oldPen);
}
}
BOOL CurveEdit::OnInitDialog()
{
CDialog::OnInitDialog();
((CButton *) GetDlgItem(IDC_ADD_POINT))->SetCheck(1);
_NumSamplesDlg = new CEditableRangeUInt(std::string("FLOAT_CURVE_NB_SAMPLE"), _Node, 1, 512);
_NumSampleWrapper.CE = this;
_NumSamplesDlg->setWrapper(&_NumSampleWrapper);
_NumSamplesDlg->init(80, 225, this);
_NumSamplesDlg->enableLowerBound(1, true);
m_SmoothingOn = Curve->hasSmoothing();
UpdateData(FALSE);
return TRUE; // return TRUE unless you set the focus to a control
// EXCEPTION: OCX Property Pages should return FALSE
}
void CurveEdit::OnMovePoint()
{
_State = Move;
_SelectedCtrlPoint = -1;
Invalidate();
}
void CurveEdit::OnAddPoint()
{
_State = Create;
_SelectedCtrlPoint = -1;
Invalidate();
}
void CurveEdit::OnRemovePoint()
{
_State = Remove;
_SelectedCtrlPoint = -1;
Invalidate();
}
void CurveEdit::OnDisplayInterpolation()
{
Invalidate();
}
float CurveEdit::getSampledValue(float date) const
{
nlassert(Curve);
nlassert(date >=0 && date < 1);
NLMISC::OptFastFloorBegin();
float result = (*Curve)(date);
NLMISC::OptFastFloorEnd();
return result;
}
void CurveEdit::OnSmoothingOn()
{
UpdateData();
nlassert(Curve);
Curve->enableSmoothing(m_SmoothingOn ? true : false /* perf; warning */);
invalidate();
}
void CurveEdit::OnLastEqualFirst()
{
CCtrlPoint pt = Curve->getControlPoint(0);
pt.Date = Curve->getControlPoint(Curve->getNumCtrlPoints() - 1).Date;
Curve->setCtrlPoint(Curve->getNumCtrlPoints() - 1, pt);
invalidate();
}
void CurveEdit::OnCenterCurve()
{
scaleMinMax();
Invalidate();
}
void CurveEdit::OnFirstEqualLast()
{
CCtrlPoint pt = Curve->getControlPoint(Curve->getNumCtrlPoints() - 1);
pt.Date = Curve->getControlPoint(0).Date;
Curve->setCtrlPoint(0, pt);
invalidate();
}
void CurveEdit::OnDestroy()
{
CDialog::OnDestroy();
}
void CurveEdit::init(CWnd *pParent)
{
CDialog::Create(IDD_CURVE_EDIT, pParent);
ShowWindow(SW_SHOW);
}
void CurveEdit::OnClose()
{
CDialog::OnClose();
_PN->childPopupClosed(this);
}
void CurveEdit::invalidate()
{
if (_Node)
{
_Node->setModified(true);
}
Invalidate();
}