我目前正在为vtkChartXY
中的几行设置动画,但是对于我的简单任务而言,我的表现却很差。我想知道的是,如何有效地更新绘图数据,以及应该使用哪种数据结构?我目前有一个QTimer
(来自Qt的计时器),它会触发更新帧号并发送更新信号,该信号进入我的DepthChartRenderer::update
函数,表示存在更新。然后,另一个计时器发出信号的自定义vtkCommand
会根据第一个计时器的更新来更新图表数据。我不确定我的系统在哪里崩溃了,但是对于我的测试仪来说,性能却很糟糕(大约是30fps时的一半)。
这是DepthChartRenderer
的头文件:
class DepthChartRenderer
{
public:
DepthChartRenderer(DepthChartView * depthChartView);
/*
* Initialize chart data structures and fill in data
*/
void init();
/*
* Gives the signal for an update to occur.
*/
void update();
/*
* Returns whether the view is currently visible to the user.
*/
bool visible() const;
/*
* Displays the visualization to the user
*/
void show();
/*
* Hides the visualizations from the user, destroying the window
*/
void hide();
private:
/*
* Update based on variables in the view.
*/
bool updateExists;
std::mutex updateMutex;
/*
* Updates the frame. Should only be called from the vtk command
*/
void updateFrame();
class UpdateFrameCallback : public vtkCommand
{
public:
static UpdateFrameCallback * New()
{
UpdateFrameCallback * cb = new UpdateFrameCallback;
return cb;
}
virtual void Execute(vtkObject * caller, unsigned long eventId, void * vtkNotUsed(callData));
DepthChartRenderer * depthChart;
};
friend class UpdateFrameCallback;
DepthChartView * m_depthChartView = nullptr;
uint m_realTime;
void initChart();
void initBpDTable();
void initCpDTables();
std::map<std::string, std::vector<uint>> m_bpdAnimationFrames;
std::map<std::string, std::vector<uint>> m_cpdAnimationFrames;
vtkSmartPointer<vtkTable> m_bpdTable;
std::vector<std::vector<vtkSmartPointer<vtkTable>>> m_cpdTables;
vtkSmartPointer<vtkChartXY> m_chart;
vtkSmartPointer<vtkContextView> m_view;
vtkPlot * m_bpdLine;
std::vector<std::vector<vtkPlot *>> m_cpdLines;
struct Color {
uint r;
uint g;
uint b;
};
std::vector<Color> colors{{255, 0, 0}, {0, 255, 0}, {0, 0, 255}};
bool m_firstRender = true;
};
和来源(缩写):
DepthChartRenderer::DepthChartRenderer(DepthChartView * depthChartView)
: updateExists(true),
m_depthChartView(depthChartView),
m_realTime(0),
m_bpdAnimationFrames(),
m_cpdAnimationFrames(),
m_bpdLine(0),
m_cpdLines()
{
}
void DepthChartRenderer::init()
{
std::cout << "Init" << std::endl;
m_view = vtkSmartPointer<vtkContextView>::New();
m_view->GetRenderer()->SetBackground(1.0, 1.0, 1.0);
initChart();
initBpDTable();
initCpDTables();
m_view->GetScene()->AddItem(m_chart);
m_view->GetRenderWindow()->SetMultiSamples(0);
m_view->GetInteractor()->Initialize();
vtkSmartPointer<UpdateFrameCallback> cb = vtkSmartPointer<UpdateFrameCallback>::New();
cb->depthChart = this;
m_view->GetInteractor()->AddObserver(vtkCommand::TimerEvent, cb);
m_view->GetInteractor()->CreateRepeatingTimer(1000 / 60);
m_view->GetInteractor()->Start();
}
void DepthChartRenderer::initChart()
{
m_chart = vtkSmartPointer<vtkChartXY>::New();
m_chart->GetAxis(vtkAxis::BOTTOM)->SetTitle("Depth");
m_chart->GetAxis(vtkAxis::LEFT)->SetTitle("#BT");
}
void DepthChartRenderer::initBpDTable()
{
m_bpdTable = vtkSmartPointer<vtkTable>::New();
vtkSmartPointer<vtkFloatArray> arrX = vtkSmartPointer<vtkFloatArray>::New();
arrX->SetName("X Axis");
m_bpdTable->AddColumn(arrX);
// Fill in the table with some example values
int numPoints = 1;
int numRows = 1;
if (m_depthChartView != nullptr && m_depthChartView->model() != nullptr) {
numRows = m_depthChartView->bpdSource()->size();
numPoints = m_depthChartView->bpdSource()->at(0).second.size();
}
// Add a column for each DepthChart Frame
for (int i = 0; i < numRows; ++i) {
vtkSmartPointer<vtkFloatArray> arrC = vtkSmartPointer<vtkFloatArray>::New();
auto name = "DepthChart-BpD-" + std::to_string(i);
arrC->SetName(name.c_str());
m_bpdTable->AddColumn(arrC);
}
m_bpdTable->SetNumberOfRows(numPoints);
// Fill out x-axis data
for (int j = 0; j < numPoints; ++j) {
m_bpdTable->SetValue(j, 0, j);
}
// Fill out entire table with all DepthChart data
std::vector<uint> data = {0};
for (int i = 0; i < numRows; ++i) {
data = m_depthChartView->bpdSource()->at(i).second;
for (int j = 0; j < numPoints; ++j) {
m_bpdTable->SetValue(j, i + 1, data[j]);
}
}
}
void DepthChartRenderer::initCpDTables()
{
// Same as initBpDTable for more than one table
}
void DepthChartRenderer::update()
{
// Assumes that the dataMutex has already been locked
// Copy animation frames
for (auto entry : m_depthChartView->m_bpdAnimationFrames) {
m_bpdAnimationFrames[entry.first] = entry.second;
}
m_depthChartView->m_bpdAnimationFrames.clear();
for (auto entry : m_depthChartView->m_cpdAnimationFrames) {
m_cpdAnimationFrames[entry.first] = entry.second;
}
std::lock_guard<std::mutex> guard(updateMutex);
updateExists = true;
}
void DepthChartRenderer::updateFrame()
{
// Update existence of BpD plot
if (m_bpdLine == nullptr && m_depthChartView->model()->showBpD()) {
m_bpdLine = m_chart->AddPlot(vtkChart::LINE);
m_bpdLine->SetColor(255, 0, 0, 255);
m_bpdLine->SetWidth(2.0);
} else if (m_bpdLine != nullptr && !m_depthChartView->model()->showBpD()) {
m_chart->RemovePlot(m_chart->GetPlotIndex(m_bpdLine));
m_bpdLine->Delete();
m_bpdLine = nullptr;
}
// Update existence of CpD plot
if (m_cpdLines.size() == 0 && m_depthChartView->model()->showCpD()) {
for (uint lineIndex = 0; lineIndex < m_cpdTables.size(); ++lineIndex) {
std::vector<vtkPlot *> algoLines;
for (uint algoIndex = 0; algoIndex < m_cpdTables[lineIndex].size(); ++algoIndex) {
auto cpdLine = m_chart->AddPlot(vtkChart::LINE);
cpdLine->SetColor(colors[lineIndex].r, colors[lineIndex].g, colors[lineIndex].b, 255);
cpdLine->SetWidth(1.0);
m_chart->SetPlotCorner(cpdLine, 1);
algoLines.push_back(cpdLine);
}
m_cpdLines.push_back(algoLines);
}
} else if (m_cpdLines.size() != 0 && !m_depthChartView->model()->showCpD()) {
for (uint lineIndex = 0; lineIndex < m_cpdLines.size(); ++lineIndex) {
for (uint algoIndex = 0; algoIndex < m_cpdLines[lineIndex].size(); ++algoIndex) {
auto line = m_cpdLines[lineIndex][algoIndex];
m_chart->RemovePlot(m_chart->GetPlotIndex(line));
line->Delete();
}
}
m_cpdLines.clear();
}
// Lock before getting update variables
std::unique_lock<std::mutex> dataLock(m_depthChartView->dataMutex);
auto animationId = m_depthChartView->m_animationId;
auto animationFrame = m_depthChartView->m_animationFrame;
auto depthChartViewRealTime = m_depthChartView->m_realTime;
auto realTime = m_realTime;
dataLock.unlock();
// Unlock so that main thread can update variables
// Update data of plots
if (m_depthChartView != nullptr && m_depthChartView->model() != nullptr) {
if (m_depthChartView->model()->showBpD()) {
uint frameNumber = 0;
if (animationId.compare("") != 0) {
frameNumber = m_bpdAnimationFrames[animationId][animationFrame];
} else if (depthChartViewRealTime != realTime) {
frameNumber = m_depthChartView->bpdSource()->indexOfTime(depthChartViewRealTime);
}
m_bpdLine->SetInputData(m_bpdTable, 0, frameNumber + 1);
}
if (m_depthChartView->model()->showCpD()) {
uint frameNumber = 0;
if (animationId.compare("") != 0) {
frameNumber = m_cpdAnimationFrames[animationId][animationFrame];
} else if (depthChartViewRealTime != realTime) {
frameNumber = m_depthChartView->cpdSource()->indexOfTime(depthChartViewRealTime);
}
for (uint lineIndex = 0; lineIndex < m_cpdLines.size(); ++lineIndex) {
for (uint algoIndex = 0; algoIndex < m_cpdLines[lineIndex].size(); ++algoIndex) {
m_cpdLines[lineIndex][algoIndex]->SetInputData(m_cpdTables[lineIndex][algoIndex], 0,
frameNumber + 1);
}
}
}
m_chart->RecalculateBounds();
m_chart->Modified();
}
}
bool DepthChartRenderer::visible() const
{
return m_view != nullptr;
}
void DepthChartRenderer::show()
{
if (m_view != nullptr)
return;
init();
}
void DepthChartRenderer::hide()
{
if (m_view == nullptr)
return;
m_view->Delete();
m_view = nullptr;
}
void DepthChartRenderer::UpdateFrameCallback::Execute(vtkObject *, unsigned long eventId, void * vtkNotUsed(callData))
{
if (vtkCommand::TimerEvent != eventId)
return;
std::unique_lock updateLock(depthChart->updateMutex);
bool doUpdate = depthChart->updateExists;
depthChart->updateExists = false;
updateLock.unlock();
if (!doUpdate)
return;
depthChart->updateFrame();
}
我的整体架构存在缺陷吗?或者我缺少一些简单的东西破坏了我的动画表现?