qwtplot3d是qt解决三维图形绘制的库,核心通过调用openGL库完成,可以实现三维曲面绘制,三维状态下折线,散点等图形的绘制。但是其在十多年前推出后一直没有太大的更新,而且自带的类并不够丰富,所以在使用时还是有一些问题的。主要参考了引用1中的博客,qwtplot3d库的代码在该博主的博客中可以下载到,另外在github中搜索qwtplot3d,找到标星最多的一个,但是该库有改动,另外可以在引用2中下载到官方库文件。
参考QWT3D 之 三维动态曲线的实现
画线的实现原理是改变了在各个顶点之间画图形的方式,由之前的画三维曲面改成直接将前后点相连。我使用的是从博主那里下载到的源码,其中的Line3D类已经写好,在qwt3d_enrichment_std.cpp中,我们看一下该类:
class QWT3D_EXPORT Line3D: public VertexEnrichment
{
public:
Line3D();
Line3D(double thick,bool smooth);
Qwt3D::Enrichment * clone() const{ return new Line3D(*this);}
void configure(double thick, bool smooth);
void drawBegin();
void drawEnd();
virtual void draw(Qwt3D::Triple const&);
virtual void draw();
virtual void add(Qwt3D::Triple const & t);
virtual void setLineColor(RGBA color);
/*virtual void clear()
{
lineData.clear();
//myColorMap.clear();
}*/
//virtual void setLineColor(int startIndex,RGBA color);
// virtual void setLineColor(RGBA color);
//virtual RGBA getColor(int pointIndex);
private:
bool smooth_;
double lineThick;
GLboolean oldstate_;
std::vector lineData;
RGBA rgba;
//std::map myColorMap;
};
该类中主要几个成员变量,double lineThick代表线的粗细;bool smooth_ 代表是否光滑,在点数较多时可能会出现显示问题,如果因为点数太多导致显示的图形颜色发挥可以将改变量设为false,点的形状会变成方形,但是颜色更加清晰;RGBA rgba; 是线的颜色,有r,g,b,a四个成员变量,都是double型,rgb合成一个颜色,a为透明度,不透明为1,rgb取值为0-1。std::vectorQwt3D::Triple lineData是折线顶点的向量,元素Qwt3D::Triple有x,y,z三个成员变量,是个三维坐标,画线操作即是读取lineData中的三维坐标按顺序画线。函数方面,configure 可以改变线的粗细和是否光滑,add 函数可以向lineData向量中添加一个坐标点,setLineColor 设置颜色,剩下的就是和实际画图相关的函数,使用时调用draw() 即可,它会调用其它几个画图相关函数,把lineData画完。我们看一下draw() 函数
void Qwt3D::Line3D::drawBegin()
{
setDeviceLineWidth(lineThick);
oldstate_ = glIsEnabled(GL_LINE_SMOOTH);
if (smooth_)
glEnable(GL_LINE_SMOOTH);
else
glDisable(GL_LINE_SMOOTH);
//glPointSize(10);
glBegin( GL_LINE_STRIP);
}
void Qwt3D::Line3D::drawEnd()
{
glEnd();
if (oldstate_)
glEnable(GL_LINE_SMOOTH);
else
glDisable(GL_LINE_SMOOTH);
}
void Qwt3D::Line3D::draw(Qwt3D::Triple const& pos)
{
glColor4d(rgba.r,rgba.g,rgba.b,rgba.a);
glVertex3d(pos.x,pos.y,pos.z);
}
void Qwt3D::Line3D::draw()
{
for (int i = 0; i < lineData.size(); i ++)
{
//RGBA rgba = getColor(i);
//glColor4d(rgba.r,rgba.g,rgba.b,rgba.a);
draw(lineData[i]);
}
}
通过这几个函数,最终调用了openGL库中的函数实现了折线的绘制。
实现画线之后,又需要用到画点功能,即绘制散点图。根据画线的实现原理,画点只需要将调用的openGL库函数改变即可,正好在qwtplot3d_enrichment_std.cpp有了一个Dot类用来实现画点,我们看一下原来的Dot类:
/////////////////////////////////////////////////////////////////
//
// Dot
//
/////////////////////////////////////////////////////////////////
Dot::Dot()
{
configure(1, false);
}
Dot::Dot(double pointsize, bool smooth)
{
configure(pointsize, smooth);
}
void Dot::configure(double pointsize, bool smooth)
{
plot = 0;
pointsize_ = pointsize;
smooth_ = smooth;
}
void Dot::drawBegin()
{
setDevicePointSize( pointsize_ );
oldstate_ = glIsEnabled(GL_POINT_SMOOTH);
if (smooth_)
glEnable(GL_POINT_SMOOTH);
else
glDisable(GL_POINT_SMOOTH);
//glPointSize(10);
glBegin( GL_POINTS );
}
void Dot::drawEnd()
{
glEnd();
if (oldstate_)
glEnable(GL_POINT_SMOOTH);
else
glDisable(GL_POINT_SMOOTH);
}
void Dot::draw(Qwt3D::Triple const& pos)
{
RGBA rgba = (*plot->dataColor())(pos);
glColor4d(rgba.r,rgba.g,rgba.b,rgba.a);
glVertex3d( pos.x, pos.y, pos.z);
}
对比Line3D的实现,可以看出主要区别在drawBegin 和drawEnd 两个函数的设置,为了实现调用方式的统一,我对Dot类进行了修改,使其在格式上与Line3D相同,代码如下:
.H
class QWT3D_EXPORT Dot : public VertexEnrichment
{
public:
Dot();
Dot(double pointsize, bool smooth);
Qwt3D::Enrichment* clone() const {return new Dot(*this);}
void configure(double thick, bool smooth);
void drawBegin();
void drawEnd();
void draw(Qwt3D::Triple const&);
virtual void draw();
virtual void add(Qwt3D::Triple const & t);
virtual void setLineColor(RGBA color);
private:
bool smooth_;
double lineThick;
GLboolean oldstate_;
std::vector lineData;
RGBA rgba;
};
.CPP
/////////////////////////////////////////////////////////////////
//
// Dot
//
/////////////////////////////////////////////////////////////////
/*
Dot::Dot()
{
configure(1, false);
}
Dot::Dot(double pointsize, bool smooth)
{
configure(pointsize, smooth);
}
void Dot::configure(double pointsize, bool smooth)
{
plot = 0;
pointsize_ = pointsize;
smooth_ = smooth;
}
void Dot::drawBegin()
{
setDevicePointSize( pointsize_ );
oldstate_ = glIsEnabled(GL_POINT_SMOOTH);
if (smooth_)
glEnable(GL_POINT_SMOOTH);
else
glDisable(GL_POINT_SMOOTH);
//glPointSize(10);
glBegin( GL_POINTS );
}
void Dot::drawEnd()
{
glEnd();
if (oldstate_)
glEnable(GL_POINT_SMOOTH);
else
glDisable(GL_POINT_SMOOTH);
}
void Dot::draw(Qwt3D::Triple const& pos)
{
RGBA rgba = (*plot->dataColor())(pos);
glColor4d(rgba.r,rgba.g,rgba.b,rgba.a);
glVertex3d( pos.x, pos.y, pos.z);
}
*/
Qwt3D::Dot::Dot()
{
rgba.a = 1;
rgba.b = 0.3;
rgba.r = 0.6;
rgba.g = 1;
}
Qwt3D::Dot::Dot(double thick,bool smooth)
{
lineThick = thick;
smooth_ = smooth;
rgba.a = 1;
rgba.b = 0.3;
rgba.r = 0.6;
rgba.g = 1;
}
void Qwt3D::Dot::configure(double thick, bool smooth)
{
lineThick = thick;
smooth_ = smooth;
}
void Qwt3D::Dot::drawBegin()
{
setDevicePointSize(lineThick);
oldstate_ = glIsEnabled(GL_POINT_SMOOTH);
if (smooth_)
glEnable(GL_POINT_SMOOTH);
else
glDisable(GL_POINT_SMOOTH);
//glPointSize(10);
glBegin( GL_POINTS);
}
void Qwt3D::Dot::drawEnd()
{
glEnd();
if (oldstate_)
glEnable(GL_POINT_SMOOTH);
else
glDisable(GL_POINT_SMOOTH);
}
void Qwt3D::Dot::draw(Qwt3D::Triple const& pos)
{
glColor4d(rgba.r,rgba.g,rgba.b,rgba.a);
glVertex3d(pos.x,pos.y,pos.z);
}
void Qwt3D::Dot::draw()
{
for (int i = 0; i < lineData.size(); i ++)
{
//RGBA rgba = getColor(i);
//glColor4d(rgba.r,rgba.g,rgba.b,rgba.a);
draw(lineData[i]);
}
}
void Qwt3D::Dot::add(Qwt3D::Triple const & t)
{
lineData.push_back(t);
}
void Qwt3D::Dot::setLineColor(RGBA color)
{
this->rgba = color;
}
修改之后,对画线和画点的实现都可以通过调用相应类的draw() 函数实现。对该函数的调用在qwt3d_surfaceplot.cpp中,看下修改后的代码:
void SurfacePlot::createEnrichment(Enrichment& p)
{
// printf("createEnrichment\n");
if (!actualData_p)
return;
//todo future work
if (p.type() != Enrichment::VERTEXENRICHMENT)
return;
p.assign(*this);
p.drawBegin();
/* VertexEnrichment* ve = (VertexEnrichment*)&p;
if (actualData_p->datatype == Qwt3D::POLYGON)
{
for (unsigned i = 0; i != actualDataC_->normals.size(); ++i)
ve->draw(actualDataC_->nodes[i]);
}
else if (actualData_p->datatype == Qwt3D::GRID &&plotStyle() != Qwt3D::LINE3D_STYLE )
{
int step = resolution();
for (int i = 0; i <= actualDataG_->columns() - step; i += step)
for (int j = 0; j <= actualDataG_->rows() - step; j += step)
ve->draw(Triple(actualDataG_->vertices[i][j][0],
actualDataG_->vertices[i][j][1],
actualDataG_->vertices[i][j][2]));
}
else if (plotStyle() == Qwt3D::LINE3D_STYLE)
{*/
// printf("pdraw\n");
p.draw();
// }
p.drawEnd();
}
原先的该函数时通过判断类的类型来调用不同绘制函数的,Qwt3D::LINE3D_STYLE等时枚举值,但是测试发现该判断没有生效,索性直接注释掉,保留了统一的p.draw()函数调用,这也是我把Dot类修改成与Line3D类格式相似的原因,不同的绘制方式在自己类中的draw()函数中实现。
为了实现画线和画点功能,对于qwtplot3d库的修改就分享到这里,之后会写库函数调用和界面方面的问题。
QWT3D 之 三维动态曲线的实现
qwtplot3d官方库
github中的qwtplot库