|
#include "CurveEdit.h"
|
|
#include "../../SDK/PlatformIndependent/Core/Timer.h"
|
|
|
|
#include <Wt/WStandardItemModel>
|
|
#include <Wt/WPointF>
|
|
#include <Wt/WStandardItem>
|
|
|
|
using namespace Wt;
|
|
using namespace Wt::Chart;
|
|
using namespace TVNG;
|
|
|
|
//-------------------------------------------------------------------------
|
|
CurveEdit::CurveEdit(const std::string& label)
|
|
:
|
|
WCartesianChart(),
|
|
label_(label),
|
|
selectedRowIndex_(-1),
|
|
lastEmitTime_(0)
|
|
{
|
|
model_ = new WStandardItemModel(0, 3);
|
|
labelFont_.setFamily(WFont::SansSerif);
|
|
labelFont_.setSize(WFont::FixedSize, WLength(7, WLength::Point));
|
|
|
|
// needed to capture key events (HTML5)
|
|
setAttributeValue("tabindex", "0");
|
|
|
|
setModel(model_);
|
|
setXSeriesColumn(0);
|
|
setLegendEnabled(false);
|
|
setType(ScatterPlot);
|
|
|
|
WPen pen;
|
|
pen.setColor(WColor(0, 0, 0, 100));
|
|
pen.setWidth(1);
|
|
|
|
WAxis& xAxis = axis(XAxis);
|
|
WAxis& yAxis = axis(YAxis);
|
|
xAxis.setLocation(ZeroValue);
|
|
yAxis.setLocation(ZeroValue);
|
|
|
|
WFont labelFont;
|
|
labelFont.setFamily(WFont::SansSerif);
|
|
labelFont.setSize(WFont::FixedSize, WLength(0, WLength::Point));
|
|
|
|
xAxis.setLabelFont(labelFont);
|
|
yAxis.setLabelFont(labelFont);
|
|
xAxis.setLabelFormat("%.2f");
|
|
yAxis.setLabelFormat("%.2f");
|
|
xAxis.setLabelInterval(0.05);
|
|
yAxis.setLabelInterval(0.05);
|
|
xAxis.setRange(-0.1, 1.1);
|
|
yAxis.setRange(-0.1, 1.1);
|
|
xAxis.setGridLinesEnabled(true);
|
|
yAxis.setGridLinesEnabled(true);
|
|
xAxis.setGridLinesPen(pen);
|
|
yAxis.setGridLinesPen(pen);
|
|
|
|
setPlotAreaPadding(0, Left);
|
|
setPlotAreaPadding(0, Right);
|
|
setPlotAreaPadding(0, Bottom);
|
|
setPlotAreaPadding(0, Top);
|
|
|
|
setMargin(5, Top | Bottom);
|
|
setMargin(WLength::Auto, Left | Right);
|
|
setBackground(WBrush(WColor(150, 150, 150)));
|
|
|
|
// Add the curves
|
|
WDataSeries s(1, LineSeries);
|
|
s.setShadow(WShadow(3, 3, WColor(0, 0, 0, 127), 2));
|
|
s.setMarker(Chart::SquareMarker);
|
|
s.setMarkerSize(5);
|
|
s.setMarkerPen(WPen(WColor(0, 0, 0)));
|
|
s.setMarkerBrush(WBrush(WColor(255, 255, 0)));
|
|
s.setPen(WPen(WColor(0, 255, 0, 255)));
|
|
|
|
s.setLabelsEnabled(XAxis);
|
|
s.setLabelsEnabled(YAxis);
|
|
addSeries(s);
|
|
|
|
WDataSeries selection(2, PointSeries);
|
|
selection.setMarker(Chart::SquareMarker);
|
|
selection.setMarkerPen(WPen(WColor(0, 0, 0)));
|
|
selection.setMarkerBrush(WBrush(WColor(255, 0, 0)));
|
|
selection.setMarkerSize(5);
|
|
addSeries(selection);
|
|
|
|
mouseWentDown().connect(this, &CurveEdit::MouseWentDown);
|
|
doubleClicked().connect(this, &CurveEdit::DoubleClicked);
|
|
mouseDragged().connect(this, &CurveEdit::MouseDragged);
|
|
keyWentUp().connect(this, &CurveEdit::KeyWentUp);
|
|
|
|
resize(300, 135);
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
CurveEdit::~CurveEdit()
|
|
{
|
|
delete model_;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CurveEdit::DoubleClicked(const WMouseEvent& e)
|
|
{
|
|
if (e.button() != WMouseEvent::LeftButton) return;
|
|
|
|
WPointF pointF = mapFromDevice(WPointF(e.widget().x, e.widget().y));
|
|
Vector2f p( static_cast<float>(pointF.x()), static_cast<float>(pointF.y()) );
|
|
if (p.X() < 0.) p.SetX(0.);
|
|
if (p.X() > 1.) p.SetX(1.);
|
|
if (p.Y() < 0.) p.SetY(0.);
|
|
if (p.Y() > 1.) p.SetY(1.);
|
|
|
|
for (int i(0); i<model_->rowCount(); i++)
|
|
{
|
|
Vector2f elem( boost::any_cast<float>(model_->data(i, 0)), boost::any_cast<float>(model_->data(i, 1)) );
|
|
if (SamePoints(p, elem)) return;
|
|
if (p.X() == elem.X()) return;
|
|
}
|
|
|
|
int row = model_->rowCount();
|
|
model_->insertRow(row);
|
|
model_->setData(row, 0, boost::any(p.X()));
|
|
model_->setData(row, 1, boost::any(p.Y()));
|
|
model_->setData(row, 2, boost::any(p.Y()));
|
|
|
|
if (selectedRowIndex_ != -1)
|
|
model_->setData(selectedRowIndex_, 2, boost::any());
|
|
model_->sort(0);
|
|
for (int i(0); i<model_->rowCount(); i++)
|
|
{
|
|
float x( boost::any_cast<float>(model_->data(i, 0)) );
|
|
if (x == p.X())
|
|
{
|
|
selectedRowIndex_ = i;
|
|
break;
|
|
}
|
|
}
|
|
UpdateCurve();
|
|
changed_.emit();
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CurveEdit::MouseWentDown(const WMouseEvent& e)
|
|
{
|
|
if (e.button() != WMouseEvent::LeftButton) return;
|
|
WPointF pointF = mapFromDevice(WPointF(e.widget().x, e.widget().y));
|
|
Vector2f p( static_cast<float>(pointF.x()), static_cast<float>(pointF.y()) );
|
|
if (p.X() < 0.) p.SetX(0.);
|
|
if (p.X() > 1.) p.SetX(1.);
|
|
if (p.Y() < 0.) p.SetY(0.);
|
|
if (p.Y() > 1.) p.SetY(1.);
|
|
|
|
int row(-1);
|
|
for (int i(0); i<model_->rowCount(); i++)
|
|
{
|
|
Vector2f elem( boost::any_cast<float>(model_->data(i, 0)), boost::any_cast<float>(model_->data(i, 1)) );
|
|
if (SamePoints(elem, p))
|
|
{
|
|
row = i;
|
|
p = elem;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (selectedRowIndex_ != -1 && row != selectedRowIndex_)
|
|
model_->setData(selectedRowIndex_, 2, boost::any());
|
|
|
|
if (row != -1 && selectedRowIndex_ != row)
|
|
model_->setData(row, 2, boost::any(p.Y()));
|
|
selectedRowIndex_ = row;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CurveEdit::MouseDragged(const WMouseEvent& e)
|
|
{
|
|
if (e.button() != WMouseEvent::LeftButton) return;
|
|
if (selectedRowIndex_ == -1) return;
|
|
|
|
WPointF pointF = mapFromDevice(WPointF(e.widget().x, e.widget().y));
|
|
Vector2f p( static_cast<float>(pointF.x()), static_cast<float>(pointF.y()) );
|
|
if (p.X() < 0.) p.SetX(0.);
|
|
if (p.X() > 1.) p.SetX(1.);
|
|
if (p.Y() < 0.) p.SetY(0.);
|
|
if (p.Y() > 1.) p.SetY(1.);
|
|
|
|
for (int i(0); i<model_->rowCount(); i++)
|
|
{
|
|
if (i == selectedRowIndex_) continue;
|
|
float x = boost::any_cast<float>(model_->data(i, 0));
|
|
if (x == p.X()) return;
|
|
}
|
|
|
|
model_->setData(selectedRowIndex_, 0, boost::any(p.X()));
|
|
model_->setData(selectedRowIndex_, 1, boost::any(p.Y()));
|
|
model_->setData(selectedRowIndex_, 2, boost::any(p.Y()));
|
|
model_->sort(0);
|
|
|
|
for (int i(0); i<model_->rowCount(); i++)
|
|
{
|
|
float x( boost::any_cast<float>(model_->data(i, 0)) );
|
|
if (x == p.X())
|
|
{
|
|
selectedRowIndex_ = i;
|
|
break;
|
|
}
|
|
}
|
|
UpdateCurve();
|
|
double emitTime( Timer::DGetTime() );
|
|
if (emitTime - lastEmitTime_ > 0.04)
|
|
{
|
|
lastEmitTime_ = emitTime;
|
|
changed_.emit();
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CurveEdit::KeyWentUp(const WKeyEvent& e)
|
|
{
|
|
if (selectedRowIndex_ == -1) return;
|
|
|
|
if (e.key() == Wt::Key_Delete)
|
|
{
|
|
model_->removeRow(selectedRowIndex_);
|
|
selectedRowIndex_ = -1;
|
|
UpdateCurve();
|
|
changed_.emit();
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CurveEdit::SetCurve( const FloatAnimationCurvePtr& curve )
|
|
{
|
|
model_->removeRows(0, model_->rowCount());
|
|
if (curve != NULL)
|
|
{
|
|
for (unsigned int i(0); i<curve->GetNumKeyFrames(); i++)
|
|
{
|
|
int row = model_->rowCount();
|
|
model_->insertRow(row);
|
|
const std::pair< float, float >& keyframe = curve->GetKeyFrame(i);
|
|
model_->setData(row, 0, boost::any(keyframe.first));
|
|
model_->setData(row, 1, boost::any(keyframe.second));
|
|
model_->setData(row, 2, boost::any());
|
|
}
|
|
}
|
|
UpdateCurve();
|
|
selectedRowIndex_ = -1;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CurveEdit::UpdateCurve()
|
|
{
|
|
curve_.RemoveAllKeyFrames();
|
|
for (int i(0); i<model_->rowCount(); i++)
|
|
{
|
|
Vector2f elem( boost::any_cast<float>(model_->data(i, 0)), boost::any_cast<float>(model_->data(i, 1)) );
|
|
curve_.AddKeyFrame(elem.X(), elem.Y());
|
|
}
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
bool CurveEdit::SamePoints(const Vector2f& p1, const Vector2f& p2)
|
|
{
|
|
float d = sqrt((p1.X() - p2.X()) * (p1.X() - p2.X()) + (p1.Y() - p2.Y()) * (p1.Y() - p2.Y()));
|
|
if (d < 0.05) return true;
|
|
return false;
|
|
}
|
|
|
|
//-------------------------------------------------------------------------
|
|
void CurveEdit::paint(Wt::WPainter& painter, const Wt::WRectF& rectangle) const
|
|
{
|
|
painter.setFont(labelFont_);
|
|
WCartesianChart::paint(painter, rectangle);
|
|
}
|