Splines: Add a method for reversing a Spline

This moved the spline reversing logic out of the Curve Reverse geometry
node and into the spline class. This allows a spline to reverse itself
with a call to `my_spline.reverse()`

The base class will reverse position, radii & tilt, while specialized
versions are created for Bezier and Nurbs splines to reverse the
additional data that these classes encapsulate.

Differential Revision: https://developer.blender.org/D12501
This commit is contained in:
Johnny Matthews 2021-09-15 13:57:01 -05:00 committed by Hans Goudey
parent a866a32ff2
commit 231948f33f
6 changed files with 62 additions and 40 deletions

View File

@ -130,6 +130,11 @@ class Spline {
virtual void translate(const blender::float3 &translation);
virtual void transform(const blender::float4x4 &matrix);
/**
* Change the direction of the spline (switch the start and end) without changing its shape.
*/
void reverse();
/**
* Mark all caches for re-computation. This must be called after any operation that would
* change the generated positions, tangents, normals, mapping, etc. of the evaluated points.
@ -210,6 +215,7 @@ class Spline {
virtual void correct_end_tangents() const = 0;
virtual void copy_settings(Spline &dst) const = 0;
virtual void copy_data(Spline &dst) const = 0;
virtual void reverse_impl() = 0;
};
/**
@ -353,6 +359,9 @@ class BezierSpline final : public Spline {
void correct_end_tangents() const final;
void copy_settings(Spline &dst) const final;
void copy_data(Spline &dst) const final;
protected:
void reverse_impl() override;
};
/**
@ -469,6 +478,7 @@ class NURBSpline final : public Spline {
void correct_end_tangents() const final;
void copy_settings(Spline &dst) const final;
void copy_data(Spline &dst) const final;
void reverse_impl() override;
void calculate_knots() const;
blender::Span<BasisCache> calculate_basis_cache() const;
@ -519,6 +529,7 @@ class PolySpline final : public Spline {
void correct_end_tangents() const final;
void copy_settings(Spline &dst) const final;
void copy_data(Spline &dst) const final;
void reverse_impl() override;
};
/**

View File

@ -19,6 +19,8 @@
#include "BLI_task.hh"
#include "BLI_timeit.hh"
#include "BKE_attribute_access.hh"
#include "BKE_attribute_math.hh"
#include "BKE_spline.hh"
#include "FN_generic_virtual_array.hh"
@ -28,6 +30,8 @@ using blender::float3;
using blender::IndexRange;
using blender::MutableSpan;
using blender::Span;
using blender::attribute_math::convert_to_static_type;
using blender::bke::AttributeIDRef;
using blender::fn::GMutableSpan;
using blender::fn::GSpan;
using blender::fn::GVArray;
@ -110,6 +114,31 @@ void Spline::transform(const blender::float4x4 &matrix)
this->mark_cache_invalid();
}
void Spline::reverse()
{
this->positions().reverse();
this->radii().reverse();
this->tilts().reverse();
this->attributes.foreach_attribute(
[&](const AttributeIDRef &id, const AttributeMetaData &meta_data) {
std::optional<blender::fn::GMutableSpan> attribute = this->attributes.get_for_write(id);
if (!attribute) {
BLI_assert_unreachable();
return false;
}
convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
attribute->typed<T>().reverse();
});
return true;
},
ATTR_DOMAIN_POINT);
this->reverse_impl();
this->mark_cache_invalid();
}
int Spline::evaluated_edges_size() const
{
const int eval_size = this->evaluated_points_size();

View File

@ -166,6 +166,17 @@ MutableSpan<float3> BezierSpline::handle_positions_right()
return handle_positions_right_;
}
void BezierSpline::reverse_impl()
{
this->handle_positions_left().reverse();
this->handle_positions_right().reverse();
std::swap(this->handle_positions_left_, this->handle_positions_right_);
this->handle_types_left().reverse();
this->handle_types_right().reverse();
std::swap(this->handle_types_left_, this->handle_types_right_);
}
static float3 previous_position(Span<float3> positions, const bool cyclic, const int i)
{
if (i == 0) {

View File

@ -142,6 +142,11 @@ Span<float> NURBSpline::weights() const
return weights_;
}
void NURBSpline::reverse_impl()
{
this->weights().reverse();
}
void NURBSpline::mark_cache_invalid()
{
basis_cache_dirty_ = true;

View File

@ -91,6 +91,10 @@ Span<float> PolySpline::tilts() const
return tilts_;
}
void PolySpline::reverse_impl()
{
}
void PolySpline::mark_cache_invalid()
{
tangent_cache_dirty_ = true;

View File

@ -49,47 +49,9 @@ static void geo_node_curve_reverse_exec(GeoNodeExecParams params)
threading::parallel_for(splines.index_range(), 128, [&](IndexRange range) {
for (const int i : range) {
if (!selection[i]) {
continue;
if (selection[i]) {
splines[i]->reverse();
}
splines[i]->positions().reverse();
splines[i]->radii().reverse();
splines[i]->tilts().reverse();
splines[i]->attributes.foreach_attribute(
[&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) {
std::optional<blender::fn::GMutableSpan> output_attribute =
splines[i]->attributes.get_for_write(attribute_id);
if (!output_attribute) {
BLI_assert_unreachable();
return false;
}
attribute_math::convert_to_static_type(meta_data.data_type, [&](auto dummy) {
using T = decltype(dummy);
output_attribute->typed<T>().reverse();
});
return true;
},
ATTR_DOMAIN_POINT);
/* Deal with extra info on derived types. */
if (BezierSpline *spline = dynamic_cast<BezierSpline *>(splines[i].get())) {
spline->handle_types_left().reverse();
spline->handle_types_right().reverse();
spline->handle_positions_left().reverse();
spline->handle_positions_right().reverse();
for (int i : spline->handle_positions_left().index_range()) {
std::swap(spline->handle_positions_left()[i], spline->handle_positions_right()[i]);
}
}
else if (NURBSpline *spline = dynamic_cast<NURBSpline *>(splines[i].get())) {
spline->weights().reverse();
}
/* Nothing to do for poly splines. */
splines[i]->mark_cache_invalid();
}
});