二维Catmull-Clark 细分

#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;
}

  • 3
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值