六、OrcaSlicer 切片之区域

一、三角形选择器 TriangleSelector

选中模型后,点击涂色,可以选择不同耗材和不同工具类型,对模型进行涂色,这种情况会打破原模型的三角面片。

1、光标类型 CursorType

cpp 复制代码
enum CursorType {
    CIRCLE,     // 圆形光标(2D平面)
    SPHERE,     // 球形光标(3D空间)
    POINTER,    // 指针光标
    // BBS 扩展
    HEIGHT_RANGE, // 高度范围光标
    GAP_FILL,    // 间隙填充光标
};

2、ClippingPlane

结构体定义了一个裁剪平面,用于限制三角形选择的范围。这是实现精确三角形选择和分割的重要组件。

cpp 复制代码
    struct ClippingPlane
    {
        Vec3f normal;
        float offset;
        ClippingPlane() : normal{0.f, 0.f, 1.f}, offset{FLT_MAX} {};
        explicit ClippingPlane(const std::array<float, 4> &clp) : normal{clp[0], clp[1], clp[2]}, offset{clp[3]} {}

        bool is_active() const { return offset != FLT_MAX; }

        bool is_mesh_point_clipped(const Vec3f &point) const { return normal.dot(point) - offset > 0.f; }
    };

裁剪平面的工作原理基于三维空间中的点-平面关系

is_mesh_point_clipped函数用来判断给定点是否被裁剪平面裁剪

  • 如果 normal · point > offset :点在平面的正方向(被裁剪)

  • 如果 normal · point < offset :点在平面的负方向(未被裁剪)

  • 如果 normal · point = offset :点在平面上

点积的几何意义:如果b为单位向量,点积就是a在b上的投影。

3、select_patch

当点击涂色时,左击在模型上进行涂色时,调用此方法,每移动一次都会调用一次。如选择圆进行涂色,会对三角面进行重新分割。最后会调用select_triangle_recursive 使用广度优先算法进行递归。调用split_triangle进行三角形的分割。

调用triangle_midpoint_or_allocate对边进行取中间点进行分割,分割后的顶点数据会写入m_vertices中。

调用push_triangle,分割出来的点重新组合成三角型的顶点索引,写入m_triangles中。

在perform_split中,将新的三角面片索引写到Triangle的children中。

4、triangle_midpoint_or_allocate 获取中间点

cpp 复制代码
int TriangleSelector::triangle_midpoint_or_allocate(int itriangle, int vertexi, int vertexj)
{
    int midpoint = this->triangle_midpoint(itriangle, vertexi, vertexj);
    if (midpoint == -1) {
        Vec3f c = 0.5f * (m_vertices[vertexi].v + m_vertices[vertexj].v);
#ifdef EXPENSIVE_DEBUG_CHECKS
        // Verify that the vertex is really a new one.
        auto it = std::find_if(m_vertices.begin(), m_vertices.end(), [c](const Vertex &v) {
            return v.ref_cnt > 0 && (v.v - c).norm() < EPSILON; });
        assert(it == m_vertices.end());
#endif // EXPENSIVE_DEBUG_CHECKS
        // Allocate a new vertex, possibly reusing the free list.
        if (m_free_vertices_head == -1) {
            // Allocate a new vertex.
            midpoint = int(m_vertices.size());
            m_vertices.emplace_back(c);
        } else {
            // Reuse a vertex from the free list.
            assert(m_free_vertices_head >= -1 && m_free_vertices_head < int(m_vertices.size()));
            midpoint = m_free_vertices_head;
            memcpy(&m_free_vertices_head, &m_vertices[midpoint].v[0], sizeof(m_free_vertices_head));
            assert(m_free_vertices_head >= -1 && m_free_vertices_head < int(m_vertices.size()));
            m_vertices[midpoint].v = c;
        }
        assert(m_vertices[midpoint].ref_cnt == 0);
    } else {
#ifndef NDEBUG
        Vec3f c1 = 0.5f * (m_vertices[vertexi].v + m_vertices[vertexj].v);
        Vec3f c2 = m_vertices[midpoint].v;
        float d = (c2 - c1).norm();
        assert(std::abs(d) < EPSILON);
#endif // NDEBUG
        assert(m_vertices[midpoint].ref_cnt > 0);
    }
    return midpoint;
}

5、push_triangle 重新组合三角形

cpp 复制代码
int TriangleSelector::push_triangle(int a, int b, int c, int source_triangle, const EnforcerBlockerType state)
{
    for (int i : {a, b, c}) {
        assert(i >= 0 && i < int(m_vertices.size()));
        ++m_vertices[i].ref_cnt;
    }
    int idx;
    if (m_free_triangles_head == -1) {
        // Allocate a new triangle.
        assert(m_invalid_triangles == 0);
        idx = int(m_triangles.size());
        m_triangles.emplace_back(a, b, c, source_triangle, state);
    } else {
        // Reuse triangle from the free list.
        assert(m_free_triangles_head >= -1 && m_free_triangles_head < int(m_triangles.size()));
        assert(! m_triangles[m_free_triangles_head].valid());
        assert(m_invalid_triangles > 0);
        idx = m_free_triangles_head;
        m_free_triangles_head = m_triangles[idx].children[0];
        -- m_invalid_triangles;
        assert(m_free_triangles_head >= -1 && m_free_triangles_head < int(m_triangles.size()));
        assert(m_free_triangles_head == -1 || ! m_triangles[m_free_triangles_head].valid());
        assert(m_invalid_triangles >= 0);
        assert((m_invalid_triangles == 0) == (m_free_triangles_head == -1));
        m_triangles[idx] = {a, b, c, source_triangle, state};
    }
    assert(m_triangles[idx].valid());
    return idx;
}

6、perform_split 完成三角形分割

调用4,5后,将新的三角面片索引写到Triangle的children中

cpp 复制代码
void TriangleSelector::perform_split(int facet_idx, const Vec3i &neighbors, EnforcerBlockerType old_state)
{
    // Reserve space for the new triangles upfront, so that the reference to this triangle will not change.
    {
        size_t num_triangles_new = m_triangles.size() + m_triangles[facet_idx].number_of_split_sides() + 1;
        if (m_triangles.capacity() < num_triangles_new)
            m_triangles.reserve(next_highest_power_of_2(num_triangles_new));
    }

    Triangle &tr = m_triangles[facet_idx];
    assert(tr.is_split());

    // indices of triangle vertices
//#ifdef NDEBUG
//    boost::container::small_vector<int, 6> verts_idxs;
//#else // NDEBUG
    // For easier debugging.
    std::vector<int> verts_idxs;
    verts_idxs.reserve(6);
//#endif // NDEBUG
    for (int j=0, idx = tr.special_side(); j<3; ++j, idx = next_idx_modulo(idx, 3))
        verts_idxs.push_back(tr.verts_idxs[idx]);

    auto get_alloc_vertex = [this, &neighbors, &verts_idxs](int edge, int i1, int i2) -> int {
        return this->triangle_midpoint_or_allocate(neighbors(edge), verts_idxs[i1], verts_idxs[i2]);
    };

    int ichild = 0;
    switch (tr.number_of_split_sides()) {
    case 1:
        verts_idxs.insert(verts_idxs.begin()+2, get_alloc_vertex(next_idx_modulo(tr.special_side(), 3), 2, 1));
        tr.children[ichild ++] = push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[2], tr.source_triangle, old_state);
        tr.children[ichild   ] = push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[0], tr.source_triangle, old_state);
        break;

    case 2:
        verts_idxs.insert(verts_idxs.begin()+1, get_alloc_vertex(tr.special_side(), 1, 0));
        verts_idxs.insert(verts_idxs.begin()+4, get_alloc_vertex(prev_idx_modulo(tr.special_side(), 3), 0, 3));
        tr.children[ichild ++] = push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[4], tr.source_triangle, old_state);
        tr.children[ichild ++] = push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[4], tr.source_triangle, old_state);
        tr.children[ichild   ] = push_triangle(verts_idxs[2], verts_idxs[3], verts_idxs[4], tr.source_triangle, old_state);
        break;

    case 3:
        assert(tr.special_side() == 0);
        verts_idxs.insert(verts_idxs.begin()+1, get_alloc_vertex(0, 1, 0));
        verts_idxs.insert(verts_idxs.begin()+3, get_alloc_vertex(1, 3, 2));
        verts_idxs.insert(verts_idxs.begin()+5, get_alloc_vertex(2, 0, 4));
        tr.children[ichild ++] = push_triangle(verts_idxs[0], verts_idxs[1], verts_idxs[5], tr.source_triangle, old_state);
        tr.children[ichild ++] = push_triangle(verts_idxs[1], verts_idxs[2], verts_idxs[3], tr.source_triangle, old_state);
        tr.children[ichild ++] = push_triangle(verts_idxs[3], verts_idxs[4], verts_idxs[5], tr.source_triangle, old_state);
        tr.children[ichild   ] = push_triangle(verts_idxs[1], verts_idxs[3], verts_idxs[5], tr.source_triangle, old_state);
        break;

    default:
        break;
    }

#ifndef NDEBUG
    assert(this->verify_triangle_neighbors(tr, neighbors));
    for (int i = 0; i <= tr.number_of_split_sides(); ++i) {
        Vec3i n = this->child_neighbors(tr, neighbors, i);
        assert(this->verify_triangle_neighbors(m_triangles[tr.children[i]], n));
    }
#endif // NDEBUG
}

7、update_model_object

当鼠标弹起后,会调用此函数将分割的数据写入到ModelVolume的mmu_segmentation_facets中

cpp 复制代码
void GLGizmoMmuSegmentation::update_model_object()
{
    bool updated = false;
    ModelObject* mo = m_c->selection_info()->model_object();
    int idx = -1;
    for (ModelVolume* mv : mo->volumes) {
        if (! mv->is_model_part())
            continue;
        ++idx;
        updated |= mv->mmu_segmentation_facets.set(*m_triangle_selectors[idx].get());
    }

    if (updated) {
        const ModelObjectPtrs &mos = wxGetApp().model().objects;
        size_t obj_idx = std::find(mos.begin(), mos.end(), mo) - mos.begin();
        wxGetApp().obj_list()->update_info_items(obj_idx);
        wxGetApp().plater()->get_partplate_list().notify_instance_update(obj_idx, 0);
        m_parent.post_event(SimpleEvent(EVT_GLCANVAS_SCHEDULE_BACKGROUND_PROCESS));
    }
}
相关推荐
2401_841495642 小时前
【LeetCode刷题】跳跃游戏Ⅱ
数据结构·python·算法·leetcode·数组·贪心策略·跳跃游戏
leaves falling2 小时前
动态规划讲解
算法·动态规划
钓鱼的肝2 小时前
GESP系列(3级)小杨的储蓄
开发语言·数据结构·c++·笔记·算法·gesp
MicroTech20252 小时前
MLGO微算法科技推出人工智能与量子计算融合新成果:基于QLSS与LCHS的量子DPM算法技术
人工智能·科技·算法
AndrewHZ2 小时前
【图像处理基石】[特殊字符]圣诞特辑:10+经典图像处理算法,让你的图片充满节日氛围感!
图像处理·人工智能·opencv·算法·计算机视觉·stable diffusion·节日氛围感
艾醒3 小时前
大模型原理剖析——矩阵吸收优化:LLM推理加速的核心原理与实践
算法
艾醒3 小时前
大模型原理剖析——多头并行 + 潜变量协同:原理、应用与部署优化
算法
KingRumn3 小时前
Linux信号之信号安全
linux·算法
智驱力人工智能3 小时前
从合规到习惯 海上作业未穿救生衣AI识别系统的工程实践与体系价值 未穿救生衣检测 AI救生衣状态识别 边缘计算救生衣监测设备
人工智能·深度学习·opencv·算法·目标检测·边缘计算