H1
传统方法的缺陷
使用Marching Cubes算法会导致一些方格被遗漏。如:存在两条与方格的一条或多条边相交的情况,并且会错过不与方格边相交的小闭合轮廓。绘制任意函数图像时,很难保证检测到所有轮廓线段。因此在实践中,应选择合适的单元格大小以确保:检测到超过一定大小的所有函数线段;每个单元格中最多存在一个线段;以及在单元格内通过直线段进行近似。如果绘图网格不够精细,也可能会出现单元格退化的情况,如下图中的情况:
H1
四叉树算法
四叉树算法Quadtree Algorithm中的四叉树是一种树状数据结构,在每一个节点上会有四个子区块。四叉树常应用于二维空间资料的分析与分类。在图像压缩、二维地图近邻分析和碰撞监测等方面都有广泛的应用。标准的四叉树算法会把平面均分为四格,该规则对每格能容纳的物体数量有严格限定。在下面的图中,每个子方格只能容纳一个物体,如果物体数量超过该限制后,将在该子方格内继续划分4格子方格空间。直到满足规则要求的为止。在实际应用中,我们往往也会使用非平均分布的四叉树模型,根据不同的需求变量,调整空间权重,形成大小不一的正方形矩形组合。
对于上述两种情况,我们认为函数可能至少存在两条线段同时通过该方格,因此需要进一步使用Quadtree算法划分子方格。 通过不断迭代,最后绘制出完整的隐函数图像。
H1
构造函数图像的点
在下面构造函数图像点的算法中,我们首先排除一些函数不通过方格的情形,当出现可能会多个线段通过方格的情形时,则需要进一步划分子方格。
RectanglePointState QuadTreeRectangle::generateCurvePoints(QList<QVector4D>& listPoints)
{
if (m_verifyStatus.emtpy) return RectanglePointState::EMPTY;
uint empty_state = (m_verifyStatus.zero + 1) | m_verifyStatus.pos | m_verifyStatus.neg;
if ( empty_state >= 4) return RectanglePointState::EMPTY;
/**
* (+)---(-) (-)---(+)
* | | 或 | |
* (-)---(+) (+)---(-)
* 如果出现上述情况,函数曲线上的点可能存在于每条边上,因此这种情况需要继续划分子方格。
*/
if(m_verifyStatus.neg == m_verifyStatus.pos && !oppSign(m_eval.topLeft, m_eval.bottomRight)) return RectanglePointState::T0101;
switch(m_verifyStatus.zero) {
case 0: // 函数曲线上的点都不在矩形的4个顶点上,因此需要通过插值方式计算函数曲线上的点
{
if(oppSign(m_eval.topLeft, m_eval.topRight)) {
float x = interpolate(m_rect.left(), m_rect.right(), m_eval.topLeft, m_eval.topRight);
listPoints.append(QVector4D(x, m_rect.top(), 0.0f, 1.0f));
}
if(oppSign(m_eval.topRight, m_eval.bottomRight)) {
float y = interpolate(m_rect.top(), m_rect.bottom(), m_eval.topRight, m_eval.bottomRight);
listPoints.append(QVector4D(m_rect.right(), y, 0.0f, 1.0f));
}
if(oppSign(m_eval.bottomRight, m_eval.bottomLeft)) {
float x = interpolate(m_rect.left(), m_rect.right(), m_eval.bottomLeft, m_eval.bottomRight);
listPoints.append(QVector4D(x, m_rect.bottom(), 0.0f, 1.0f));
}
if(oppSign(m_eval.bottomLeft, m_eval.topLeft)) {
float y = interpolate(m_rect.top(), m_rect.bottom(), m_eval.topLeft, m_eval.bottomLeft);
listPoints.append(QVector4D(m_rect.left(), y, 0.0f, 1.0f));
}
return RectanglePointState::VALID;
}
return RectanglePointState::EMPTY;
}
H2
线形插值算法
H1