diff options
Diffstat (limited to 'source/blender/blenkernel')
-rw-r--r-- | source/blender/blenkernel/intern/geometry_component_curve.cc | 31 | ||||
-rw-r--r-- | source/blender/blenkernel/intern/geometry_component_mesh.cc | 236 |
2 files changed, 263 insertions, 4 deletions
diff --git a/source/blender/blenkernel/intern/geometry_component_curve.cc b/source/blender/blenkernel/intern/geometry_component_curve.cc index afafd766760..7d0537178ef 100644 --- a/source/blender/blenkernel/intern/geometry_component_curve.cc +++ b/source/blender/blenkernel/intern/geometry_component_curve.cc @@ -222,6 +222,37 @@ static void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, mixer.finalize(); } +/** + * A spline is selected if all of its control points were selected. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ +template<> +void adapt_curve_domain_point_to_spline_impl(const CurveEval &curve, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + const int splines_len = curve.splines().size(); + Array<int> offsets = curve.control_point_offsets(); + BLI_assert(r_values.size() == splines_len); + + r_values.fill(true); + + for (const int i_spline : IndexRange(splines_len)) { + const int spline_offset = offsets[i_spline]; + const int spline_point_len = offsets[i_spline + 1] - spline_offset; + + for (const int i_point : IndexRange(spline_point_len)) { + if (!old_values[spline_offset + i_point]) { + r_values[i_spline] = false; + break; + } + } + } +} + static GVArrayPtr adapt_curve_domain_point_to_spline(const CurveEval &curve, GVArrayPtr varray) { GVArrayPtr new_varray; diff --git a/source/blender/blenkernel/intern/geometry_component_mesh.cc b/source/blender/blenkernel/intern/geometry_component_mesh.cc index 9a4b8f4eb92..25ef4b6000f 100644 --- a/source/blender/blenkernel/intern/geometry_component_mesh.cc +++ b/source/blender/blenkernel/intern/geometry_component_mesh.cc @@ -175,6 +175,34 @@ static void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if all connected face corners were selected and it is not loose. */ +template<> +void adapt_mesh_domain_corner_to_point_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + Array<bool> loose_verts(mesh.totvert, true); + + r_values.fill(true); + for (const int loop_index : IndexRange(mesh.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int point_index = loop.v; + + loose_verts[point_index] = false; + if (!old_values[loop_index]) { + r_values[point_index] = false; + } + } + + /* Deselect loose vertices without corners that are still selected from the 'true' default. */ + for (const int vert_index : IndexRange(mesh.totvert)) { + if (loose_verts[vert_index]) { + r_values[vert_index] = false; + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -191,6 +219,13 @@ static GVArrayPtr adapt_mesh_domain_corner_to_point(const Mesh &mesh, GVArrayPtr return new_varray; } +/** + * Each corner's value is simply a copy of the value at its vertex. + * + * \note Theoretically this interpolation does not need to compute all values at once. + * However, doing that makes the implementation simpler, and this can be optimized in the future if + * only some values are required. + */ template<typename T> static void adapt_mesh_domain_point_to_corner_impl(const Mesh &mesh, const VArray<T> &old_values, @@ -209,10 +244,6 @@ static GVArrayPtr adapt_mesh_domain_point_to_corner(const Mesh &mesh, GVArrayPtr GVArrayPtr new_varray; attribute_math::convert_to_static_type(varray->type(), [&](auto dummy) { using T = decltype(dummy); - /* It is not strictly necessary to compute the value for all corners here. Instead one could - * lazily lookup the mesh topology when a specific index accessed. This can be more efficient - * when an algorithm only accesses very few of the corner values. However, for the algorithms - * we currently have, precomputing the array is fine. Also, it is easier to implement. */ Array<T> values(mesh.totloop); adapt_mesh_domain_point_to_corner_impl<T>(mesh, varray->typed<T>(), values); new_varray = std::make_unique<fn::GVArray_For_ArrayContainer<Array<T>>>(std::move(values)); @@ -244,6 +275,26 @@ static void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its corners were selected. */ +template<> +void adapt_mesh_domain_corner_to_face_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + if (!old_values[loop_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -282,6 +333,41 @@ static void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if all corners on adjacent faces were selected. */ +template<> +void adapt_mesh_domain_corner_to_edge_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + /* It may be possible to rely on the #ME_LOOSEEDGE flag, but that seems error-prone. */ + Array<bool> loose_edges(mesh.totedge, true); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const int loop_index_next = (loop_index == poly.totloop) ? poly.loopstart : (loop_index + 1); + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + loose_edges[edge_index] = false; + + if (!old_values[loop_index] || !old_values[loop_index_next]) { + r_values[edge_index] = false; + } + } + } + + /* Deselect loose edges without corners that are still selected from the 'true' default. */ + for (const int edge_index : IndexRange(mesh.totedge)) { + if (loose_edges[edge_index]) { + r_values[edge_index] = false; + } + } +} + static GVArrayPtr adapt_mesh_domain_corner_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -317,6 +403,27 @@ void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if any of the connected faces were selected. */ +template<> +void adapt_mesh_domain_face_to_point_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + + r_values.fill(false); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + if (old_values[poly_index]) { + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int vert_index = loop.v; + r_values[vert_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -331,6 +438,7 @@ static GVArrayPtr adapt_mesh_domain_face_to_point(const Mesh &mesh, GVArrayPtr v return new_varray; } +/* Each corner's value is simply a copy of the value at its face. */ template<typename T> void adapt_mesh_domain_face_to_corner_impl(const Mesh &mesh, const VArray<T> &old_values, @@ -378,6 +486,27 @@ void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if any connected face was selected. */ +template<> +void adapt_mesh_domain_face_to_edge_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + r_values.fill(false); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + if (old_values[poly_index]) { + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + r_values[edge_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_face_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -416,6 +545,28 @@ static void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its vertices were selected too. */ +template<> +void adapt_mesh_domain_point_to_face_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + MLoop &loop = mesh.mloop[loop_index]; + const int vert_index = loop.v; + if (!old_values[vert_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_point_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -452,6 +603,20 @@ static void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, mixer.finalize(); } +/* An edge is selected if both of its vertices were selected. */ +template<> +void adapt_mesh_domain_point_to_edge_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totedge); + + for (const int edge_index : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[edge_index]; + r_values[edge_index] = old_values[edge.v1] && old_values[edge.v2]; + } +} + static GVArrayPtr adapt_mesh_domain_point_to_edge(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -490,6 +655,29 @@ void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, mixer.finalize(); } +/* A corner is selected if its two adjacent edges were selected. */ +template<> +void adapt_mesh_domain_edge_to_corner_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totloop); + + r_values.fill(false); + + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const int loop_index_prev = loop_index - 1 + (loop_index == poly.loopstart) * poly.totloop; + const MLoop &loop = mesh.mloop[loop_index]; + const MLoop &loop_prev = mesh.mloop[loop_index_prev]; + if (old_values[loop.e] && old_values[loop_prev.e]) { + r_values[loop_index] = true; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_corner(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -522,6 +710,24 @@ static void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, mixer.finalize(); } +/* A vertex is selected if any connected edge was selected. */ +template<> +void adapt_mesh_domain_edge_to_point_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totvert); + + r_values.fill(false); + for (const int edge_index : IndexRange(mesh.totedge)) { + const MEdge &edge = mesh.medge[edge_index]; + if (old_values[edge_index]) { + r_values[edge.v1] = true; + r_values[edge.v2] = true; + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_point(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; @@ -560,6 +766,28 @@ static void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, mixer.finalize(); } +/* A face is selected if all of its edges are selected. */ +template<> +void adapt_mesh_domain_edge_to_face_impl(const Mesh &mesh, + const VArray<bool> &old_values, + MutableSpan<bool> r_values) +{ + BLI_assert(r_values.size() == mesh.totpoly); + + r_values.fill(true); + for (const int poly_index : IndexRange(mesh.totpoly)) { + const MPoly &poly = mesh.mpoly[poly_index]; + for (const int loop_index : IndexRange(poly.loopstart, poly.totloop)) { + const MLoop &loop = mesh.mloop[loop_index]; + const int edge_index = loop.e; + if (!old_values[edge_index]) { + r_values[poly_index] = false; + break; + } + } + } +} + static GVArrayPtr adapt_mesh_domain_edge_to_face(const Mesh &mesh, GVArrayPtr varray) { GVArrayPtr new_varray; |