Merge
commit
1ad996c3eb
|
@ -87,12 +87,16 @@
|
|||
<ItemGroup>
|
||||
<ClCompile Include="BvhTree.cpp" />
|
||||
<ClCompile Include="Camera.cpp" />
|
||||
<ClCompile Include="CubicBezier.cpp" />
|
||||
<ClCompile Include="Light.cpp" />
|
||||
<ClCompile Include="Line.cpp" />
|
||||
<ClCompile Include="Mesh.cpp" />
|
||||
<ClCompile Include="Model.cpp" />
|
||||
<ClCompile Include="PaintingHelper.cpp" />
|
||||
<ClCompile Include="PaintingMesh.cpp" />
|
||||
<ClCompile Include="RendererWidget.cpp" />
|
||||
<ClCompile Include="ShortCutTree.cpp" />
|
||||
<ClCompile Include="StraightLine.cpp" />
|
||||
<QtRcc Include="MainWindow.qrc" />
|
||||
<QtUic Include="MainWindow.ui" />
|
||||
<QtMoc Include="MainWindow.h" />
|
||||
|
@ -123,12 +127,16 @@
|
|||
<ItemGroup>
|
||||
<ClInclude Include="BvhTree.h" />
|
||||
<ClInclude Include="Camera.h" />
|
||||
<ClInclude Include="CubicBezier.h" />
|
||||
<ClInclude Include="Drawable.h" />
|
||||
<ClInclude Include="Light.h" />
|
||||
<ClInclude Include="Line.h" />
|
||||
<ClInclude Include="Mesh.h" />
|
||||
<ClInclude Include="Model.h" />
|
||||
<ClInclude Include="PaintingHelper.h" />
|
||||
<ClInclude Include="PaintingMesh.h" />
|
||||
<ClInclude Include="ShortCutTree.h" />
|
||||
<ClInclude Include="StraightLine.h" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Condition="Exists('$(QtMsBuild)\qt.targets')">
|
||||
|
|
|
@ -67,6 +67,18 @@
|
|||
<ClCompile Include="Light.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ShortCutTree.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="Line.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="CubicBezier.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StraightLine.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<QtMoc Include="RendererWidget.h">
|
||||
|
@ -148,5 +160,17 @@
|
|||
<ClInclude Include="Light.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ShortCutTree.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="Line.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="CubicBezier.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StraightLine.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
</Project>
|
|
@ -4,7 +4,7 @@ GLuint BvhTree::getBvhNodeNum() {
|
|||
return tot;
|
||||
}
|
||||
|
||||
void BvhTree::buildBvhTree(QVector4D initBound[], int len) {
|
||||
void BvhTree::buildBvhTree(BvhTreeData initBound[], int len) {
|
||||
tot = 0;
|
||||
root = subBvhTree(initBound, 0, len-1);
|
||||
}
|
||||
|
@ -18,39 +18,48 @@ QVector4D BvhTree::Union(QVector4D a, QVector4D b) {
|
|||
);
|
||||
}
|
||||
|
||||
QVector4D BvhTree::calcBound(QVector4D initBound[], int l, int r) {
|
||||
QVector4D res = initBound[l];
|
||||
QVector4D BvhTree::calcBound(BvhTreeData initBound[], int l, int r) {
|
||||
QVector4D res = initBound[l].bound;
|
||||
for (int i = l + 1; i <= r; i++) {
|
||||
res = Union(res, initBound[i]);
|
||||
res = Union(res, initBound[i].bound);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
bvhPtr BvhTree::subBvhTree(QVector4D initBound[], int l, int r) {
|
||||
BvhPtr BvhTree::subBvhTree(BvhTreeData initBound[], int l, int r) {
|
||||
if (l > r) return NULL;
|
||||
bvhPtr p(new BvhNode());
|
||||
p.get()->lab = tot++;
|
||||
p.get()->bound = calcBound(initBound, l, r);
|
||||
if (l == r) return p;
|
||||
int dim = p.get()->maximumBound();
|
||||
BvhPtr p(new BvhNode());
|
||||
p->lab = tot++;
|
||||
p->bound = calcBound(initBound, l, r);
|
||||
if (l == r) {
|
||||
p->isLeaf = true;
|
||||
BvhPtr lp(new BvhNode());
|
||||
lp->lab = initBound[l].leftSon;
|
||||
p->child[0] = lp;
|
||||
BvhPtr rp(new BvhNode());
|
||||
rp->lab = initBound[r].rightSon;
|
||||
p->child[1] = rp;
|
||||
return p;
|
||||
}
|
||||
int dim = p->maximumBound();
|
||||
if (dim == 0) {
|
||||
std::sort(initBound + l, initBound + r+1, BvhNode::x_compare);
|
||||
} else {
|
||||
std::sort(initBound + l, initBound + r+1, BvhNode::y_compare);
|
||||
}
|
||||
int mid = (l + r) >> 1;
|
||||
p.get()->child[0] = subBvhTree(initBound, l, mid);
|
||||
p.get()->child[1] = subBvhTree(initBound, mid+1, r);
|
||||
p->child[0] = subBvhTree(initBound, l, mid);
|
||||
p->child[1] = subBvhTree(initBound, mid+1, r);
|
||||
return p;
|
||||
}
|
||||
|
||||
void BvhTree::traverseBvhTree(bvhPtr now, std::vector<GLuint>& children, std::vector<QVector4D>& bounds) {
|
||||
void BvhTree::traverseBvhTree(BvhPtr now, std::vector<GLuint>& children, std::vector<QVector4D>& bounds) {
|
||||
if (now == NULL) return ;
|
||||
children.push_back(now.get()->getLeftSon(0));
|
||||
children.push_back(now.get()->getRightSon(1));
|
||||
bounds.push_back(now.get()->bound);
|
||||
traverseBvhTree(now.get()->child[0], children, bounds);
|
||||
traverseBvhTree(now.get()->child[1], children, bounds);
|
||||
children.push_back(now->getLeftSon(0));
|
||||
children.push_back(now->getRightSon(1));
|
||||
bounds.push_back(now->bound);
|
||||
traverseBvhTree(now->child[0], children, bounds);
|
||||
traverseBvhTree(now->child[1], children, bounds);
|
||||
|
||||
}
|
||||
|
||||
|
|
|
@ -9,33 +9,49 @@
|
|||
#include <memory>
|
||||
#include <vector>
|
||||
#include <cmath>
|
||||
#define bvhPtr std::tr1::shared_ptr<BvhNode>
|
||||
|
||||
using std::tr1::shared_ptr;
|
||||
|
||||
struct BvhTreeData {
|
||||
QVector4D bound;
|
||||
GLuint leftSon, rightSon;
|
||||
void show() {
|
||||
printf("Bvh: (%lf, %lf) (%lf, %lf): %d %d\n",
|
||||
bound.x(), bound.y(), bound.z(), bound.w(),
|
||||
leftSon, rightSon);
|
||||
}
|
||||
BvhTreeData()
|
||||
: leftSon(0), rightSon(0) {}
|
||||
~BvhTreeData() {}
|
||||
};
|
||||
// BvhTree 节点
|
||||
struct BvhNode {
|
||||
GLuint lab;
|
||||
bool isLeaf;
|
||||
QVector4D bound;
|
||||
bvhPtr child[2];
|
||||
static bool x_compare(QVector4D a, QVector4D b) {
|
||||
return a.x() < b.x();
|
||||
shared_ptr<BvhNode> child[2];
|
||||
static bool x_compare(BvhTreeData a, BvhTreeData b) {
|
||||
return a.bound.x() < b.bound.x();
|
||||
}
|
||||
static bool y_compare(QVector4D a, QVector4D b) {
|
||||
return a.y() < b.y();
|
||||
static bool y_compare(BvhTreeData a, BvhTreeData b) {
|
||||
return a.bound.y() < b.bound.y();
|
||||
}
|
||||
GLuint getLeftSon(int k) {
|
||||
if (child[0] == NULL) {
|
||||
return INT_MAX;
|
||||
if (isLeaf) {
|
||||
return 0x80000000+child[0]->lab;
|
||||
}
|
||||
return child[0].get()->lab;
|
||||
return child[0]->lab;
|
||||
}
|
||||
GLuint getRightSon(int k) {
|
||||
if (child[1] == NULL) {
|
||||
return 0;
|
||||
if (isLeaf) {
|
||||
return child[1]->lab;
|
||||
}
|
||||
return child[1].get()->lab;
|
||||
return child[1]->lab;
|
||||
}
|
||||
BvhNode() {
|
||||
child[0] = child[1] = NULL;
|
||||
isLeaf = false;
|
||||
lab = 0;
|
||||
}
|
||||
int maximumBound() {
|
||||
return (std::fabs(bound.x() - bound.w()) < std::fabs(bound.y() - bound.z()));
|
||||
|
@ -43,22 +59,25 @@ struct BvhNode {
|
|||
~BvhNode() {}
|
||||
};
|
||||
|
||||
typedef std::tr1::shared_ptr<BvhNode> BvhPtr;
|
||||
|
||||
class BvhTree
|
||||
{
|
||||
|
||||
private:
|
||||
GLuint tot;
|
||||
bvhPtr root;
|
||||
static QVector4D calcBound(QVector4D initBound[], int l, int r);
|
||||
BvhPtr root;
|
||||
static QVector4D calcBound(BvhTreeData initBound[], int l, int r);
|
||||
static QVector4D Union(QVector4D a, QVector4D b);
|
||||
BvhPtr subBvhTree(BvhTreeData initBound[], int l, int r);
|
||||
void traverseBvhTree(BvhPtr now, std::vector<GLuint>& children, std::vector<QVector4D>& bounds);
|
||||
public:
|
||||
BvhTree() {
|
||||
tot = 0;
|
||||
root = NULL;
|
||||
}
|
||||
// 根据底层包围盒生成bvh树
|
||||
void buildBvhTree(QVector4D initBound[], int len);
|
||||
bvhPtr subBvhTree(QVector4D initBound[], int l, int r);
|
||||
void traverseBvhTree(bvhPtr now, std::vector<GLuint>& children, std::vector<QVector4D>& bounds);
|
||||
void buildBvhTree(BvhTreeData initBound[], int len);
|
||||
// 获得 Bvh (rootBvh部分)的 children 数组,包围盒数组 vector 传输
|
||||
void getBvhArray(std::vector<GLuint>& children, std::vector<QVector4D>& bounds);
|
||||
// 获得 BvhTree 中节点总数
|
||||
|
|
|
@ -0,0 +1,122 @@
|
|||
#include "CubicBezier.h"
|
||||
|
||||
float CubicBezier::getLineValueByT(float t, bool isY) {
|
||||
vector<float> p;
|
||||
if (isY) p = vY;
|
||||
else p = vX;
|
||||
float pt = 1 - t;
|
||||
return pt * pt * pt * p[0] + 3 * t * pt * pt * p[1] + 3 * t * t * pt * p[2] + t * t * t * p[3];
|
||||
}
|
||||
|
||||
point CubicBezier::getPointByT(point a, point b, float t) {
|
||||
return { a.x * (1 - t) + b.x * t, a.y * (1 - t) + b.y * t };
|
||||
}
|
||||
|
||||
point CubicBezier::calculateControlPoint(point a, point b) {
|
||||
return {
|
||||
a.x + 2 * (b.x - a.x) / 3,
|
||||
a.y + 2 * (b.y - a.y) / 3
|
||||
};
|
||||
}
|
||||
|
||||
void CubicBezier::findPointsOfDivison(vector<float> &p, vector<float>& res) {
|
||||
float a = -3 * p[0] + 9 * p[1] - 9 * p[2] + 3 * p[3];
|
||||
float b = 6 * p[0] - 12 * p[1] + 6 * p[2];
|
||||
float c = -3 * p[0] + 3 * p[1];
|
||||
float deta = b * b - 4 * a * c, t = 0;
|
||||
if (fabs(a) > eps) {
|
||||
if (deta < eps) return;
|
||||
deta = sqrt(deta);
|
||||
t = (-b + deta) / 2 * a;
|
||||
if (0 < t && t < 1) {
|
||||
res.push_back(t);
|
||||
}
|
||||
if (fabs(deta) <= eps) return;
|
||||
t = (-b - deta) / 2 * a;
|
||||
if (0 < t && t < 1) {
|
||||
res.push_back(t);
|
||||
}
|
||||
}
|
||||
else if(fabs(b) > eps) {
|
||||
t = -c/b;
|
||||
if (0 < t && t < 1) {
|
||||
res.push_back(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void CubicBezier::transformToCubic() {
|
||||
if (siz == 4) return;
|
||||
point p[3] = {
|
||||
getPointByIndex(0),
|
||||
getPointByIndex(1),
|
||||
getPointByIndex(2)
|
||||
};
|
||||
setPointByIndex(1, calculateControlPoint(p[0], p[1]));
|
||||
setPointByIndex(2, calculateControlPoint(p[1], p[2]));
|
||||
push_back(p[2]);
|
||||
siz = 4;
|
||||
}
|
||||
|
||||
void CubicBezier::splitBezierCubic(float t, vector<LinePtr>& res) {
|
||||
LinePtr Lf(new CubicBezier());
|
||||
float pt = 1 - t;
|
||||
point pointE = { getLineValueByT(t, false), getLineValueByT(t, true) };
|
||||
point pointF = getPointByT(getPointByIndex(0), getPointByIndex(1), t);
|
||||
point pointG = getPointByT(getPointByIndex(1), getPointByIndex(2), t);
|
||||
point pointH = getPointByT(getPointByIndex(2), getPointByIndex(3), t);
|
||||
Lf->push_back(getPointByIndex(0));
|
||||
Lf->push_back(pointF);
|
||||
Lf->push_back(getPointByT(pointF, pointG, t));
|
||||
Lf->push_back(pointE);
|
||||
res.push_back(Lf);
|
||||
setPointByIndex(0, pointE);
|
||||
setPointByIndex(1, getPointByT(pointG, pointH, t));
|
||||
setPointByIndex(2, pointH);
|
||||
}
|
||||
|
||||
void CubicBezier::monotonization(vector <LinePtr>& res) {
|
||||
transformToCubic();
|
||||
vector<float> re;
|
||||
re.clear();
|
||||
findPointsOfDivison(vX, re);
|
||||
findPointsOfDivison(vY, re);
|
||||
sort(re.begin(), re.end());
|
||||
float lt = -1, x = 0, y = 0;
|
||||
for (float &t : re) {
|
||||
if (fabs(t - lt) <= eps) continue;
|
||||
splitBezierCubic(t, res);
|
||||
lt = t;
|
||||
}
|
||||
}
|
||||
|
||||
float CubicBezier::findTByValue(float value, bool isY) {
|
||||
float l = 0, r = 1, be = 0;
|
||||
if (isY) be = vY[0];
|
||||
else be = vX[0];
|
||||
while (fabs(r-l) > eps) {
|
||||
float mid = (l + r) / 2, midValue = getLineValueByT(mid, isY);
|
||||
if (fabs(midValue - be) > fabs(value - be)) {
|
||||
r = mid;
|
||||
}
|
||||
else {
|
||||
l = mid;
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
int CubicBezier::judgeBoundIntersection(float xy, float l, float r, bool isY) {
|
||||
float be = *vX.begin(), en = *vX.rbegin();
|
||||
if (isY) {
|
||||
be = *vY.begin();
|
||||
en = *vY.rbegin();
|
||||
}
|
||||
if ((be - xy) * (en - xy) > eps) return 0;
|
||||
float t = findTByValue(xy, isY);
|
||||
float value = getLineValueByT(t, !isY);
|
||||
if (l <= value && value <= r && fabs(t) > eps) {
|
||||
return 1 + direction(isY);
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,20 @@
|
|||
#pragma once
|
||||
#include "Line.h"
|
||||
#include <memory>
|
||||
|
||||
class CubicBezier : public Line
|
||||
{
|
||||
using Line::Line;
|
||||
private:
|
||||
static point getPointByT(point a, point b, float t);
|
||||
static point calculateControlPoint(point a, point b);
|
||||
static void findPointsOfDivison(vector<float> &p, vector<float>& res);
|
||||
void splitBezierCubic(float t, vector<LinePtr>& res);
|
||||
public:
|
||||
virtual float findTByValue(float value, bool isY);
|
||||
virtual void monotonization(vector <LinePtr>& res);
|
||||
virtual float getLineValueByT(float t, bool isY);
|
||||
virtual int judgeBoundIntersection(float xy, float l, float r, bool isY);
|
||||
void transformToCubic();
|
||||
};
|
||||
|
|
@ -0,0 +1,73 @@
|
|||
#include "CubicMonotonization.h"
|
||||
|
||||
const double CubicMonotonization::eps = 1e-6;
|
||||
|
||||
|
||||
CubicMonotonization::point CubicMonotonization::getPointByT(point a, point b, double t) const {
|
||||
return { a.first * (1 - t) + b.first, a.second * (1 - t) + b.second };
|
||||
}
|
||||
|
||||
|
||||
void CubicMonotonization::findPointsOfDivison(vector<double> p, vector<double>& res) {
|
||||
double a = -p[0] + 7 * p[1] - 9 * p[2] + 3 * p[3];
|
||||
double b = 6 * p[0] - 12 * p[1] + 6 * p[2];
|
||||
double c = -3 * p[0] + 3*p[1];
|
||||
double deta = b * b - 4*a*c;
|
||||
if (fabs(deta) < eps) return;
|
||||
double t = 0;
|
||||
if (fabs(a) <= eps) {
|
||||
deta = sqrt(deta);
|
||||
t = -b + deta;
|
||||
if (0 < t && t < 1) {
|
||||
res.push_back(t);
|
||||
}
|
||||
if (fabs(deta) <= eps) return ;
|
||||
t = -b - deta;
|
||||
if (0 < t && t < 1) {
|
||||
res.push_back(t);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
double CubicMonotonization::getBezierPoint(vector<double> &p, double t) const{
|
||||
double pt = 1 - t;
|
||||
return pt * pt * pt * p[0] + 3 * t * pt * pt * p[1] + 3 * t * t * pt * p[2] + t * t * t * p[3];
|
||||
}
|
||||
|
||||
void CubicMonotonization::splitBezierCubic(Line& p, point mid, double t, vector <Line>& res) {
|
||||
Line Lf; Lf.clear();
|
||||
double pt = 1 - t;
|
||||
Lf.push_back(p[0]);
|
||||
point pointF = getPointByT(p[0], p[1], t);
|
||||
point pointG = getPointByT(p[1], p[2], t);
|
||||
point pointH = getPointByT(p[2], p[3], t);
|
||||
Lf.push_back(pointF);
|
||||
Lf.push_back(getPointByT(pointF, pointG, t));
|
||||
Lf.push_back(mid);
|
||||
res.push_back(Lf);
|
||||
p[0] = mid;
|
||||
p[1] = getPointByT(pointG, pointH, t);
|
||||
p[2] = pointH;
|
||||
}
|
||||
|
||||
void CubicMonotonization::getPointsOfDivision(Line p, vector <Line>& res) {
|
||||
vector<double> vX, vY, re;
|
||||
vX.clear();
|
||||
vY.clear();
|
||||
re.clear();
|
||||
for (point now : p) {
|
||||
vX.push_back(now.first);
|
||||
vY.push_back(now.second);
|
||||
}
|
||||
findPointsOfDivison(vX, re);
|
||||
findPointsOfDivison(vY, re);
|
||||
sort(re.begin(), re.end());
|
||||
double lt = 0, x = 0, y = 0;
|
||||
for (double &t : re) {
|
||||
if (fabs(t - lt) <= eps) continue;
|
||||
x = getBezierPoint(vX, t);
|
||||
y = getBezierPoint(vY, t);
|
||||
splitBezierCubic(p, { x, y }, t, res);
|
||||
lt = t;
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
#include <utility>
|
||||
#include <vector>
|
||||
#include <algorithm>
|
||||
#pragma once
|
||||
using std::pair;
|
||||
using std::vector;
|
||||
using std::min;
|
||||
using std::max;
|
||||
using std::sort;
|
||||
class CubicMonotonization
|
||||
{
|
||||
typedef pair<double, double> point;
|
||||
typedef vector<point> Line;
|
||||
private:
|
||||
static const double eps;
|
||||
point getPointByT(point a, point b, double t) const;
|
||||
void findPointsOfDivison(vector<double> p, vector<double> &res);
|
||||
double getBezierPoint(vector<double> &p, double t) const;
|
||||
void splitBezierCubic(Line& p, point mid, double t, vector <Line> &res);
|
||||
public:
|
||||
// p 为端点、控制点、控制点、端点, res 为所有导数为0的点
|
||||
void getPointsOfDivision(Line p, vector <Line> &res);
|
||||
};
|
||||
|
|
@ -0,0 +1,80 @@
|
|||
#include "Line.h"
|
||||
|
||||
Line::Line(vector<point> Vp) {
|
||||
siz = Vp.size();
|
||||
for (point now : Vp) {
|
||||
vX.push_back(now.x);
|
||||
vY.push_back(now.y);
|
||||
}
|
||||
}
|
||||
|
||||
int Line::size() {
|
||||
return siz;
|
||||
}
|
||||
|
||||
vector<point> Line::toVectorPoint() const {
|
||||
vector<point> vL;
|
||||
for (int i = 0; i < siz; i++) {
|
||||
vL.push_back({vX[i], vY[i]});
|
||||
}
|
||||
return vL;
|
||||
}
|
||||
|
||||
point Line::operator[](int index) const {
|
||||
return { vX[index], vY[index] };
|
||||
}
|
||||
|
||||
point Line::getPointByIndex(int index) const {
|
||||
return operator[](index);
|
||||
}
|
||||
|
||||
void Line::setPointByIndex(int index, point now) {
|
||||
vX[index] = now.x;
|
||||
vY[index] = now.y;
|
||||
}
|
||||
|
||||
void Line::clear() {
|
||||
vX.clear();
|
||||
vY.clear();
|
||||
}
|
||||
|
||||
void Line::push_back(point now) {
|
||||
siz++;
|
||||
vX.push_back(now.x);
|
||||
vY.push_back(now.y);
|
||||
}
|
||||
|
||||
bool Line::isLineContained(QVector4D bound) {
|
||||
float xMi = min(*vX.begin(), *vX.rbegin()), xMx = max(*vX.begin(), *vX.rbegin());
|
||||
float yMi = min(*vY.begin(), *vY.rbegin()), yMx = max(*vY.begin(), *vY.rbegin());
|
||||
if (bound.x() <= xMi && xMx <= bound.z() && bound.y() <= yMi && yMx <= bound.w())
|
||||
return true;
|
||||
else return false;
|
||||
}
|
||||
|
||||
point Line::getBegin() {
|
||||
return { *vX.begin(), *vY.begin() };
|
||||
}
|
||||
|
||||
point Line::getEnd() {
|
||||
return { *vX.rbegin(), *vY.rbegin() };
|
||||
}
|
||||
|
||||
int Line::direction(bool isY) {
|
||||
float be = *vX.begin(), en = *vX.rbegin();
|
||||
if (isY) {
|
||||
be = *vY.begin();
|
||||
en = *vY.rbegin();
|
||||
}
|
||||
if (fabs(en - be) <= eps) return 0;
|
||||
else if (be < en) return 1;
|
||||
else return 2;
|
||||
}
|
||||
|
||||
bool Line::judgeIntersection(QVector4D bound) {
|
||||
return isLineContained(bound)
|
||||
|| judgeBoundIntersection(bound.y(), bound.x(), bound.z(), true)
|
||||
|| judgeBoundIntersection(bound.w(), bound.x(), bound.z(), true)
|
||||
|| judgeBoundIntersection(bound.x(), bound.y(), bound.w(), false)
|
||||
|| judgeBoundIntersection(bound.z(), bound.y(), bound.w(), false);
|
||||
}
|
|
@ -0,0 +1,63 @@
|
|||
#pragma once
|
||||
#include <vector>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <QVector4D>
|
||||
#include <algorithm>
|
||||
#include <memory>
|
||||
#include <iostream>
|
||||
|
||||
using std::vector;
|
||||
using std::pair;
|
||||
using std::min;
|
||||
using std::max;
|
||||
using std::sort;
|
||||
using std::tr1::shared_ptr;
|
||||
using std::tr1::make_shared;
|
||||
using std::swap;
|
||||
|
||||
const float eps = 1e-6;
|
||||
|
||||
struct point {
|
||||
float x, y;
|
||||
point operator - (const point a) { return { x - a.x, y - a.y }; }
|
||||
float operator * (const point a) { return x * a.y - y * a.x; }
|
||||
bool operator== (const point& a) const {
|
||||
return fabs(x - a.x) <= eps && fabs(y - a.y) <= eps;
|
||||
}
|
||||
bool operator< (const point& a) const {
|
||||
return fabs(x - a.x) <= eps ? y < a.y : x < a.x;
|
||||
}
|
||||
void show() {
|
||||
std::cout << '(' << x << ',' << y << ')' << ' ';
|
||||
}
|
||||
};
|
||||
|
||||
class Line
|
||||
{
|
||||
protected:
|
||||
vector<float> vX, vY;
|
||||
int siz;
|
||||
public:
|
||||
typedef shared_ptr<Line> LinePtr;
|
||||
Line() :siz(0) {}
|
||||
Line(vector<point> Vp);
|
||||
int size();
|
||||
void clear();
|
||||
point getBegin();
|
||||
point getEnd();
|
||||
int direction(bool isY);
|
||||
virtual float getLineValueByT(float t, bool isY) = 0;
|
||||
virtual void monotonization(vector <LinePtr>& res) = 0;
|
||||
virtual int judgeBoundIntersection(float xy, float l, float r, bool isY) = 0;
|
||||
virtual bool judgeIntersection(QVector4D bound);
|
||||
bool isLineContained(QVector4D bound);
|
||||
point operator[](int index) const;
|
||||
point getPointByIndex(int index) const;
|
||||
vector<point> toVectorPoint() const;
|
||||
void setPointByIndex(int index, point now);
|
||||
void push_back(point now);
|
||||
virtual ~Line() {}
|
||||
};
|
||||
|
||||
typedef shared_ptr<Line> LinePtr;
|
|
@ -185,7 +185,7 @@ Drawable* Model::processMesh(aiMesh* mesh, const aiScene* scene, aiMatrix4x4 mod
|
|||
initBound.push_back(QVector4D(-0.8, 0.7, -0.7, 0.8));
|
||||
initBound.push_back(QVector4D(0.7, -0.8, 0.8, -0.7));
|
||||
initBound.push_back(QVector4D(0.7, 0.7, 0.8, 0.8));*/
|
||||
bvhTree.buildBvhTree(initBound.data(), initBound.size());
|
||||
//bvhTree.buildBvhTree(initBound.data(), initBound.size());
|
||||
std::vector<GLuint> children;
|
||||
std::vector<QVector4D> bounds;
|
||||
bvhTree.getBvhArray(children, bounds);
|
||||
|
|
|
@ -0,0 +1,238 @@
|
|||
#include "ShortCutTree.h"
|
||||
|
||||
void ShortCutTree::init() {
|
||||
outTree.clear();
|
||||
allLine.clear();
|
||||
pointMap.clear();
|
||||
lineIndexSet.clear();
|
||||
numLine = numPoint = 0;
|
||||
}
|
||||
|
||||
int ShortCutTree::getPointIndex(point now) {
|
||||
auto iter = pointMap.find(now);
|
||||
if (iter != pointMap.end()) {
|
||||
return iter->second;
|
||||
}
|
||||
else {
|
||||
++numPoint;
|
||||
pointMap.insert({ now, numPoint });
|
||||
return numPoint;
|
||||
}
|
||||
}
|
||||
|
||||
bool ShortCutTree::isLineEqual(LineIndex& a, LineIndex& b) const {
|
||||
if (a.size() != b.size())
|
||||
return false;
|
||||
for (int i = 0; i < a.size(); i++)
|
||||
if (a[i] != b[i])
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
void ShortCutTree::Monotonization(vector<vLine>& inL, vector<LinePtr>& outL) {
|
||||
for (vLine &l: inL) {
|
||||
LinePtr now;
|
||||
switch(l.size()) {
|
||||
case 2: now.reset(new StraightLine(l)); break;
|
||||
case 3: case 4: now.reset(new CubicBezier(l)); break;
|
||||
default: break;
|
||||
}
|
||||
now->monotonization(outL);
|
||||
outL.push_back(now);
|
||||
}
|
||||
}
|
||||
|
||||
void ShortCutTree::generateShortCutSegement(ShortCutNode& now) {
|
||||
point p;
|
||||
vector<int> v;
|
||||
for (int &index : now.lineSet) {
|
||||
int type = allLine[index]->judgeBoundIntersection(now.bound.z(), now.bound.y(), now.bound.w(), false), be, en;
|
||||
if (type >= 2) {
|
||||
numLine++;
|
||||
v.push_back(numLine);
|
||||
if (type == 2) {
|
||||
p = allLine[index]->getEnd();
|
||||
}
|
||||
if (type == 3) {
|
||||
p = allLine[index]->getBegin();
|
||||
}
|
||||
be = getPointIndex({ p.x, now.bound.w() });
|
||||
en = getPointIndex(p);
|
||||
lineIndexSet.push_back(be);
|
||||
lineIndexSet.push_back(be);
|
||||
lineIndexSet.push_back(en);
|
||||
lineIndexSet.push_back(en);
|
||||
}
|
||||
}
|
||||
for (int& index : v) {
|
||||
now.lineSet.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
bool ShortCutTree::handleShortCutNode(ShortCutNode& fa, ShortCutNode& now, float yValue, vector<ShortCutNode>& v, int& sumIncrement) {
|
||||
now.windingIncrement = sumIncrement;
|
||||
for (int &index : fa.lineSet) {
|
||||
int type = allLine[index]->judgeBoundIntersection(yValue, now.bound.x(), now.bound.z(), true);
|
||||
if (type == 2)
|
||||
sumIncrement++;
|
||||
if (type == 3)
|
||||
sumIncrement--;
|
||||
if (allLine[index]->judgeIntersection(now.bound)) {
|
||||
now.lineSet.push_back(index);
|
||||
}
|
||||
}
|
||||
|
||||
if (now.lineSet.size() <= RequiredLineMi) {
|
||||
if (now.lineSet.empty() && now.windingIncrement == 0)
|
||||
return false;
|
||||
outTree.push_back(now);
|
||||
now.divided = false;
|
||||
v.push_back(now);
|
||||
return false;
|
||||
}
|
||||
else {
|
||||
v.push_back(now);
|
||||
return true;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
void ShortCutTree::simplifyLineVector() {
|
||||
numLine = allLine.size();
|
||||
for (auto& l : allLine) {
|
||||
vLine vl = l->toVectorPoint();
|
||||
for (point& p : vl) {
|
||||
int index = getPointIndex(p);
|
||||
lineIndexSet.push_back(index);
|
||||
if (vl.size() == 2)
|
||||
lineIndexSet.push_back(index);
|
||||
}
|
||||
}
|
||||
for (auto& now : outTree) {
|
||||
generateShortCutSegement(now);
|
||||
}
|
||||
}
|
||||
|
||||
void ShortCutTree::spliteToShortCutTree() {
|
||||
queue<ShortCutNode> Q;
|
||||
queue<pair<float, float> > lineBound;
|
||||
ShortCutNode root;
|
||||
root.bound = QVector4D(-1, -1, 1, 1);
|
||||
for (int i = 0; i < allLine.size(); i++) {
|
||||
root.lineSet.push_back(i);
|
||||
}
|
||||
Q.push(root);
|
||||
lineBound.push({ -1, 1 });
|
||||
vector<ShortCutNode> v, vn;
|
||||
while (!lineBound.empty()) {
|
||||
// 取出一行的所有可行方格
|
||||
auto seg = lineBound.front();
|
||||
float yMid = (seg.first + seg.second) / 2;
|
||||
lineBound.pop();
|
||||
while (!Q.empty()) {
|
||||
auto now = Q.front();
|
||||
if (now.bound.y() <= seg.first && seg.second <= now.bound.w()) {
|
||||
v.push_back(now);
|
||||
Q.pop();
|
||||
}
|
||||
else {
|
||||
break;
|
||||
}
|
||||
}
|
||||
int sumIncrement = 0;
|
||||
bool isDivided = false;
|
||||
vn.clear();
|
||||
// 上部分;
|
||||
for (auto& now : v) {
|
||||
if (!now.divided) {
|
||||
for (int& index : now.lineSet) {
|
||||
int type = allLine[index]->judgeBoundIntersection(yMid, now.bound.x(), now.bound.z(), true);
|
||||
if (type == 2)
|
||||
sumIncrement++;
|
||||
if (type == 3)
|
||||
sumIncrement--;
|
||||
}
|
||||
vn.push_back(now);
|
||||
}
|
||||
else {
|
||||
ShortCutNode nw, ne;
|
||||
float xMid = (now.bound.x() + now.bound.z()) / 2;
|
||||
ne.bound = QVector4D(xMid, yMid, now.bound.z(), now.bound.w());
|
||||
isDivided |= handleShortCutNode(now, ne, yMid, vn, sumIncrement);
|
||||
nw.bound = QVector4D(now.bound.x(), yMid, xMid, now.bound.w());
|
||||
isDivided |= handleShortCutNode(now, nw, yMid, vn, sumIncrement);
|
||||
}
|
||||
}
|
||||
if (isDivided) {
|
||||
lineBound.push({yMid, seg.second});
|
||||
for (auto& now : vn) {
|
||||
Q.push(now);
|
||||
}
|
||||
}
|
||||
// 下部分
|
||||
vn.clear();
|
||||
sumIncrement = 0;
|
||||
isDivided = false;
|
||||
for (auto& now : v) {
|
||||
if (!now.divided) {
|
||||
sumIncrement += now.windingIncrement;
|
||||
vn.push_back(now);
|
||||
}
|
||||
else {
|
||||
ShortCutNode se, sw;
|
||||
float xMid = (now.bound.x() + now.bound.z()) / 2;
|
||||
yMid = (now.bound.y() + now.bound.w()) / 2;
|
||||
se.bound = QVector4D(xMid, now.bound.y(), now.bound.z(), yMid);
|
||||
isDivided |= handleShortCutNode(now, se, seg.first, vn, sumIncrement);
|
||||
sw.bound = QVector4D(now.bound.x(), now.bound.y(), xMid, yMid);
|
||||
isDivided |= handleShortCutNode(now, sw, seg.first, vn, sumIncrement);
|
||||
}
|
||||
}
|
||||
if (isDivided) {
|
||||
lineBound.push({seg.first, yMid});
|
||||
for (auto& now : vn) {
|
||||
Q.push(now);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void ShortCutTree::buildShortCutTree(vector<vLine>& lineSet) {
|
||||
init();
|
||||
Monotonization(lineSet, allLine);
|
||||
spliteToShortCutTree();
|
||||
simplifyLineVector();
|
||||
}
|
||||
|
||||
vector<BvhTreeData> ShortCutTree::getPointLineAndBvhTree(vector<float>& pointSet, vector<int>& lineSet) {
|
||||
vector<pair<int, point> > vp; vp.clear();
|
||||
for (auto& now : pointMap) {
|
||||
vp.push_back({ now.second , now.first});
|
||||
}
|
||||
sort(vp.begin(), vp.end());
|
||||
for (auto& now : vp) {
|
||||
pointSet.push_back(now.second.x);
|
||||
pointSet.push_back(now.second.y);
|
||||
//now.second.show();
|
||||
//std::cout << '\n';
|
||||
}
|
||||
for (auto& now : lineIndexSet) {
|
||||
lineSet.push_back(now);
|
||||
//std::cout << now << ' ';
|
||||
}
|
||||
std::cout << '\n';
|
||||
vector<BvhTreeData> v;
|
||||
for (auto& now : outTree) {
|
||||
BvhTreeData oneData;
|
||||
oneData.leftSon = lineSet.size();
|
||||
//std::cout << now.lineSet.size() << ' ';
|
||||
lineSet.push_back(now.lineSet.size());
|
||||
for (auto& index : now.lineSet) {
|
||||
lineSet.push_back(index);
|
||||
//std::cout << index << ' ';
|
||||
}
|
||||
//std::cout << '\n';
|
||||
v.push_back(oneData);
|
||||
}
|
||||
return v;
|
||||
}
|
|
@ -0,0 +1,56 @@
|
|||
#pragma once
|
||||
#include "CubicBezier.h"
|
||||
#include "StraightLine.h"
|
||||
#include "BvhTree.h"
|
||||
#include <queue>
|
||||
|
||||
using std::queue;
|
||||
using std::map;
|
||||
using std::for_each;
|
||||
|
||||
struct ShortCutNode {
|
||||
typedef vector<point> vLine;
|
||||
int windingIncrement;
|
||||
bool divided;
|
||||
/* type 代表进入广度优先搜索队列的节点种类:
|
||||
0:不需要拆开的节点
|
||||
1:需要拆分*/
|
||||
QVector4D bound;
|
||||
vector<int> lineSet;
|
||||
ShortCutNode() {
|
||||
divided = true;
|
||||
windingIncrement = 0;
|
||||
lineSet.clear();
|
||||
bound = { 0, 0, 0, 0 };
|
||||
}
|
||||
~ShortCutNode() {}
|
||||
};
|
||||
class ShortCutTree
|
||||
{
|
||||
typedef vector<point> vLine;
|
||||
typedef vector<int> LineIndex;
|
||||
private:
|
||||
vector<ShortCutNode> outTree;
|
||||
vector<LinePtr> allLine;
|
||||
int RequiredLineMi, numPoint, numLine;
|
||||
map<point, int> pointMap;
|
||||
vector<int> lineIndexSet;
|
||||
|
||||
int getPointIndex(point now);
|
||||
void generateShortCutSegement(ShortCutNode& now);
|
||||
bool handleShortCutNode(ShortCutNode& fa, ShortCutNode& now, float yValue, vector<ShortCutNode>& v, int& sumIncrement);
|
||||
void spliteToShortCutTree();
|
||||
static void Monotonization(vector<vLine>& inL, vector<LinePtr> &outL);
|
||||
bool isLineEqual(LineIndex& a, LineIndex& b) const;
|
||||
void simplifyLineVector();
|
||||
public:
|
||||
void init();
|
||||
ShortCutTree(int lineMi = 3)
|
||||
:RequiredLineMi(lineMi), numPoint(0), numLine(0) {}
|
||||
// 传入一个vector<vector<point> > 作为所有输入的线
|
||||
void buildShortCutTree(vector<vLine>& lineSet);
|
||||
// 获得点集合和线集合 返回输入BvhTree的数据集合
|
||||
vector<BvhTreeData> getPointLineAndBvhTree(vector<float> &pointSet, vector<int> &lineSet);
|
||||
~ShortCutTree() {}
|
||||
};
|
||||
|
|
@ -0,0 +1,48 @@
|
|||
#include "StraightLine.h"
|
||||
|
||||
float StraightLine::getLineValueByT(float t, bool isY) {
|
||||
float pBe, pEn;
|
||||
if (isY) {
|
||||
pBe = *vY.begin();
|
||||
pEn = *vY.rbegin();
|
||||
}
|
||||
else {
|
||||
pBe = *vX.begin();
|
||||
pEn = *vY.rbegin();
|
||||
}
|
||||
return t * (pEn - pBe) + pBe;
|
||||
}
|
||||
|
||||
float StraightLine::findTByValue(float value, bool isY) {
|
||||
point be = getPointByIndex(0), en = getPointByIndex(1);
|
||||
if (!isY) {
|
||||
if(fabs(be.x - en.x) <= eps) return 0;
|
||||
return (value - be.x) / (en.x - be.x);
|
||||
}
|
||||
else {
|
||||
if (fabs(be.y - en.y) <= eps) return 0;
|
||||
return (value - be.y) / (en.y - be.y);
|
||||
}
|
||||
}
|
||||
|
||||
int StraightLine::judgeBoundIntersection(float xy, float l, float r, bool isY) {
|
||||
point be = getBegin(), en = getEnd();
|
||||
if (isY) {
|
||||
swap(be.x, be.y);
|
||||
swap(en.x, en.y);
|
||||
}
|
||||
if ((be.x - xy) * (en.x - xy) > eps) return 0;
|
||||
if (direction(isY)) {
|
||||
float t = findTByValue(xy, isY);
|
||||
float value = getLineValueByT(t, !isY);
|
||||
if (l <= value && value <= r && fabs(t) > eps) {
|
||||
return 1 + direction(isY);
|
||||
}
|
||||
return 0;
|
||||
} else {
|
||||
if (be.y <= l && en.y < l) return 0;
|
||||
if (be.y >= r && en.y > r) return 0;
|
||||
return 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,10 @@
|
|||
#include "Line.h"
|
||||
|
||||
class StraightLine : public Line
|
||||
{
|
||||
using Line::Line;
|
||||
virtual float getLineValueByT(float t, bool isY);
|
||||
virtual void monotonization(vector <LinePtr>& res) {};
|
||||
virtual float findTByValue(float value, bool isY);
|
||||
virtual int judgeBoundIntersection(float xy, float l, float r, bool isY);
|
||||
};
|
Loading…
Reference in New Issue