mirror of
https://github.com/OrcaSlicer/OrcaSlicer.git
synced 2026-10-09 00:31:19 +00:00
Merge upstream/main into belt/rebase/may-18
Reconciles the belt-printer branch with upstream PRs through #13723. Six files had conflicts; three additional files needed manual follow-up fixes where the auto-merge produced code that referenced upstream-renamed fields or changed function signatures. Notable reconciliations: - TreeSupport.cpp: kept belt-floor early-exit branches around HEAD's drop-down logic, folded upstream's `(distance_to_top > 0 ? 1 : 0)` formula into the non-belt-floor path (upstream PR #11812). Dropped dead `roof_enabled`/`force_tip_to_roof` locals. - TreeSupport3D.cpp: combined upstream's safety-offset + remove_small changes with HEAD's belt-floor clip in the per-slice trim loop. Dropped HEAD's `else` block (superseded by upstream's rewritten bottom-contact propagation) and re-added the belt-floor clip into the new propagation loop. Gated the propagation on belt printers to prevent OOM when belt-floor clipping produces empty initial slices. - TriangleSelector.{cpp,hpp}: merged both new `select_patch` parameters (HEAD's `up_direction` and upstream's `select_partially`); body uses `dot(up_direction)` for the overhang angle check and forwards `select_partially` to `select_triangle`. - SupportMaterial.cpp: `slicing_params.soluble_interface` → `zero_gap_interface_bottom` in HEAD's `detect_belt_floor_bottom_contacts`, matching upstream's same-purpose rename at line 2495. - Custom.json, GCodeWriter.cpp: simple additive merges (kept entries / includes from both sides). Verified by building OrcaSlicer (RelWithDebInfo) after a full deps rebuild (Eigen v5.0.1, libigl v2.6.0 are now managed deps) and slicing a scaled Benchy on the NORMALIZER belt-printer profile without OOM. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,9 +1,11 @@
|
||||
#include "TriangleSelector.hpp"
|
||||
#include "Model.hpp"
|
||||
#include "AABBTreeIndirect.hpp"
|
||||
|
||||
#include <boost/container/small_vector.hpp>
|
||||
#include <boost/log/trivial.hpp>
|
||||
#include <cstddef>
|
||||
#include <functional>
|
||||
#include <tbb/parallel_for.h>
|
||||
|
||||
#ifndef NDEBUG
|
||||
@@ -168,24 +170,69 @@ void TriangleSelector::Triangle::set_division(int sides_to_split, int special_si
|
||||
this->special_side_idx = char(special_side_idx);
|
||||
}
|
||||
|
||||
inline bool is_point_inside_triangle(const Vec3f &pt, const Vec3f &p1, const Vec3f &p2, const Vec3f &p3)
|
||||
// Pre-computed barycentric resolver
|
||||
// Real-time collision detection, Ericson, Chapter 3.4
|
||||
// Cache inspired by Don Hatch at https://gamedev.stackexchange.com/questions/23743/whats-the-most-efficient-way-to-find-barycentric-coordinates/23745#comment390123_23745
|
||||
struct Barycentric
|
||||
{
|
||||
// Real-time collision detection, Ericson, Chapter 3.4
|
||||
auto barycentric = [&pt, &p1, &p2, &p3]() -> Vec3f {
|
||||
std::array<Vec3f, 3> v = {p2 - p1, p3 - p1, pt - p1};
|
||||
float d00 = v[0].dot(v[0]);
|
||||
float d01 = v[0].dot(v[1]);
|
||||
float d11 = v[1].dot(v[1]);
|
||||
float d20 = v[2].dot(v[0]);
|
||||
float d21 = v[2].dot(v[1]);
|
||||
float denom = d00 * d11 - d01 * d01;
|
||||
public:
|
||||
Barycentric(const Vec3f& a, const Vec3f& b, const Vec3f& c)
|
||||
:a_(a)
|
||||
{
|
||||
// Pre-compute denominator
|
||||
const Vec3f v0 = b - a;
|
||||
const Vec3f v1 = c - a;
|
||||
const float d00 = v0.dot(v0);
|
||||
const float d01 = v0.dot(v1);
|
||||
const float d11 = v1.dot(v1);
|
||||
const float inv_denom_ = 1.0f / (d00 * d11 - d01 * d01);
|
||||
|
||||
x1_ = (d11 * v0 - d01 * v1) * inv_denom_;
|
||||
x2_ = (d00 * v1 - d01 * v0) * inv_denom_;
|
||||
}
|
||||
|
||||
Vec3f barycentric_cords(1.f, (d11 * d20 - d01 * d21) / denom, (d00 * d21 - d01 * d20) / denom);
|
||||
barycentric_cords.x() = barycentric_cords.x() - barycentric_cords.y() - barycentric_cords.z();
|
||||
return barycentric_cords;
|
||||
};
|
||||
Vec3f calc(const Vec3f& p) const
|
||||
{
|
||||
const Vec3f v2 = p - a_;
|
||||
const float v = v2.dot(x1_);
|
||||
const float w = v2.dot(x2_);
|
||||
const float u = 1.f - v - w;
|
||||
|
||||
Vec3f barycentric_cords = barycentric();
|
||||
return {u, v, w};
|
||||
}
|
||||
|
||||
bool is_point_inside_triangle(const Vec3f& p) const
|
||||
{
|
||||
const Vec3f barycentric_cords = calc(p);
|
||||
return std::all_of(begin(barycentric_cords), end(barycentric_cords), [](float cord) { return 0.f <= cord && cord <= 1.0; });
|
||||
}
|
||||
|
||||
static Vec3f calc(const Vec3f& pt, const Vec3f& p1, const Vec3f& p2, const Vec3f& p3)
|
||||
{
|
||||
const std::array<Vec3f, 3> vec = {p2 - p1, p3 - p1, pt - p1};
|
||||
const float d00 = vec[0].dot(vec[0]);
|
||||
const float d01 = vec[0].dot(vec[1]);
|
||||
const float d11 = vec[1].dot(vec[1]);
|
||||
const float d20 = vec[2].dot(vec[0]);
|
||||
const float d21 = vec[2].dot(vec[1]);
|
||||
const float denom = d00 * d11 - d01 * d01;
|
||||
|
||||
const float v = (d11 * d20 - d01 * d21) / denom;
|
||||
const float w = (d00 * d21 - d01 * d20) / denom;
|
||||
const float u = 1.f - v - w;
|
||||
|
||||
return {u, v, w};
|
||||
}
|
||||
|
||||
private:
|
||||
Vec3f a_;
|
||||
Vec3f x1_;
|
||||
Vec3f x2_;
|
||||
};
|
||||
|
||||
static bool is_point_inside_triangle(const Vec3f &pt, const Vec3f &p1, const Vec3f &p2, const Vec3f &p3)
|
||||
{
|
||||
Vec3f barycentric_cords = Barycentric::calc(pt, p1, p2, p3);
|
||||
return std::all_of(begin(barycentric_cords), end(barycentric_cords), [](float cord) { return 0.f <= cord && cord <= 1.0; });
|
||||
}
|
||||
|
||||
@@ -233,7 +280,7 @@ int TriangleSelector::select_unsplit_triangle(const Vec3f &hit, int facet_idx) c
|
||||
return this->select_unsplit_triangle(hit, facet_idx, neighbors);
|
||||
}
|
||||
|
||||
void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&cursor, EnforcerBlockerType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg, const Vec3f &up_direction)
|
||||
void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&cursor, EnforcerBlockerType new_state, const Transform3d& trafo_no_translate, bool triangle_splitting, float highlight_by_angle_deg, const Vec3f &up_direction, const bool select_partially)
|
||||
{
|
||||
assert(facet_start < m_orig_size_indices);
|
||||
|
||||
@@ -296,7 +343,7 @@ void TriangleSelector::select_patch(int facet_start, std::unique_ptr<Cursor> &&c
|
||||
Matrix3f normal_matrix = static_cast<Matrix3f>(trafo_no_translate.matrix().block(0, 0, 3, 3).inverse().transpose().cast<float>());
|
||||
float world_normal_dot = (normal_matrix * facet_normal).normalized().dot(up_direction);
|
||||
if (!visited[facet] && (highlight_by_angle_deg == 0.f || world_normal_dot < highlight_angle_limit)) {
|
||||
if (select_triangle(facet, new_state, triangle_splitting)) {
|
||||
if (select_triangle(facet, new_state, triangle_splitting, select_partially)) {
|
||||
// add neighboring facets to list to be processed later
|
||||
for (int neighbor_idx : m_neighbors[facet])
|
||||
if (neighbor_idx >= 0 && m_cursor->is_facet_visible(neighbor_idx, m_face_normals))
|
||||
@@ -552,7 +599,7 @@ void TriangleSelector::bucket_fill_select_triangles(const Vec3f& hit, int facet_
|
||||
// This is done by an actual recursive call. Returns false if the triangle is
|
||||
// outside the cursor.
|
||||
// Called by select_patch() and by itself.
|
||||
bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting)
|
||||
bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type, bool triangle_splitting, bool select_partially)
|
||||
{
|
||||
assert(facet_idx < int(m_triangles.size()));
|
||||
|
||||
@@ -562,7 +609,7 @@ bool TriangleSelector::select_triangle(int facet_idx, EnforcerBlockerType type,
|
||||
Vec3i32 neighbors = m_neighbors[facet_idx];
|
||||
assert(this->verify_triangle_neighbors(m_triangles[facet_idx], neighbors));
|
||||
|
||||
if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting))
|
||||
if (! select_triangle_recursive(facet_idx, neighbors, type, triangle_splitting, select_partially))
|
||||
return false;
|
||||
|
||||
// In case that all children are leafs and have the same state now,
|
||||
@@ -902,7 +949,7 @@ Vec3i32 TriangleSelector::child_neighbors_propagated(const Triangle &tr, const V
|
||||
return out;
|
||||
}
|
||||
|
||||
bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i32 &neighbors, EnforcerBlockerType type, bool triangle_splitting)
|
||||
bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i32 &neighbors, EnforcerBlockerType type, bool triangle_splitting, bool select_partially)
|
||||
{
|
||||
assert(facet_idx < int(m_triangles.size()));
|
||||
|
||||
@@ -935,8 +982,10 @@ bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i32 &n
|
||||
|
||||
if (triangle_splitting)
|
||||
split_triangle(facet_idx, neighbors);
|
||||
else if (!m_triangles[facet_idx].is_split())
|
||||
if ((!triangle_splitting || select_partially) && !m_triangles[facet_idx].is_split()) {
|
||||
m_triangles[facet_idx].set_state(type);
|
||||
return true;
|
||||
}
|
||||
tr = &m_triangles[facet_idx]; // might have been invalidated by split_triangle().
|
||||
|
||||
int num_of_children = tr->number_of_split_sides() + 1;
|
||||
@@ -946,7 +995,7 @@ bool TriangleSelector::select_triangle_recursive(int facet_idx, const Vec3i32 &n
|
||||
assert(tr->children[i] < int(m_triangles.size()));
|
||||
// Recursion, deep first search over the children of this triangle.
|
||||
// All children of this triangle were created by splitting a single source triangle of the original mesh.
|
||||
select_triangle_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i), type, triangle_splitting);
|
||||
select_triangle_recursive(tr->children[i], this->child_neighbors(*tr, neighbors, i), type, triangle_splitting, select_partially);
|
||||
tr = &m_triangles[facet_idx]; // might have been invalidated
|
||||
}
|
||||
}
|
||||
@@ -2208,4 +2257,271 @@ std::vector<EnforcerBlockerType> TriangleSelector::extract_used_facet_states(con
|
||||
return out;
|
||||
}
|
||||
|
||||
static bool segments_intersect_proj(const Vec3f& p1, const Vec3f& p2, const Vec3f& p3, const Vec3f& p4, const std::pair<int, int>& proj)
|
||||
{
|
||||
auto cross2d = [](float ax, float ay, float bx, float by) -> float { return ax * by - ay * bx; };
|
||||
const auto [u_axis, v_axis] = proj;
|
||||
const auto u1 = p1(u_axis), v1 = p1(v_axis);
|
||||
const auto u2 = p2(u_axis), v2 = p2(v_axis);
|
||||
const auto u3 = p3(u_axis), v3 = p3(v_axis);
|
||||
const auto u4 = p4(u_axis), v4 = p4(v_axis);
|
||||
const float ru = u2 - u1, rv = v2 - v1;
|
||||
const float su = u4 - u3, sv = v4 - v3;
|
||||
const float denom = cross2d(ru, rv, su, sv);
|
||||
if (std::abs(denom) < 1e-10f)
|
||||
return false;
|
||||
const float dpu = u3 - u1, dpv = v3 - v1;
|
||||
const float t1 = cross2d(dpu, dpv, su, sv) / denom;
|
||||
const float t2 = cross2d(dpu, dpv, ru, rv) / denom;
|
||||
return 0.f <= t1 && t1 <= 1.0f && 0.f <= t2 && t2 <= 1.f;
|
||||
};
|
||||
|
||||
class TriangleCursor : public TriangleSelector::SinglePointCursor
|
||||
{
|
||||
public:
|
||||
TriangleCursor() = delete;
|
||||
explicit TriangleCursor(const Vec3f& center_,
|
||||
const Vec3f& source_,
|
||||
float radius_world,
|
||||
const Transform3d& trafo_,
|
||||
const TriangleSelector::ClippingPlane& clipping_plane_,
|
||||
const std::array<const Vec3f, 3>& vertices)
|
||||
: TriangleSelector::SinglePointCursor(center_, source_, radius_world, trafo_, clipping_plane_)
|
||||
, barycentric_(vertices[0], vertices[1], vertices[2])
|
||||
{}
|
||||
~TriangleCursor() override = default;
|
||||
|
||||
static std::unique_ptr<Cursor> build_cursor(const TriangleSelector& source_selector, const TriangleSelector::Triangle& tri)
|
||||
{
|
||||
const Vec3f& pv0 = source_selector.m_vertices[tri.verts_idxs[0]].v;
|
||||
const Vec3f& pv1 = source_selector.m_vertices[tri.verts_idxs[1]].v;
|
||||
const Vec3f& pv2 = source_selector.m_vertices[tri.verts_idxs[2]].v;
|
||||
|
||||
// Calculate the centroid of the triangle
|
||||
const Vec3f center = (pv0 + pv1 + pv2) / 3.f;
|
||||
|
||||
// Calculate the norm of the plane
|
||||
const Vec3f& norm = source_selector.m_face_normals[tri.source_triangle];
|
||||
|
||||
// Calculate the min distance from the centroid to every edges
|
||||
const float radius = std::max(std::min(std::min(point_to_line_dist(center, pv0, pv1), point_to_line_dist(center, pv0, pv2)),
|
||||
point_to_line_dist(center, pv1, pv2)), 0.1f);
|
||||
|
||||
return std::make_unique<TriangleCursor>(center, center + norm, radius, Transform3d::Identity(), TriangleSelector::ClippingPlane(),
|
||||
std::array<const Vec3f, 3>{pv0, pv1, pv2});
|
||||
}
|
||||
|
||||
bool is_mesh_point_inside(const Vec3f& point) const override
|
||||
{
|
||||
return barycentric_.is_point_inside_triangle(point);
|
||||
}
|
||||
|
||||
bool is_edge_inside_cursor(const TriangleSelector::Triangle& tr, const std::vector<TriangleSelector::Vertex>& vertices) const override
|
||||
{
|
||||
std::array<Vec3f, 3> pts_proj; // Projected point onto the plane
|
||||
std::array<float, 3> pts_dist; // Distance to the plane, positive means above, negative means below the plane
|
||||
for (int i = 0; i < 3; ++i) {
|
||||
Vec3f p = vertices[tr.verts_idxs[i]].v;
|
||||
if (!this->uniform_scaling)
|
||||
p = this->trafo * p;
|
||||
|
||||
const Vec3f diff = p - center;
|
||||
pts_dist[i] = diff.dot(dir);
|
||||
pts_proj[i] = p - pts_dist[i] * dir;
|
||||
}
|
||||
|
||||
for (int side = 0; side < 3; ++side) {
|
||||
const int idx_a = side;
|
||||
const int idx_b = side < 2 ? side + 1 : 0;
|
||||
|
||||
// If both ends at the same side and farther than tolerance, skip
|
||||
const float dist_a = pts_dist[idx_a];
|
||||
const float dist_b = pts_dist[idx_b];
|
||||
if ((dist_a > tolerance && dist_b > tolerance) || (dist_a < -tolerance && dist_b < -tolerance)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// Find the projected line segment which has distance to the plane within the tolerance
|
||||
Vec3f pt_a = pts_proj[idx_a];
|
||||
Vec3f pt_b = pts_proj[idx_b];
|
||||
if (std::abs(dist_a) > tolerance) {
|
||||
pt_a = (tolerance - dist_b) / (dist_a - dist_b) * (pts_proj[idx_a] - pts_proj[idx_b]) + pts_proj[idx_b];
|
||||
}
|
||||
if (std::abs(dist_b) > tolerance) {
|
||||
pt_b = (tolerance - dist_a) / (dist_b - dist_a) * (pts_proj[idx_b] - pts_proj[idx_a]) + pts_proj[idx_a];
|
||||
}
|
||||
|
||||
// If any projected end is inside the triangle, then is in
|
||||
if (barycentric_.is_point_inside_triangle(pt_a) ||
|
||||
barycentric_.is_point_inside_triangle(pt_b)) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise see if the segment (pt_a, pt_b) intersects the triangle
|
||||
{
|
||||
const Vec3f uvw_a = barycentric_.calc(pt_a);
|
||||
const Vec3f uvw_b = barycentric_.calc(pt_b);
|
||||
const Vec3f uvw_0 {1.f,0.f,0.f};
|
||||
const Vec3f uvw_1 {0.f,1.f,0.f};
|
||||
const Vec3f uvw_2 {0.f,0.f,1.f};
|
||||
constexpr std::pair<int, int> proj{0, 1};
|
||||
|
||||
if (segments_intersect_proj(uvw_a, uvw_b, uvw_0, uvw_1, proj)||
|
||||
segments_intersect_proj(uvw_a, uvw_b, uvw_0, uvw_2, proj)||
|
||||
segments_intersect_proj(uvw_a, uvw_b, uvw_1, uvw_2, proj)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_facet_visible(int facet_idx, const std::vector<Vec3f>& face_normals) const override
|
||||
{
|
||||
const Vec3f& n = face_normals[facet_idx];
|
||||
|
||||
return check_normal(n, this->dir);
|
||||
}
|
||||
|
||||
static bool check_normal(const Vec3f& facet_norm, const Vec3f& camera_dir)
|
||||
{
|
||||
const float a = -(facet_norm.dot(camera_dir));
|
||||
return std::clamp(a, 0.f, 1.f) >= facet_angle_limit;
|
||||
}
|
||||
|
||||
static constexpr float tolerance = 0.01f;
|
||||
|
||||
private:
|
||||
const Barycentric barycentric_;
|
||||
static const double facet_angle_limit;
|
||||
|
||||
static float point_to_line_dist(const Vec3f& p, const Vec3f& a, const Vec3f& b)
|
||||
{
|
||||
const Eigen::ParametrizedLine<float, 3> line = Eigen::ParametrizedLine<float, 3>::Through(a, b);
|
||||
return line.distance(p);
|
||||
}
|
||||
};
|
||||
const double TriangleCursor::facet_angle_limit = cos(Geometry::deg2rad(5.0));
|
||||
|
||||
|
||||
// Remap painting data from source mesh to target mesh using spatial mapping.
|
||||
TriangleSelector::TriangleSplittingData TriangleSelector::remap_painting(
|
||||
const indexed_triangle_set& source_its,
|
||||
const TriangleSplittingData& source_painting,
|
||||
const indexed_triangle_set& target_its,
|
||||
const Transform3d& target_transform,
|
||||
const std::optional<std::reference_wrapper<const TriangleSplittingData>>& existing_painting)
|
||||
{
|
||||
TriangleSelector::TriangleSplittingData result;
|
||||
if (source_painting.bitstream.empty())
|
||||
return result;
|
||||
|
||||
// 1. Deserialize source painting
|
||||
TriangleMesh source_mesh(source_its);
|
||||
TriangleSelector source_selector(source_mesh);
|
||||
source_selector.deserialize(source_painting, false);
|
||||
|
||||
// 2. Extract painted geometry
|
||||
std::vector<std::reference_wrapper<const Triangle>> painted_triangles;
|
||||
painted_triangles.reserve(source_selector.m_triangles.size());
|
||||
for (const Triangle& tr : source_selector.m_triangles) {
|
||||
if (tr.valid() && !tr.is_split() && tr.get_state() != EnforcerBlockerType::NONE) {
|
||||
painted_triangles.push_back(std::ref(tr));
|
||||
}
|
||||
}
|
||||
|
||||
if (painted_triangles.empty())
|
||||
return result;
|
||||
|
||||
// 3. Build AABB tree of target mesh so we could find nearest face quickly
|
||||
TriangleMesh target_mesh(target_its);
|
||||
target_mesh.transform(target_transform);
|
||||
AABBTreeIndirect::Tree3f target_tree = AABBTreeIndirect::build_aabb_tree_over_indexed_triangle_set(target_mesh.its.vertices, target_mesh.its.indices);
|
||||
|
||||
// Helper: check overlap between a paint triangle and a target triangle.
|
||||
// Uses 3D barycentric point-in-triangle tests and dominant-axis 2D projection
|
||||
// for edge-edge intersection to handle all triangle orientations correctly.
|
||||
auto check_overlap = [&](const Vec3f& pa, const Vec3f& pb, const Vec3f& pc,
|
||||
const Vec3f& ta, const Vec3f& tb, const Vec3f& tc) -> bool {
|
||||
// Check if any target vertex is inside the paint triangle
|
||||
if (is_point_inside_triangle(ta, pa, pb, pc)) return true;
|
||||
if (is_point_inside_triangle(tb, pa, pb, pc)) return true;
|
||||
if (is_point_inside_triangle(tc, pa, pb, pc)) return true;
|
||||
|
||||
// Check if any paint vertex is inside the target triangle
|
||||
if (is_point_inside_triangle(pa, ta, tb, tc)) return true;
|
||||
if (is_point_inside_triangle(pb, ta, tb, tc)) return true;
|
||||
if (is_point_inside_triangle(pc, ta, tb, tc)) return true;
|
||||
|
||||
// Check edge-edge intersections using dominant-axis 2D projection.
|
||||
// Project onto the plane (XY, XZ, or YZ) where the triangles have the
|
||||
// most area, avoiding degenerate projections for vertical/angled surfaces.
|
||||
Vec3f n1 = (pb - pa).cross(pc - pa);
|
||||
Vec3f n2 = (tb - ta).cross(tc - ta);
|
||||
Vec3f n_abs = (n1.cwiseAbs() + n2.cwiseAbs());
|
||||
int axis = (n_abs.x() >= n_abs.y() && n_abs.x() >= n_abs.z()) ? 0
|
||||
: (n_abs.y() >= n_abs.z()) ? 1 : 2;
|
||||
const int u_axis = (axis + 1) % 3;
|
||||
const int v_axis = (axis + 2) % 3;
|
||||
const std::pair<int, int> proj{u_axis, v_axis};
|
||||
|
||||
if (segments_intersect_proj(pa, pb, ta, tb, proj)) return true;
|
||||
if (segments_intersect_proj(pa, pb, tb, tc, proj)) return true;
|
||||
if (segments_intersect_proj(pa, pb, tc, ta, proj)) return true;
|
||||
if (segments_intersect_proj(pb, pc, ta, tb, proj)) return true;
|
||||
if (segments_intersect_proj(pb, pc, tb, tc, proj)) return true;
|
||||
if (segments_intersect_proj(pb, pc, tc, ta, proj)) return true;
|
||||
if (segments_intersect_proj(pc, pa, ta, tb, proj)) return true;
|
||||
if (segments_intersect_proj(pc, pa, tb, tc, proj)) return true;
|
||||
if (segments_intersect_proj(pc, pa, tc, ta, proj)) return true;
|
||||
|
||||
return false;
|
||||
};
|
||||
|
||||
// 4. For each painted face, we find the nearest target face, and apply the TriangleCursor to paint it
|
||||
TriangleSelector target_selector(target_mesh);
|
||||
if (existing_painting) {
|
||||
// Restore existing painting first, if given
|
||||
target_selector.deserialize(existing_painting->get(), false);
|
||||
}
|
||||
for (auto tri_ref : painted_triangles) {
|
||||
const Triangle& tri = tri_ref.get();
|
||||
const Vec3f& pv0 = source_selector.m_vertices[tri.verts_idxs[0]].v;
|
||||
const Vec3f& pv1 = source_selector.m_vertices[tri.verts_idxs[1]].v;
|
||||
const Vec3f& pv2 = source_selector.m_vertices[tri.verts_idxs[2]].v;
|
||||
|
||||
// Find ALL target faces whose bounding boxes overlap with the paint
|
||||
// triangle's bounding box, not just the nearest one.
|
||||
Eigen::AlignedBox3f pt_bbox;
|
||||
pt_bbox.extend(pv0);
|
||||
pt_bbox.extend(pv1);
|
||||
pt_bbox.extend(pv2);
|
||||
pt_bbox.min() -= Eigen::Vector3f::Constant(TriangleCursor::tolerance);
|
||||
pt_bbox.max() += Eigen::Vector3f::Constant(TriangleCursor::tolerance);
|
||||
|
||||
AABBTreeIndirect::traverse(target_tree, AABBTreeIndirect::intersecting(pt_bbox), [&](const AABBTreeIndirect::Tree3f::Node& node) -> bool {
|
||||
size_t face_idx = node.idx;
|
||||
if (face_idx >= target_mesh.its.indices.size())
|
||||
return true;
|
||||
|
||||
const Vec3f& norm_a = source_selector.m_face_normals[tri.source_triangle];
|
||||
const Vec3f& norm_b = target_selector.m_face_normals[face_idx];
|
||||
|
||||
const Vec3i32& face = target_mesh.its.indices[face_idx];
|
||||
const Vec3f& ta = target_mesh.its.vertices[face(0)];
|
||||
const Vec3f& tb = target_mesh.its.vertices[face(1)];
|
||||
const Vec3f& tc = target_mesh.its.vertices[face(2)];
|
||||
|
||||
if (TriangleCursor::check_normal(norm_b, -norm_a) && check_overlap(pv0, pv1, pv2, ta, tb, tc)) {
|
||||
// Paint this face
|
||||
target_selector.select_patch(face_idx, TriangleCursor::build_cursor(source_selector, tri), tri.get_state(),
|
||||
Transform3d::Identity(), true, 0.f, Vec3f::UnitZ(), true);
|
||||
}
|
||||
return true; // continue traversal
|
||||
});
|
||||
}
|
||||
|
||||
return target_selector.serialize();
|
||||
}
|
||||
|
||||
} // namespace Slic3r
|
||||
|
||||
Reference in New Issue
Block a user