Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/blenkernel/intern/spline_base.cc')
-rw-r--r--source/blender/blenkernel/intern/spline_base.cc184
1 files changed, 164 insertions, 20 deletions
diff --git a/source/blender/blenkernel/intern/spline_base.cc b/source/blender/blenkernel/intern/spline_base.cc
index 8956ba6adae..584156ea40f 100644
--- a/source/blender/blenkernel/intern/spline_base.cc
+++ b/source/blender/blenkernel/intern/spline_base.cc
@@ -40,6 +40,60 @@ Spline::Type Spline::type() const
return type_;
}
+void Spline::copy_base_settings(const Spline &src, Spline &dst)
+{
+ dst.normal_mode = src.normal_mode;
+ dst.is_cyclic_ = src.is_cyclic_;
+}
+
+static SplinePtr create_spline(const Spline::Type type)
+{
+ switch (type) {
+ case Spline::Type::Poly:
+ return std::make_unique<PolySpline>();
+ case Spline::Type::Bezier:
+ return std::make_unique<BezierSpline>();
+ case Spline::Type::NURBS:
+ return std::make_unique<NURBSpline>();
+ }
+ BLI_assert_unreachable();
+ return {};
+}
+
+/**
+ * Return a new spline with the same data, settings, and attributes.
+ */
+SplinePtr Spline::copy() const
+{
+ SplinePtr dst = this->copy_without_attributes();
+ dst->attributes = this->attributes;
+ return dst;
+}
+
+/**
+ * Return a new spline with the same type and settings like "cyclic", but without any data.
+ */
+SplinePtr Spline::copy_only_settings() const
+{
+ SplinePtr dst = create_spline(type_);
+ this->copy_base_settings(*this, *dst);
+ this->copy_settings(*dst);
+ return dst;
+}
+
+/**
+ * The same as #copy, but skips copying dynamic attributes to the new spline.
+ */
+SplinePtr Spline::copy_without_attributes() const
+{
+ SplinePtr dst = this->copy_only_settings();
+ this->copy_data(*dst);
+
+ /* Though the attributes storage is empty, it still needs to know the correct size. */
+ dst->attributes.reallocate(dst->size());
+ return dst;
+}
+
void Spline::translate(const blender::float3 &translation)
{
for (float3 &position : this->positions()) {
@@ -74,9 +128,9 @@ float Spline::length() const
int Spline::segments_size() const
{
- const int points_len = this->size();
+ const int size = this->size();
- return is_cyclic_ ? points_len : points_len - 1;
+ return is_cyclic_ ? size : size - 1;
}
bool Spline::is_cyclic() const
@@ -209,10 +263,86 @@ static float3 rotate_direction_around_axis(const float3 &direction,
return axis_scaled + diff * std::cos(angle) + cross * std::sin(angle);
}
-static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> normals)
+static void calculate_normals_z_up(Span<float3> tangents, MutableSpan<float3> r_normals)
{
- for (const int i : normals.index_range()) {
- normals[i] = float3::cross(tangents[i], float3(0.0f, 0.0f, 1.0f)).normalized();
+ BLI_assert(r_normals.size() == tangents.size());
+
+ /* Same as in `vec_to_quat`. */
+ const float epsilon = 1e-4f;
+ for (const int i : r_normals.index_range()) {
+ const float3 &tangent = tangents[i];
+ if (fabsf(tangent.x) + fabsf(tangent.y) < epsilon) {
+ r_normals[i] = {1.0f, 0.0f, 0.0f};
+ }
+ else {
+ r_normals[i] = float3(tangent.y, -tangent.x, 0.0f).normalized();
+ }
+ }
+}
+
+/**
+ * Rotate the last normal in the same way the tangent has been rotated.
+ */
+static float3 calculate_next_normal(const float3 &last_normal,
+ const float3 &last_tangent,
+ const float3 &current_tangent)
+{
+ if (last_tangent.is_zero() || current_tangent.is_zero()) {
+ return last_normal;
+ }
+ const float angle = angle_normalized_v3v3(last_tangent, current_tangent);
+ if (angle != 0.0) {
+ const float3 axis = float3::cross(last_tangent, current_tangent).normalized();
+ return rotate_direction_around_axis(last_normal, axis, angle);
+ }
+ return last_normal;
+}
+
+static void calculate_normals_minimum(Span<float3> tangents,
+ const bool cyclic,
+ MutableSpan<float3> r_normals)
+{
+ BLI_assert(r_normals.size() == tangents.size());
+
+ if (r_normals.is_empty()) {
+ return;
+ }
+
+ const float epsilon = 1e-4f;
+
+ /* Set initial normal. */
+ const float3 &first_tangent = tangents[0];
+ if (fabs(first_tangent.x) + fabs(first_tangent.y) < epsilon) {
+ r_normals[0] = {1.0f, 0.0f, 0.0f};
+ }
+ else {
+ r_normals[0] = float3(first_tangent.y, -first_tangent.x, 0.0f).normalized();
+ }
+
+ /* Forward normal with minimum twist along the entire spline. */
+ for (const int i : IndexRange(1, r_normals.size() - 1)) {
+ r_normals[i] = calculate_next_normal(r_normals[i - 1], tangents[i - 1], tangents[i]);
+ }
+
+ if (!cyclic) {
+ return;
+ }
+
+ /* Compute how much the first normal deviates from the normal that has been forwarded along the
+ * entire cyclic spline. */
+ const float3 uncorrected_last_normal = calculate_next_normal(
+ r_normals.last(), tangents.last(), tangents[0]);
+ float correction_angle = angle_signed_on_axis_v3v3_v3(
+ r_normals[0], uncorrected_last_normal, tangents[0]);
+ if (correction_angle > M_PI) {
+ correction_angle = correction_angle - 2 * M_PI;
+ }
+
+ /* Gradually apply correction by rotating all normals slightly. */
+ const float angle_step = correction_angle / r_normals.size();
+ for (const int i : r_normals.index_range()) {
+ const float angle = angle_step * i;
+ r_normals[i] = rotate_direction_around_axis(r_normals[i], tangents[i], angle);
}
}
@@ -234,14 +364,28 @@ Span<float3> Spline::evaluated_normals() const
const int eval_size = this->evaluated_points_size();
evaluated_normals_cache_.resize(eval_size);
- Span<float3> tangents = evaluated_tangents();
+ Span<float3> tangents = this->evaluated_tangents();
MutableSpan<float3> normals = evaluated_normals_cache_;
/* Only Z up normals are supported at the moment. */
- calculate_normals_z_up(tangents, normals);
+ switch (this->normal_mode) {
+ case ZUp: {
+ calculate_normals_z_up(tangents, normals);
+ break;
+ }
+ case Minimum: {
+ calculate_normals_minimum(tangents, is_cyclic_, normals);
+ break;
+ }
+ case Tangent: {
+ /* Tangent mode is not yet supported. */
+ calculate_normals_z_up(tangents, normals);
+ break;
+ }
+ }
/* Rotate the generated normals with the interpolated tilt data. */
- GVArray_Typed<float> tilts = this->interpolate_to_evaluated_points(this->tilts());
+ GVArray_Typed<float> tilts = this->interpolate_to_evaluated(this->tilts());
for (const int i : normals.index_range()) {
normals[i] = rotate_direction_around_axis(normals[i], tangents[i], tilts[i]);
}
@@ -380,23 +524,23 @@ void Spline::sample_length_parameters_to_index_factors(MutableSpan<float> parame
Spline::LookupResult Spline::lookup_data_from_index_factor(const float index_factor) const
{
- const int points_len = this->evaluated_points_size();
+ const int eval_size = this->evaluated_points_size();
if (is_cyclic_) {
- if (index_factor < points_len) {
+ if (index_factor < eval_size) {
const int index = std::floor(index_factor);
- const int next_index = (index < points_len - 1) ? index + 1 : 0;
+ const int next_index = (index < eval_size - 1) ? index + 1 : 0;
return LookupResult{index, next_index, index_factor - index};
}
- return LookupResult{points_len - 1, 0, 1.0f};
+ return LookupResult{eval_size - 1, 0, 1.0f};
}
- if (index_factor < points_len - 1) {
+ if (index_factor < eval_size - 1) {
const int index = std::floor(index_factor);
const int next_index = index + 1;
return LookupResult{index, next_index, index_factor - index};
}
- return LookupResult{points_len - 2, points_len - 1, 1.0f};
+ return LookupResult{eval_size - 2, eval_size - 1, 1.0f};
}
void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated) const
@@ -407,9 +551,9 @@ void Spline::bounds_min_max(float3 &min, float3 &max, const bool use_evaluated)
}
}
-GVArrayPtr Spline::interpolate_to_evaluated_points(GSpan data) const
+GVArrayPtr Spline::interpolate_to_evaluated(GSpan data) const
{
- return this->interpolate_to_evaluated_points(GVArray_For_GSpan(data));
+ return this->interpolate_to_evaluated(GVArray_For_GSpan(data));
}
/**
@@ -417,9 +561,9 @@ GVArrayPtr Spline::interpolate_to_evaluated_points(GSpan data) const
* points) to arbitrary parameters in between the evaluated points. The interpolation is quite
* simple, but this handles the cyclic and end point special cases.
*/
-void Spline::sample_based_on_index_factors(const GVArray &src,
- Span<float> index_factors,
- GMutableSpan dst) const
+void Spline::sample_with_index_factors(const GVArray &src,
+ Span<float> index_factors,
+ GMutableSpan dst) const
{
BLI_assert(src.size() == this->evaluated_points_size());
@@ -427,7 +571,7 @@ void Spline::sample_based_on_index_factors(const GVArray &src,
using T = decltype(dummy);
const GVArray_Typed<T> src_typed = src.typed<T>();
MutableSpan<T> dst_typed = dst.typed<T>();
- blender::parallel_for(dst_typed.index_range(), 1024, [&](IndexRange range) {
+ blender::threading::parallel_for(dst_typed.index_range(), 1024, [&](IndexRange range) {
for (const int i : range) {
const LookupResult interp = this->lookup_data_from_index_factor(index_factors[i]);
dst_typed[i] = blender::attribute_math::mix2(interp.factor,