#include <iostream>
#include <fstream>
#include <vector>
#include <sstream>
#include <algorithm>
#include <cmath>
#include <opencv2/opencv.hpp>
using namespace cv;
using namespace std;
// 定义顶点类
class V
{
public:
int v_id;
double x, y;
};
// 定义边类
class E
{
public:
int e_id, V1, V2;
};
// 定义面类
class F
{
public:
int f_id;
vector<int> es; // 属于该面的所有边的索引
vector<int> vs; // 属于该面的所有点的索引
};
// 从 .obj 文件中读取数据
void readObjFile(const string &filename, vector<V> &vs, vector<E> &es, vector<F> &fs)
{
ifstream file(filename);
string line;
int i = 0, j = 0, k = 0;
while (getline(file, line))
{
istringstream iss(line);
string type;
iss >> type;
if (type == "v")
{ // 顶点信息
V v;
i++;
v.v_id = i;
iss >> v.x >> v.y;
vs.push_back(v);
}
else if (type == "e")
{ // 边信息
E e;
j++;
e.e_id = j;
iss >> e.V1 >> e.V2;
es.push_back(e);
}
else if (type == "f")
{ // 面信息
F f;
k++;
f.f_id = k;
string vertexIndex;
while (iss >> vertexIndex)
{
istringstream vertexIss(vertexIndex);
int vIndex;
vertexIss >> vIndex;
f.vs.push_back(vIndex);
}
for (auto &e : es)
{
int count = 0;
for (auto v : f.vs)
{
if (e.V1 == v || e.V2 == v)
{
count++;
if (count == 2)
{
f.es.push_back(e.e_id);
break;
}
}
}
}
fs.push_back(f);
}
}
}
void writeObjFile(const string &filename, const vector<V> &vs, const vector<E> &es, const vector<F> &fs)
{
ofstream file(filename);
// 写入顶点信息
for (const auto &v : vs)
{
file << "v " << v.x << " " << v.y << endl;
}
// 写入边信息
for (const auto &e : es)
{
file << "e " << e.V1 << " " << e.V2 << endl;
}
// 写入面信息
for (const auto &f : fs)
{
file << "f";
for (int v : f.vs)
{
file << " " << v; // 从1开始的索引转换为从0开始
}
file << endl;
}
}
void draw(const vector<V> &vs, const vector<E> &es)
{
cv::Mat image(600, 800, CV_8UC3, cv::Scalar(255, 255, 255));
for (const auto &v : vs)
{
cv::Point p(vs[v.v_id - 1].x * 100, vs[v.v_id - 1].y * 100);
// cout<<v.v_id+1<<" "<<vs[v.v_id+1].x * 50 + 100<<" "<<vs[v.v_id+1].y * 50 + 100<<endl;
circle(image, p, 3, Scalar(0, 255, 0), -1);
}
for (const auto &e : es)
{
cv::Point p1(vs[e.V1 - 1].x * 100, vs[e.V1 - 1].y * 100);
cv::Point p2(vs[e.V2 - 1].x * 100, vs[e.V2 - 1].y * 100);
cv::line(image, p1, p2, cv::Scalar(0, 0, 255), 1);
}
cv::imshow("画板", image);
cv::waitKey(0);
}
void catmullClark(vector<V> &vs, vector<E> &es, vector<F> &fs)
{
vector<E> new_es;
vector<F> new_fs;
int v_cnt = vs.size();
int n = v_cnt;
int e_cnt = es.size();
int f_cnt = fs.size();
int m = 0;
int k = 0;
// 生成所有面点
for (auto &f : fs)
{
double x = 0, y = 0;
for (auto v_index : f.vs)
{
x += vs[v_index - 1].x; // 索引从1开始,需要减1
y += vs[v_index - 1].y;
}
x /= f.vs.size();
y /= f.vs.size();
V faceCenter;
faceCenter.x = x;
faceCenter.y = y;
vs.push_back(faceCenter);
}
// 生成所有边点
for (auto &e : es)
{
double i = 2;
double x = (vs[e.V1 - 1].x + vs[e.V2 - 1].x); // 中点坐标计算
// cout<<"端点"<<e.V1 - 1<<" ";
double y = (vs[e.V1 - 1].y + vs[e.V2 - 1].y);
E new_edge1;
m++;
new_edge1.e_id = m; // 新边的索引从1开始
new_edge1.V1 = e.V1;
new_edge1.V2 = v_cnt + f_cnt + e.e_id;
new_es.push_back(new_edge1);
E new_edge2;
m++;
new_edge2.e_id = m; // 新边的索引从1开始
new_edge2.V1 = v_cnt + f_cnt + e.e_id;
new_edge2.V2 = e.V2;
new_es.push_back(new_edge2);
for (auto f : fs)
{
for (auto e_index : f.es)
{
if (e_index == e.e_id)
{
x += vs[v_cnt + f.f_id - 1].x; // 对相邻面中心点加权
// cout<<"相邻面"<<vs.size() + f.f_id - 1 - fs.size()<<" ";
y += vs[v_cnt + f.f_id - 1].y;
m++;
E new_edge3;
new_edge3.e_id = m;
new_edge3.V1 = v_cnt + f.f_id;
new_edge3.V2 = v_cnt + f_cnt + e.e_id;
new_es.push_back(new_edge3);
i++;
}
}
}
x = x / i;
y = y / i;
// 计算新的边点坐标
V edgeCenter;
edgeCenter.x = x;
edgeCenter.y = y;
vs.push_back(edgeCenter);
}
for (auto f : fs)
{
for (auto v_index : f.vs)
{
k++;
F new_f1;
new_f1.f_id = k;
new_f1.vs.push_back(v_index);
// cout<<v_index<<" ";
new_f1.vs.push_back(v_cnt + f.f_id);
for (auto e_index : f.es)
{
// cout<<e_index<<" :"<<es[e_index].V1<<"+"<<es[e_index].V2<<" ";
if (es[e_index - 1].V1 == v_index || es[e_index - 1].V2 == v_index)
{
new_f1.vs.push_back(v_cnt + f_cnt + e_index);
// cout<<"Yes"<<v_cnt+f_cnt+e_index;
}
}
// cout<<endl;
new_fs.push_back(new_f1);
}
}
// 更新顶点坐标
for (auto &v : vs)
{
n--;
// cout<<v.v_id<<" ";
double x = 4.0 * v.x, y = 4.0 * v.y;
int count = 4;
for (auto f : fs)
{
for (auto v_index : f.vs)
{
if (v_index == v.v_id)
{
x += vs[v_cnt + f.f_id - 1].x;
y += vs[v_cnt + f.f_id - 1].y;
// cout<<v_cnt + f.f_id <<"x:"<<x<<"y :"<<y<<endl;
count++;
}
}
}
for (auto e : es)
{
if (e.V1 == v.v_id || e.V2 == v.v_id)
{
x += 2 * vs[v_cnt + f_cnt + e.e_id - 1].x; // 对相邻边中点加权
y += 2 * vs[v_cnt + f_cnt + e.e_id - 1].y;
// cout<<v_cnt + f_cnt + e.e_id <<"x:"<<x<<"y :"<<y<<endl;
count += 2;
}
}
x /= count; // 计算新的顶点坐标
y /= count;
// cout<<" "<<count<<"x:"<<x<<"y:"<<y<<endl;
v.x = x;
v.y = y;
if (n == 0)
break;
}
// 更新所有的边
// 1) 删除旧的边:删除原来的所有边
es.clear();
// 2) 添加新的边:面点到边点的边,连接边点到旧顶点的边
es = new_es;
fs.clear();
fs = new_fs;
}
int main()
{
vector<V> vs;
vector<E> es;
vector<F> fs;
// 添加新的点、线、面
// draw(vs,es);
// 调用 Catmull-Clark 细分方法
int op;
cin >> op;
for (int i = 0; i < op; i++)
{
vs.clear();
fs.clear();
es.clear();
readObjFile("/home/cs18/gra_test/input1.obj", vs, es, fs);
catmullClark(vs, es, fs);
writeObjFile("/home/cs18/gra_test/input1.obj", vs, es, fs);
// readObjFile("/home/cs18/gra_test/input.obj", vs, es, fs);
}
vs.clear();
fs.clear();
es.clear();
draw(vs, es);
return 0;
}
二维Catmull-Clark 细分
于 2024-04-27 10:50:08 首次发布