13-三维曲面图
10.4.1 三维曲面图
1.绘制三维曲面图
绘制三维曲面使用Q3DSurface图表类和QSurface3DSeries序列,根据使用的数据代理类的不同,可以绘制两种三维曲面图。
- QSurfaceDataProxy数据代理类根据空间点的三维坐标绘制曲面,如一般的三维函数曲面。
- QHeightMapSurfaceDataProxy数据代理类根据一个图片的数据绘制三维曲面,典型的如三维地形图。
图10-9是实例samp10_3使用QSurfaceDataProxy数据代理绘制普通三维曲面图的运行界面,左侧的控制面板的控制功能与前述实例基本相同,只是增加了3个颜色设置的按钮。
下面是主窗口类的定义的主要部分(省略了界面组件的槽函数定义):
class MainWindow : public QMainWindow
{
Q_OBJECT
private:
QWidget *graphContainer;
Q3DSurface *graph3D; //三维图表
QSurface3DSeries *series; //序列
QSurfaceDataProxy *proxy; //数据代理
void iniGraph3D();
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
private:
Ui::MainWindow *ui;
};

MainWindow类定义了Q3DSurface类的变量graph3D,QSurface3DSeries类的序列变量series,QSurfaceDataProxy类的数据代理变量proxy。iniGraph3D()函数用于图表初始化绘制,在构造函数里调用此函数。
下面是MainWindow类的构造函数,在构造函数里为控制面板上的两个设置曲面渐变颜色的按钮绘制了渐变图片。QLinearGradient类的使用见8.1节,这里不再赘述。
MainWindow::MainWindow(QWidget *parent) : QMainWindow(parent), ui(new Ui::MainWindow)
{
ui->setupUi(this);
//渐变颜色按钮
QLinearGradient grBtoY(0, 0, 100, 0); //线性渐变
grBtoY.setColorAt(1.0, Qt::black);
grBtoY.setColorAt(0.67, Qt::blue);
grBtoY.setColorAt(0.33, Qt::red);
grBtoY.setColorAt(0.0, Qt::yellow);
QPixmap pm(160, 20); //图片
QPainter pmp(&pm);
pmp.setBrush(QBrush(grBtoY)); //渐变颜色1
pmp.setPen(Qt::NoPen);
pmp.drawRect(0, 0, 160, 20);
ui->btnGrad1->setIcon(QIcon(pm)); //渐变颜色按钮1
ui->btnGrad1->setIconSize(QSize(160, 20));
QLinearGradient grGtoR(0, 0, 100, 0); //渐变颜色2
grGtoR.setColorAt(1.0, Qt::darkGreen);
grGtoR.setColorAt(0.5, Qt::yellow);
grGtoR.setColorAt(0.2, Qt::red);
grGtoR.setColorAt(0.0, Qt::darkRed);
pmp.setBrush(QBrush(grGtoR));
pmp.drawRect(0, 0, 160, 20);
ui->btnGrad2->setIcon(QIcon(pm)); //渐变颜色按钮2
ui->btnGrad2->setIconSize(QSize(160, 20));
iniGraph3D();
QSplitter *splitter=new QSplitter(Qt::Horizontal);
splitter->addWidget(ui->groupBox);
splitter->addWidget(graphContainer);
this->setCentralWidget(splitter);
}
构造函数里调用iniGraph3D()函数进行图形绘制,下面是iniGraph3D()函数的完整代码。
void MainWindow::iniGraph3D()
{
graph3D = new Q3DSurface();
graphContainer = QWidget::createWindowContainer(graph3D);
//创建坐标轴
QValue3DAxis *axisX=new QValue3DAxis;
axisX->setTitle("Axis X");
axisX->setTitleVisible(true);
axisX->setRange(-11,11);
graph3D->setAxisX(axisX);
QValue3DAxis *axisY=new QValue3DAxis;
axisY->setTitle("Axis Y");
axisY->setTitleVisible(true);
axisY->setAutoAdjustRange(true);
graph3D->setAxisY(axisY);
QValue3DAxis *axisZ=new QValue3DAxis;
axisZ->setTitle("Axis Z");
axisZ->setTitleVisible(true);
axisZ->setRange(-11,11);
graph3D->setAxisZ(axisZ);
//创建数据代理
proxy = new QSurfaceDataProxy();
series = new QSurface3DSeries(proxy);
graph3D->addSeries(series);
series->setItemLabelFormat("(@xLabel @yLabel @zLabel)");
series->setMeshSmooth(true);
graph3D->activeTheme()->setLabelBackgroundEnabled(false);
series->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe);
//创建数据, 墨西哥草帽
int N=41;//-10:0.5:10, N个数据点
QSurfaceDataArray *dataArray = new QSurfaceDataArray; //数组
dataArray->reserve(N);
float x=-10,y,z;
int i,j;
for (i =1 ; i <=N; i++)
{
QSurfaceDataRow *newRow = new QSurfaceDataRow(N); //一行的数据
y=-10;
int index=0;
for (j = 1; j <=N; j++)
{
z=qSqrt(x*x+y*y);
if (z!=0)
z=10*qSin(z)/z;
else
z=10;
(*newRow)[index++].setPosition(QVector3D(x, z, y));
y=y+0.5;
}
x=x+0.5;
*dataArray << newRow;
}
proxy->resetArray(dataArray);
}
代码首先创建三维图表graph3D,并为其创建窗口容器,然后创建了3个QValue3DAxis类型的坐标轴对象,设置基本属性后作为图表的相应坐标轴。
程序创建QSurfaceDataProxy类型的数据代理变量proxy,并在创建序列时作为参数传递给它,然后将序列添加到图表。
下面的代码对序列做了一些属性的设置,特别设置了绘图模式为曲面加线网的模式,即:
series->setDrawMode(QSurface3DSeries::DrawSurfaceAndWireframe);
然后是创建数据,采用与实例samp10_2相同的“墨西哥草帽”函数生成数据点。QsurfaceData Proxy数据代理采用两层一维数组的模式存储三维曲面的数据,与三维柱状图的数据代理类存储数据的模式类似。
这里创建一个QSurfaceDataArray类型的向量dataArray,它的元素个数等于行数,每一行又是一个QSurfaceDataRow类型的列表,用于存储一行中多列的数据。QSurfaceDataArray和QSurface DataRow是类型定义,其定义为:
typedef QVector<QSurfaceDataItem> QSurfaceDataRow;
typedef QList<QSurfaceDataRow *> QSurfaceDataArray;
每个基本的数据点都是QSurfaceDataItem类,有x、y、z 3个坐标,通过setPosition(const QVector3D &pos)函数可以设置一个点的三维坐标。同样,Q3DSurface的三维坐标系是图10-8的样式,与在数学中常见的坐标轴的名称不一致,在设置数据点时需要注意。
2.曲面样式
QSurface3DSeries的函数setDrawMode(DrawFlags mode)用于设置曲面样式,参数是枚举类型QSurface3DSeries::DrawFlag的组合,取值有以下几种:
- QSurface3DSeries::DrawWireframe,只绘制线网;
- QSurface3DSeries::DrawSurface,只绘制曲面;
- QSurface3DSeries::DrawSurfaceAndWireframe,绘制线网和曲面。
图10-9是设置为QSurface3DSeries::DrawSurfaceAndWireframe时的绘图效果。
3.曲面的单一颜色设置
在改变图表的主题时,曲面的颜色会根据主题的设置而改变,也可以单独改变曲面的颜色。控制面板中的“曲面颜色”按钮用于设置曲面颜色,代码如下:
void MainWindow::on_btnBarColor_clicked()
{ //单一曲面颜色
QColor color=series->baseColor();
color=QColorDialog::getColor(color);
if (color.isValid())
{
series->setBaseColor(color);
series->setColorStyle(Q3DTheme::ColorStyleUniform);
}
}
对序列调用setBaseColor()函数设置颜色,并且调用setColorStyle()函数将序列颜色样式设置为单一颜色。setColorStyle()设置的参数是枚举类型Q3DTheme::ColorStyle,其取值有以下几种:
- Q3DTheme::ColorStyleUniform,单一颜色;
- Q3DTheme::ColorStyleObjectGradient,不考虑对象高度,只根据对象渐变;
- Q3DTheme::ColorStyleRangeGradient,根据对象高度渐变。
4.曲面的渐变颜色设置
曲面还可以设置为渐变颜色,例如Q3DTheme::ColorStyleRangeGradient渐变类型,即根据数据点的高度赋予不同的颜色。控制面板中“渐变色1”按钮的响应代码如下:
void MainWindow::on_btnGrad1_clicked()
{//渐变颜色
QLinearGradient gr;
gr.setColorAt(0.0, Qt::black);
gr.setColorAt(0.33, Qt::blue);
gr.setColorAt(0.67, Qt::red);
gr.setColorAt(1.0, Qt::yellow);
series->setBaseGradient(gr);
series->setColorStyle(Q3DTheme::ColorStyleRangeGradient);
}
程序定义一个QLinearGradient线性渐变类的变量,定义了渐变颜色属性后,调用序列的setBaseGradient()函数设置渐变颜色,并设置颜色样式为Q3DTheme::ColorStyleRangeGradient。