2022-02-17 16:47:37 +11:00
|
|
|
/* SPDX-License-Identifier: GPL-2.0-or-later */
|
2022-02-16 11:32:37 -06:00
|
|
|
|
|
|
|
/** \file
|
|
|
|
* \ingroup bke
|
|
|
|
*/
|
|
|
|
|
2022-03-16 15:47:00 -05:00
|
|
|
#include <mutex>
|
2022-03-18 10:30:27 -05:00
|
|
|
#include <utility>
|
2022-03-16 15:47:00 -05:00
|
|
|
|
2022-02-16 11:32:37 -06:00
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
|
|
|
|
#include "BLI_bounds.hh"
|
2022-03-16 15:47:00 -05:00
|
|
|
#include "BLI_index_mask_ops.hh"
|
2022-03-29 19:44:01 -05:00
|
|
|
#include "BLI_length_parameterize.hh"
|
2022-04-15 09:54:15 -05:00
|
|
|
#include "BLI_math_rotation.hh"
|
2022-02-16 11:32:37 -06:00
|
|
|
|
|
|
|
#include "DNA_curves_types.h"
|
|
|
|
|
|
|
|
#include "BKE_attribute_math.hh"
|
|
|
|
#include "BKE_curves.hh"
|
|
|
|
|
|
|
|
namespace blender::bke {
|
|
|
|
|
|
|
|
static const std::string ATTR_POSITION = "position";
|
|
|
|
static const std::string ATTR_RADIUS = "radius";
|
2022-04-15 09:54:15 -05:00
|
|
|
static const std::string ATTR_TILT = "tilt";
|
2022-02-16 11:32:37 -06:00
|
|
|
static const std::string ATTR_CURVE_TYPE = "curve_type";
|
2022-02-28 17:20:37 -05:00
|
|
|
static const std::string ATTR_CYCLIC = "cyclic";
|
2022-03-16 15:47:00 -05:00
|
|
|
static const std::string ATTR_RESOLUTION = "resolution";
|
2022-04-09 12:46:30 -05:00
|
|
|
static const std::string ATTR_NORMAL_MODE = "normal_mode";
|
2022-03-16 15:47:00 -05:00
|
|
|
static const std::string ATTR_HANDLE_TYPE_LEFT = "handle_type_left";
|
|
|
|
static const std::string ATTR_HANDLE_TYPE_RIGHT = "handle_type_right";
|
|
|
|
static const std::string ATTR_HANDLE_POSITION_LEFT = "handle_left";
|
|
|
|
static const std::string ATTR_HANDLE_POSITION_RIGHT = "handle_right";
|
|
|
|
static const std::string ATTR_NURBS_ORDER = "nurbs_order";
|
|
|
|
static const std::string ATTR_NURBS_WEIGHT = "nurbs_weight";
|
|
|
|
static const std::string ATTR_NURBS_KNOTS_MODE = "knots_mode";
|
2022-03-21 18:54:31 +01:00
|
|
|
static const std::string ATTR_SURFACE_TRIANGLE_INDEX = "surface_triangle_index";
|
|
|
|
static const std::string ATTR_SURFACE_TRIANGLE_COORDINATE = "surface_triangle_coordinate";
|
2022-05-31 19:00:24 +02:00
|
|
|
static const std::string ATTR_SELECTION_POINT_FLOAT = ".selection_point_float";
|
|
|
|
static const std::string ATTR_SELECTION_CURVE_FLOAT = ".selection_curve_float";
|
2022-02-16 11:32:37 -06:00
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Constructors/Destructor
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
CurvesGeometry::CurvesGeometry() : CurvesGeometry(0, 0)
|
|
|
|
{
|
|
|
|
}
|
|
|
|
|
2022-05-11 09:58:39 +10:00
|
|
|
CurvesGeometry::CurvesGeometry(const int point_num, const int curve_num)
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
2022-05-11 09:58:39 +10:00
|
|
|
this->point_num = point_num;
|
|
|
|
this->curve_num = curve_num;
|
2022-02-16 11:32:37 -06:00
|
|
|
CustomData_reset(&this->point_data);
|
|
|
|
CustomData_reset(&this->curve_data);
|
|
|
|
|
|
|
|
CustomData_add_layer_named(&this->point_data,
|
|
|
|
CD_PROP_FLOAT3,
|
|
|
|
CD_DEFAULT,
|
|
|
|
nullptr,
|
2022-05-11 09:58:39 +10:00
|
|
|
this->point_num,
|
2022-02-16 11:32:37 -06:00
|
|
|
ATTR_POSITION.c_str());
|
|
|
|
|
2022-05-11 09:58:39 +10:00
|
|
|
this->curve_offsets = (int *)MEM_calloc_arrayN(this->curve_num + 1, sizeof(int), __func__);
|
2022-02-16 11:32:37 -06:00
|
|
|
|
|
|
|
this->update_customdata_pointers();
|
|
|
|
|
|
|
|
this->runtime = MEM_new<CurvesGeometryRuntime>(__func__);
|
2022-05-06 10:58:54 +02:00
|
|
|
/* Fill the type counts with the default so they're in a valid state. */
|
2022-05-11 09:58:39 +10:00
|
|
|
this->runtime->type_counts[CURVE_TYPE_CATMULL_ROM] = curve_num;
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
|
2022-02-17 09:04:58 -06:00
|
|
|
/**
|
|
|
|
* \note Expects `dst` to be initialized, since the original attributes must be freed.
|
|
|
|
*/
|
2022-02-16 11:32:37 -06:00
|
|
|
static void copy_curves_geometry(CurvesGeometry &dst, const CurvesGeometry &src)
|
|
|
|
{
|
2022-05-11 09:58:39 +10:00
|
|
|
CustomData_free(&dst.point_data, dst.point_num);
|
|
|
|
CustomData_free(&dst.curve_data, dst.curve_num);
|
|
|
|
dst.point_num = src.point_num;
|
|
|
|
dst.curve_num = src.curve_num;
|
|
|
|
CustomData_copy(&src.point_data, &dst.point_data, CD_MASK_ALL, CD_DUPLICATE, dst.point_num);
|
|
|
|
CustomData_copy(&src.curve_data, &dst.curve_data, CD_MASK_ALL, CD_DUPLICATE, dst.curve_num);
|
2022-02-16 11:32:37 -06:00
|
|
|
|
|
|
|
MEM_SAFE_FREE(dst.curve_offsets);
|
2022-05-11 09:58:39 +10:00
|
|
|
dst.curve_offsets = (int *)MEM_calloc_arrayN(dst.point_num + 1, sizeof(int), __func__);
|
2022-04-06 16:30:27 -05:00
|
|
|
dst.offsets_for_write().copy_from(src.offsets());
|
2022-02-16 11:32:37 -06:00
|
|
|
|
|
|
|
dst.tag_topology_changed();
|
|
|
|
|
2022-04-25 13:39:51 -05:00
|
|
|
/* Though type counts are a cache, they must be copied because they are calculated eagerly. */
|
|
|
|
dst.runtime->type_counts = src.runtime->type_counts;
|
|
|
|
|
2022-02-16 11:32:37 -06:00
|
|
|
dst.update_customdata_pointers();
|
|
|
|
}
|
|
|
|
|
|
|
|
CurvesGeometry::CurvesGeometry(const CurvesGeometry &other)
|
2022-05-11 09:58:39 +10:00
|
|
|
: CurvesGeometry(other.point_num, other.curve_num)
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
|
|
|
copy_curves_geometry(*this, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
CurvesGeometry &CurvesGeometry::operator=(const CurvesGeometry &other)
|
|
|
|
{
|
|
|
|
if (this != &other) {
|
|
|
|
copy_curves_geometry(*this, other);
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2022-03-11 11:34:03 -06:00
|
|
|
/* The source should be empty, but in a valid state so that using it further will work. */
|
|
|
|
static void move_curves_geometry(CurvesGeometry &dst, CurvesGeometry &src)
|
|
|
|
{
|
2022-05-11 09:58:39 +10:00
|
|
|
dst.point_num = src.point_num;
|
2022-03-11 11:34:03 -06:00
|
|
|
std::swap(dst.point_data, src.point_data);
|
2022-05-11 09:58:39 +10:00
|
|
|
CustomData_free(&src.point_data, src.point_num);
|
|
|
|
src.point_num = 0;
|
2022-03-11 11:34:03 -06:00
|
|
|
|
2022-05-11 09:58:39 +10:00
|
|
|
dst.curve_num = src.curve_num;
|
2022-04-08 15:19:14 +02:00
|
|
|
std::swap(dst.curve_data, src.curve_data);
|
2022-05-11 09:58:39 +10:00
|
|
|
CustomData_free(&src.curve_data, src.curve_num);
|
|
|
|
src.curve_num = 0;
|
2022-03-11 11:34:03 -06:00
|
|
|
|
|
|
|
std::swap(dst.curve_offsets, src.curve_offsets);
|
|
|
|
MEM_SAFE_FREE(src.curve_offsets);
|
|
|
|
|
|
|
|
std::swap(dst.runtime, src.runtime);
|
|
|
|
|
|
|
|
src.update_customdata_pointers();
|
|
|
|
dst.update_customdata_pointers();
|
|
|
|
}
|
|
|
|
|
|
|
|
CurvesGeometry::CurvesGeometry(CurvesGeometry &&other)
|
2022-05-11 09:58:39 +10:00
|
|
|
: CurvesGeometry(other.point_num, other.curve_num)
|
2022-03-11 11:34:03 -06:00
|
|
|
{
|
|
|
|
move_curves_geometry(*this, other);
|
|
|
|
}
|
|
|
|
|
|
|
|
CurvesGeometry &CurvesGeometry::operator=(CurvesGeometry &&other)
|
|
|
|
{
|
|
|
|
if (this != &other) {
|
|
|
|
move_curves_geometry(*this, other);
|
|
|
|
}
|
|
|
|
return *this;
|
|
|
|
}
|
|
|
|
|
2022-02-16 11:32:37 -06:00
|
|
|
CurvesGeometry::~CurvesGeometry()
|
|
|
|
{
|
2022-05-11 09:58:39 +10:00
|
|
|
CustomData_free(&this->point_data, this->point_num);
|
|
|
|
CustomData_free(&this->curve_data, this->curve_num);
|
2022-02-16 11:32:37 -06:00
|
|
|
MEM_SAFE_FREE(this->curve_offsets);
|
|
|
|
MEM_delete(this->runtime);
|
|
|
|
this->runtime = nullptr;
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Accessors
|
|
|
|
* \{ */
|
|
|
|
|
2022-06-01 14:38:06 +10:00
|
|
|
static int domain_num(const CurvesGeometry &curves, const eAttrDomain domain)
|
2022-03-09 22:59:39 -06:00
|
|
|
{
|
2022-03-24 20:48:08 -05:00
|
|
|
return domain == ATTR_DOMAIN_POINT ? curves.points_num() : curves.curves_num();
|
2022-03-09 22:59:39 -06:00
|
|
|
}
|
|
|
|
|
2022-06-01 14:38:06 +10:00
|
|
|
static CustomData &domain_custom_data(CurvesGeometry &curves, const eAttrDomain domain)
|
2022-03-09 22:59:39 -06:00
|
|
|
{
|
|
|
|
return domain == ATTR_DOMAIN_POINT ? curves.point_data : curves.curve_data;
|
|
|
|
}
|
|
|
|
|
2022-06-01 14:38:06 +10:00
|
|
|
static const CustomData &domain_custom_data(const CurvesGeometry &curves, const eAttrDomain domain)
|
2022-03-09 22:59:39 -06:00
|
|
|
{
|
|
|
|
return domain == ATTR_DOMAIN_POINT ? curves.point_data : curves.curve_data;
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
static VArray<T> get_varray_attribute(const CurvesGeometry &curves,
|
2022-06-01 14:38:06 +10:00
|
|
|
const eAttrDomain domain,
|
2022-03-09 22:59:39 -06:00
|
|
|
const StringRefNull name,
|
|
|
|
const T default_value)
|
|
|
|
{
|
2022-05-11 12:59:58 +10:00
|
|
|
const int num = domain_num(curves, domain);
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
|
2022-03-09 22:59:39 -06:00
|
|
|
const CustomData &custom_data = domain_custom_data(curves, domain);
|
|
|
|
|
|
|
|
const T *data = (const T *)CustomData_get_layer_named(&custom_data, type, name.c_str());
|
|
|
|
if (data != nullptr) {
|
2022-05-11 12:59:58 +10:00
|
|
|
return VArray<T>::ForSpan(Span<T>(data, num));
|
2022-03-09 22:59:39 -06:00
|
|
|
}
|
2022-05-11 12:59:58 +10:00
|
|
|
return VArray<T>::ForSingle(default_value, num);
|
2022-03-09 22:59:39 -06:00
|
|
|
}
|
|
|
|
|
2022-03-16 15:47:00 -05:00
|
|
|
template<typename T>
|
|
|
|
static Span<T> get_span_attribute(const CurvesGeometry &curves,
|
2022-06-01 14:38:06 +10:00
|
|
|
const eAttrDomain domain,
|
2022-03-16 15:47:00 -05:00
|
|
|
const StringRefNull name)
|
|
|
|
{
|
2022-05-11 12:59:58 +10:00
|
|
|
const int num = domain_num(curves, domain);
|
2022-03-16 15:47:00 -05:00
|
|
|
const CustomData &custom_data = domain_custom_data(curves, domain);
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
|
2022-03-16 15:47:00 -05:00
|
|
|
|
|
|
|
T *data = (T *)CustomData_get_layer_named(&custom_data, type, name.c_str());
|
|
|
|
if (data == nullptr) {
|
|
|
|
return {};
|
|
|
|
}
|
2022-05-11 12:59:58 +10:00
|
|
|
return {data, num};
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
2022-03-09 22:59:39 -06:00
|
|
|
template<typename T>
|
|
|
|
static MutableSpan<T> get_mutable_attribute(CurvesGeometry &curves,
|
2022-06-01 14:38:06 +10:00
|
|
|
const eAttrDomain domain,
|
2022-04-09 10:51:07 +02:00
|
|
|
const StringRefNull name,
|
|
|
|
const T default_value = T())
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
2022-05-11 12:59:58 +10:00
|
|
|
const int num = domain_num(curves, domain);
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType type = cpp_type_to_custom_data_type(CPPType::get<T>());
|
2022-03-09 22:59:39 -06:00
|
|
|
CustomData &custom_data = domain_custom_data(curves, domain);
|
|
|
|
|
|
|
|
T *data = (T *)CustomData_duplicate_referenced_layer_named(
|
2022-05-11 12:59:58 +10:00
|
|
|
&custom_data, type, name.c_str(), num);
|
2022-03-09 22:59:39 -06:00
|
|
|
if (data != nullptr) {
|
2022-05-11 12:59:58 +10:00
|
|
|
return {data, num};
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
2022-03-09 22:59:39 -06:00
|
|
|
data = (T *)CustomData_add_layer_named(
|
2022-05-11 12:59:58 +10:00
|
|
|
&custom_data, type, CD_CALLOC, nullptr, num, name.c_str());
|
|
|
|
MutableSpan<T> span = {data, num};
|
|
|
|
if (num > 0 && span.first() != default_value) {
|
2022-04-09 10:51:07 +02:00
|
|
|
span.fill(default_value);
|
|
|
|
}
|
|
|
|
return span;
|
2022-03-09 22:59:39 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
VArray<int8_t> CurvesGeometry::curve_types() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<int8_t>(
|
|
|
|
*this, ATTR_DOMAIN_CURVE, ATTR_CURVE_TYPE, CURVE_TYPE_CATMULL_ROM);
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int8_t> CurvesGeometry::curve_types_for_write()
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
2022-03-09 22:59:39 -06:00
|
|
|
return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_CURVE_TYPE);
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
|
2022-04-25 13:39:51 -05:00
|
|
|
void CurvesGeometry::fill_curve_types(const CurveType type)
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
2022-04-25 13:39:51 -05:00
|
|
|
this->curve_types_for_write().fill(type);
|
|
|
|
this->runtime->type_counts.fill(0);
|
|
|
|
this->runtime->type_counts[type] = this->curves_num();
|
|
|
|
this->tag_topology_changed();
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
2022-04-25 13:39:51 -05:00
|
|
|
void CurvesGeometry::fill_curve_types(const IndexMask selection, const CurveType type)
|
2022-03-25 09:00:30 -05:00
|
|
|
{
|
2022-05-04 13:46:41 +02:00
|
|
|
if (selection.size() == this->curves_num()) {
|
|
|
|
this->fill_curve_types(type);
|
|
|
|
return;
|
|
|
|
}
|
2022-04-25 13:39:51 -05:00
|
|
|
/* A potential performance optimization is only counting the changed indices. */
|
|
|
|
this->curve_types_for_write().fill_indices(selection, type);
|
|
|
|
this->update_curve_types();
|
|
|
|
this->tag_topology_changed();
|
|
|
|
}
|
2022-03-25 09:00:30 -05:00
|
|
|
|
2022-04-25 13:39:51 -05:00
|
|
|
std::array<int, CURVE_TYPES_NUM> calculate_type_counts(const VArray<int8_t> &types)
|
|
|
|
{
|
|
|
|
using CountsType = std::array<int, CURVE_TYPES_NUM>;
|
|
|
|
CountsType counts;
|
|
|
|
counts.fill(0);
|
2022-03-25 09:00:30 -05:00
|
|
|
|
|
|
|
if (types.is_single()) {
|
2022-04-25 13:39:51 -05:00
|
|
|
counts[types.get_internal_single()] = types.size();
|
|
|
|
return counts;
|
2022-03-25 09:00:30 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Span<int8_t> types_span = types.get_internal_span();
|
|
|
|
return threading::parallel_reduce(
|
2022-04-25 13:39:51 -05:00
|
|
|
types.index_range(),
|
2022-03-25 09:00:30 -05:00
|
|
|
2048,
|
2022-04-25 13:39:51 -05:00
|
|
|
counts,
|
2022-03-25 09:00:30 -05:00
|
|
|
[&](const IndexRange curves_range, const CountsType &init) {
|
|
|
|
CountsType result = init;
|
|
|
|
for (const int curve_index : curves_range) {
|
|
|
|
result[types_span[curve_index]]++;
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
},
|
|
|
|
[](const CountsType &a, const CountsType &b) {
|
|
|
|
CountsType result = a;
|
|
|
|
for (const int i : IndexRange(CURVE_TYPES_NUM)) {
|
|
|
|
result[i] += b[i];
|
|
|
|
}
|
|
|
|
return result;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-25 13:39:51 -05:00
|
|
|
void CurvesGeometry::update_curve_types()
|
|
|
|
{
|
|
|
|
this->runtime->type_counts = calculate_type_counts(this->curve_types());
|
|
|
|
}
|
|
|
|
|
2022-04-06 16:30:27 -05:00
|
|
|
Span<float3> CurvesGeometry::positions() const
|
|
|
|
{
|
2022-05-11 09:58:39 +10:00
|
|
|
return {(const float3 *)this->position, this->point_num};
|
2022-04-06 16:30:27 -05:00
|
|
|
}
|
|
|
|
MutableSpan<float3> CurvesGeometry::positions_for_write()
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
2022-02-21 17:06:17 -05:00
|
|
|
this->position = (float(*)[3])CustomData_duplicate_referenced_layer_named(
|
2022-05-11 09:58:39 +10:00
|
|
|
&this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str(), this->point_num);
|
|
|
|
return {(float3 *)this->position, this->point_num};
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
|
2022-04-06 16:30:27 -05:00
|
|
|
Span<int> CurvesGeometry::offsets() const
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
2022-05-11 09:58:39 +10:00
|
|
|
return {this->curve_offsets, this->curve_num + 1};
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int> CurvesGeometry::offsets_for_write()
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
2022-05-11 09:58:39 +10:00
|
|
|
return {this->curve_offsets, this->curve_num + 1};
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
|
2022-02-28 17:20:37 -05:00
|
|
|
VArray<bool> CurvesGeometry::cyclic() const
|
|
|
|
{
|
2022-03-09 22:59:39 -06:00
|
|
|
return get_varray_attribute<bool>(*this, ATTR_DOMAIN_CURVE, ATTR_CYCLIC, false);
|
2022-02-28 17:20:37 -05:00
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<bool> CurvesGeometry::cyclic_for_write()
|
2022-02-28 17:20:37 -05:00
|
|
|
{
|
2022-04-09 10:51:07 +02:00
|
|
|
return get_mutable_attribute<bool>(*this, ATTR_DOMAIN_CURVE, ATTR_CYCLIC, false);
|
2022-02-28 17:20:37 -05:00
|
|
|
}
|
|
|
|
|
2022-03-16 15:47:00 -05:00
|
|
|
VArray<int> CurvesGeometry::resolution() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_RESOLUTION, 12);
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int> CurvesGeometry::resolution_for_write()
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
2022-04-09 10:51:07 +02:00
|
|
|
return get_mutable_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_RESOLUTION, 12);
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
2022-04-09 12:46:30 -05:00
|
|
|
VArray<int8_t> CurvesGeometry::normal_mode() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE, 0);
|
|
|
|
}
|
|
|
|
MutableSpan<int8_t> CurvesGeometry::normal_mode_for_write()
|
|
|
|
{
|
|
|
|
return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NORMAL_MODE);
|
|
|
|
}
|
|
|
|
|
2022-04-15 09:54:15 -05:00
|
|
|
VArray<float> CurvesGeometry::tilt() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT, 0.0f);
|
|
|
|
}
|
|
|
|
MutableSpan<float> CurvesGeometry::tilt_for_write()
|
|
|
|
{
|
|
|
|
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_TILT);
|
|
|
|
}
|
|
|
|
|
2022-03-16 15:47:00 -05:00
|
|
|
VArray<int8_t> CurvesGeometry::handle_types_left() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0);
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int8_t> CurvesGeometry::handle_types_left_for_write()
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
2022-04-09 10:51:07 +02:00
|
|
|
return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_LEFT, 0);
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
VArray<int8_t> CurvesGeometry::handle_types_right() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_RIGHT, 0);
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int8_t> CurvesGeometry::handle_types_right_for_write()
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
2022-04-09 10:51:07 +02:00
|
|
|
return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_TYPE_RIGHT, 0);
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Span<float3> CurvesGeometry::handle_positions_left() const
|
|
|
|
{
|
|
|
|
return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_LEFT);
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<float3> CurvesGeometry::handle_positions_left_for_write()
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
|
|
|
return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_LEFT);
|
|
|
|
}
|
|
|
|
|
|
|
|
Span<float3> CurvesGeometry::handle_positions_right() const
|
|
|
|
{
|
|
|
|
return get_span_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_RIGHT);
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<float3> CurvesGeometry::handle_positions_right_for_write()
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
|
|
|
return get_mutable_attribute<float3>(*this, ATTR_DOMAIN_POINT, ATTR_HANDLE_POSITION_RIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
VArray<int8_t> CurvesGeometry::nurbs_orders() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_ORDER, 4);
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int8_t> CurvesGeometry::nurbs_orders_for_write()
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
2022-04-09 10:51:07 +02:00
|
|
|
return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_ORDER, 4);
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Span<float> CurvesGeometry::nurbs_weights() const
|
|
|
|
{
|
|
|
|
return get_span_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_NURBS_WEIGHT);
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<float> CurvesGeometry::nurbs_weights_for_write()
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
|
|
|
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_NURBS_WEIGHT);
|
|
|
|
}
|
|
|
|
|
|
|
|
VArray<int8_t> CurvesGeometry::nurbs_knots_modes() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_KNOTS_MODE, 0);
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int8_t> CurvesGeometry::nurbs_knots_modes_for_write()
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
2022-04-09 10:51:07 +02:00
|
|
|
return get_mutable_attribute<int8_t>(*this, ATTR_DOMAIN_CURVE, ATTR_NURBS_KNOTS_MODE, 0);
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
2022-03-21 18:54:31 +01:00
|
|
|
VArray<int> CurvesGeometry::surface_triangle_indices() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_INDEX, -1);
|
|
|
|
}
|
|
|
|
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int> CurvesGeometry::surface_triangle_indices_for_write()
|
2022-03-21 18:54:31 +01:00
|
|
|
{
|
2022-04-09 10:51:07 +02:00
|
|
|
return get_mutable_attribute<int>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_INDEX, -1);
|
2022-03-21 18:54:31 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
Span<float2> CurvesGeometry::surface_triangle_coords() const
|
|
|
|
{
|
|
|
|
return get_span_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_COORDINATE);
|
|
|
|
}
|
|
|
|
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<float2> CurvesGeometry::surface_triangle_coords_for_write()
|
2022-03-21 18:54:31 +01:00
|
|
|
{
|
|
|
|
return get_mutable_attribute<float2>(*this, ATTR_DOMAIN_CURVE, ATTR_SURFACE_TRIANGLE_COORDINATE);
|
|
|
|
}
|
|
|
|
|
2022-05-31 19:00:24 +02:00
|
|
|
VArray<float> CurvesGeometry::selection_point_float() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
MutableSpan<float> CurvesGeometry::selection_point_float_for_write()
|
|
|
|
{
|
|
|
|
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_POINT, ATTR_SELECTION_POINT_FLOAT, 1.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
VArray<float> CurvesGeometry::selection_curve_float() const
|
|
|
|
{
|
|
|
|
return get_varray_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f);
|
|
|
|
}
|
|
|
|
|
|
|
|
MutableSpan<float> CurvesGeometry::selection_curve_float_for_write()
|
|
|
|
{
|
|
|
|
return get_mutable_attribute<float>(*this, ATTR_DOMAIN_CURVE, ATTR_SELECTION_CURVE_FLOAT, 1.0f);
|
|
|
|
}
|
|
|
|
|
2022-03-16 15:47:00 -05:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Evaluation
|
|
|
|
* \{ */
|
|
|
|
|
2022-05-11 12:59:58 +10:00
|
|
|
template<typename CountFn> void build_offsets(MutableSpan<int> offsets, const CountFn &count_fn)
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
|
|
|
int offset = 0;
|
|
|
|
for (const int i : offsets.drop_back(1).index_range()) {
|
|
|
|
offsets[i] = offset;
|
2022-05-11 12:59:58 +10:00
|
|
|
offset += count_fn(i);
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
offsets.last() = offset;
|
|
|
|
}
|
|
|
|
|
|
|
|
static void calculate_evaluated_offsets(const CurvesGeometry &curves,
|
|
|
|
MutableSpan<int> offsets,
|
|
|
|
MutableSpan<int> bezier_evaluated_offsets)
|
|
|
|
{
|
|
|
|
VArray<int8_t> types = curves.curve_types();
|
|
|
|
VArray<int> resolution = curves.resolution();
|
|
|
|
VArray<bool> cyclic = curves.cyclic();
|
|
|
|
|
|
|
|
VArray_Span<int8_t> handle_types_left{curves.handle_types_left()};
|
|
|
|
VArray_Span<int8_t> handle_types_right{curves.handle_types_right()};
|
|
|
|
|
|
|
|
VArray<int8_t> nurbs_orders = curves.nurbs_orders();
|
|
|
|
VArray<int8_t> nurbs_knots_modes = curves.nurbs_knots_modes();
|
|
|
|
|
|
|
|
build_offsets(offsets, [&](const int curve_index) -> int {
|
2022-03-23 23:01:02 -05:00
|
|
|
const IndexRange points = curves.points_for_curve(curve_index);
|
2022-03-16 15:47:00 -05:00
|
|
|
switch (types[curve_index]) {
|
|
|
|
case CURVE_TYPE_CATMULL_ROM:
|
2022-05-11 12:59:58 +10:00
|
|
|
return curves::catmull_rom::calculate_evaluated_num(
|
2022-03-16 15:47:00 -05:00
|
|
|
points.size(), cyclic[curve_index], resolution[curve_index]);
|
|
|
|
case CURVE_TYPE_POLY:
|
|
|
|
return points.size();
|
|
|
|
case CURVE_TYPE_BEZIER:
|
|
|
|
curves::bezier::calculate_evaluated_offsets(handle_types_left.slice(points),
|
|
|
|
handle_types_right.slice(points),
|
|
|
|
cyclic[curve_index],
|
|
|
|
resolution[curve_index],
|
|
|
|
bezier_evaluated_offsets.slice(points));
|
|
|
|
return bezier_evaluated_offsets[points.last()];
|
|
|
|
case CURVE_TYPE_NURBS:
|
2022-05-11 12:59:58 +10:00
|
|
|
return curves::nurbs::calculate_evaluated_num(points.size(),
|
|
|
|
nurbs_orders[curve_index],
|
|
|
|
cyclic[curve_index],
|
|
|
|
resolution[curve_index],
|
|
|
|
KnotsMode(nurbs_knots_modes[curve_index]));
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
return 0;
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-03-24 20:58:01 -05:00
|
|
|
void CurvesGeometry::ensure_evaluated_offsets() const
|
2022-03-16 15:47:00 -05:00
|
|
|
{
|
|
|
|
if (!this->runtime->offsets_cache_dirty) {
|
2022-03-24 20:58:01 -05:00
|
|
|
return;
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* A double checked lock. */
|
|
|
|
std::scoped_lock lock{this->runtime->offsets_cache_mutex};
|
|
|
|
if (!this->runtime->offsets_cache_dirty) {
|
2022-03-24 20:58:01 -05:00
|
|
|
return;
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
threading::isolate_task([&]() {
|
2022-03-24 20:48:08 -05:00
|
|
|
this->runtime->evaluated_offsets_cache.resize(this->curves_num() + 1);
|
2022-03-16 15:47:00 -05:00
|
|
|
|
|
|
|
if (this->has_curve_with_type(CURVE_TYPE_BEZIER)) {
|
2022-03-24 20:48:08 -05:00
|
|
|
this->runtime->bezier_evaluated_offsets.resize(this->points_num());
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
else {
|
|
|
|
this->runtime->bezier_evaluated_offsets.clear_and_make_inline();
|
|
|
|
}
|
|
|
|
|
|
|
|
calculate_evaluated_offsets(
|
|
|
|
*this, this->runtime->evaluated_offsets_cache, this->runtime->bezier_evaluated_offsets);
|
|
|
|
});
|
|
|
|
|
|
|
|
this->runtime->offsets_cache_dirty = false;
|
2022-03-24 20:58:01 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Span<int> CurvesGeometry::evaluated_offsets() const
|
|
|
|
{
|
|
|
|
this->ensure_evaluated_offsets();
|
2022-03-16 15:47:00 -05:00
|
|
|
return this->runtime->evaluated_offsets_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
|
|
|
|
Vector<int64_t> &r_indices) const
|
|
|
|
{
|
2022-05-04 13:55:08 +02:00
|
|
|
return this->indices_for_curve_type(type, this->curves_range(), r_indices);
|
|
|
|
}
|
2022-03-16 15:47:00 -05:00
|
|
|
|
2022-05-04 13:55:08 +02:00
|
|
|
IndexMask CurvesGeometry::indices_for_curve_type(const CurveType type,
|
|
|
|
const IndexMask selection,
|
|
|
|
Vector<int64_t> &r_indices) const
|
|
|
|
{
|
|
|
|
if (this->curve_type_counts()[type] == this->curves_num()) {
|
|
|
|
return selection;
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
2022-05-06 10:58:54 +02:00
|
|
|
const VArray<int8_t> types = this->curve_types();
|
|
|
|
if (types.is_single()) {
|
|
|
|
return types.get_internal_single() == type ? IndexMask(this->curves_num()) : IndexMask(0);
|
|
|
|
}
|
|
|
|
Span<int8_t> types_span = types.get_internal_span();
|
2022-03-16 15:47:00 -05:00
|
|
|
return index_mask_ops::find_indices_based_on_predicate(
|
2022-05-04 13:55:08 +02:00
|
|
|
selection, 1024, r_indices, [&](const int index) { return types_span[index] == type; });
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesGeometry::ensure_nurbs_basis_cache() const
|
|
|
|
{
|
|
|
|
if (!this->runtime->nurbs_basis_cache_dirty) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A double checked lock. */
|
|
|
|
std::scoped_lock lock{this->runtime->nurbs_basis_cache_mutex};
|
|
|
|
if (!this->runtime->nurbs_basis_cache_dirty) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
threading::isolate_task([&]() {
|
|
|
|
Vector<int64_t> nurbs_indices;
|
|
|
|
const IndexMask nurbs_mask = this->indices_for_curve_type(CURVE_TYPE_NURBS, nurbs_indices);
|
|
|
|
if (nurbs_mask.is_empty()) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-24 20:48:08 -05:00
|
|
|
this->runtime->nurbs_basis_cache.resize(this->curves_num());
|
2022-03-16 15:47:00 -05:00
|
|
|
MutableSpan<curves::nurbs::BasisCache> basis_caches(this->runtime->nurbs_basis_cache);
|
|
|
|
|
|
|
|
VArray<bool> cyclic = this->cyclic();
|
|
|
|
VArray<int8_t> orders = this->nurbs_orders();
|
|
|
|
VArray<int8_t> knots_modes = this->nurbs_knots_modes();
|
|
|
|
|
|
|
|
threading::parallel_for(nurbs_mask.index_range(), 64, [&](const IndexRange range) {
|
|
|
|
for (const int curve_index : nurbs_mask.slice(range)) {
|
2022-03-23 23:01:02 -05:00
|
|
|
const IndexRange points = this->points_for_curve(curve_index);
|
|
|
|
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
2022-03-16 15:47:00 -05:00
|
|
|
|
|
|
|
const int8_t order = orders[curve_index];
|
|
|
|
const bool is_cyclic = cyclic[curve_index];
|
|
|
|
const KnotsMode mode = KnotsMode(knots_modes[curve_index]);
|
|
|
|
|
2022-05-11 12:59:58 +10:00
|
|
|
if (!curves::nurbs::check_valid_num_and_order(points.size(), order, is_cyclic, mode)) {
|
2022-05-04 10:27:46 +02:00
|
|
|
basis_caches[curve_index].invalid = true;
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-05-11 12:59:58 +10:00
|
|
|
const int knots_num = curves::nurbs::knots_num(points.size(), order, is_cyclic);
|
|
|
|
Array<float> knots(knots_num);
|
2022-03-16 15:47:00 -05:00
|
|
|
curves::nurbs::calculate_knots(points.size(), mode, order, is_cyclic, knots);
|
|
|
|
curves::nurbs::calculate_basis_cache(points.size(),
|
|
|
|
evaluated_points.size(),
|
|
|
|
order,
|
|
|
|
is_cyclic,
|
|
|
|
knots,
|
|
|
|
basis_caches[curve_index]);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
2022-03-24 22:31:50 -05:00
|
|
|
|
|
|
|
this->runtime->nurbs_basis_cache_dirty = false;
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
Span<float3> CurvesGeometry::evaluated_positions() const
|
|
|
|
{
|
|
|
|
if (!this->runtime->position_cache_dirty) {
|
2022-04-13 17:13:30 -05:00
|
|
|
return this->runtime->evaluated_positions_span;
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
/* A double checked lock. */
|
|
|
|
std::scoped_lock lock{this->runtime->position_cache_mutex};
|
|
|
|
if (!this->runtime->position_cache_dirty) {
|
2022-04-13 17:13:30 -05:00
|
|
|
return this->runtime->evaluated_positions_span;
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
|
|
|
threading::isolate_task([&]() {
|
2022-04-13 17:13:30 -05:00
|
|
|
if (this->is_single_type(CURVE_TYPE_POLY)) {
|
|
|
|
this->runtime->evaluated_positions_span = this->positions();
|
|
|
|
this->runtime->evaluated_position_cache.clear_and_make_inline();
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
2022-03-24 20:48:08 -05:00
|
|
|
this->runtime->evaluated_position_cache.resize(this->evaluated_points_num());
|
2022-03-16 15:47:00 -05:00
|
|
|
MutableSpan<float3> evaluated_positions = this->runtime->evaluated_position_cache;
|
2022-04-13 17:13:30 -05:00
|
|
|
this->runtime->evaluated_positions_span = evaluated_positions;
|
2022-03-16 15:47:00 -05:00
|
|
|
|
|
|
|
VArray<int8_t> types = this->curve_types();
|
|
|
|
VArray<bool> cyclic = this->cyclic();
|
|
|
|
VArray<int> resolution = this->resolution();
|
|
|
|
Span<float3> positions = this->positions();
|
|
|
|
|
|
|
|
Span<float3> handle_positions_left = this->handle_positions_left();
|
|
|
|
Span<float3> handle_positions_right = this->handle_positions_right();
|
|
|
|
Span<int> bezier_evaluated_offsets = this->runtime->bezier_evaluated_offsets;
|
|
|
|
|
|
|
|
VArray<int8_t> nurbs_orders = this->nurbs_orders();
|
|
|
|
Span<float> nurbs_weights = this->nurbs_weights();
|
|
|
|
|
|
|
|
this->ensure_nurbs_basis_cache();
|
|
|
|
|
|
|
|
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
|
|
|
|
for (const int curve_index : curves_range) {
|
2022-03-23 23:01:02 -05:00
|
|
|
const IndexRange points = this->points_for_curve(curve_index);
|
|
|
|
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
2022-03-16 15:47:00 -05:00
|
|
|
|
|
|
|
switch (types[curve_index]) {
|
|
|
|
case CURVE_TYPE_CATMULL_ROM:
|
|
|
|
curves::catmull_rom::interpolate_to_evaluated(
|
|
|
|
positions.slice(points),
|
|
|
|
cyclic[curve_index],
|
|
|
|
resolution[curve_index],
|
|
|
|
evaluated_positions.slice(evaluated_points));
|
|
|
|
break;
|
|
|
|
case CURVE_TYPE_POLY:
|
|
|
|
evaluated_positions.slice(evaluated_points).copy_from(positions.slice(points));
|
|
|
|
break;
|
|
|
|
case CURVE_TYPE_BEZIER:
|
|
|
|
curves::bezier::calculate_evaluated_positions(
|
|
|
|
positions.slice(points),
|
|
|
|
handle_positions_left.slice(points),
|
|
|
|
handle_positions_right.slice(points),
|
|
|
|
bezier_evaluated_offsets.slice(points),
|
|
|
|
evaluated_positions.slice(evaluated_points));
|
|
|
|
break;
|
|
|
|
case CURVE_TYPE_NURBS: {
|
|
|
|
curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
|
|
|
|
nurbs_orders[curve_index],
|
|
|
|
nurbs_weights.slice(points),
|
|
|
|
positions.slice(points),
|
|
|
|
evaluated_positions.slice(evaluated_points));
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
default:
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
2022-04-08 17:50:00 -05:00
|
|
|
this->runtime->position_cache_dirty = false;
|
2022-04-13 17:13:30 -05:00
|
|
|
return this->runtime->evaluated_positions_span;
|
2022-03-16 15:47:00 -05:00
|
|
|
}
|
|
|
|
|
2022-04-09 12:46:30 -05:00
|
|
|
Span<float3> CurvesGeometry::evaluated_tangents() const
|
|
|
|
{
|
|
|
|
if (!this->runtime->tangent_cache_dirty) {
|
|
|
|
return this->runtime->evaluated_tangent_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A double checked lock. */
|
|
|
|
std::scoped_lock lock{this->runtime->tangent_cache_mutex};
|
|
|
|
if (!this->runtime->tangent_cache_dirty) {
|
|
|
|
return this->runtime->evaluated_tangent_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
threading::isolate_task([&]() {
|
|
|
|
const Span<float3> evaluated_positions = this->evaluated_positions();
|
|
|
|
const VArray<bool> cyclic = this->cyclic();
|
|
|
|
|
|
|
|
this->runtime->evaluated_tangent_cache.resize(this->evaluated_points_num());
|
|
|
|
MutableSpan<float3> tangents = this->runtime->evaluated_tangent_cache;
|
|
|
|
|
|
|
|
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
|
|
|
|
for (const int curve_index : curves_range) {
|
|
|
|
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
|
|
|
curves::poly::calculate_tangents(evaluated_positions.slice(evaluated_points),
|
|
|
|
cyclic[curve_index],
|
|
|
|
tangents.slice(evaluated_points));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
/* Correct the first and last tangents of Bezier curves so that they align with the inner
|
|
|
|
* handles. This is a separate loop to avoid the cost when Bezier type curves are not used. */
|
|
|
|
Vector<int64_t> bezier_indices;
|
|
|
|
const IndexMask bezier_mask = this->indices_for_curve_type(CURVE_TYPE_BEZIER, bezier_indices);
|
|
|
|
if (!bezier_mask.is_empty()) {
|
|
|
|
const Span<float3> positions = this->positions();
|
|
|
|
const Span<float3> handles_left = this->handle_positions_left();
|
|
|
|
const Span<float3> handles_right = this->handle_positions_right();
|
|
|
|
|
|
|
|
threading::parallel_for(bezier_mask.index_range(), 1024, [&](IndexRange range) {
|
|
|
|
for (const int curve_index : bezier_mask.slice(range)) {
|
|
|
|
const IndexRange points = this->points_for_curve(curve_index);
|
|
|
|
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
|
|
|
|
|
|
|
if (handles_right[points.first()] != positions[points.first()]) {
|
|
|
|
tangents[evaluated_points.first()] = math::normalize(handles_right[points.first()] -
|
|
|
|
positions[points.first()]);
|
|
|
|
}
|
|
|
|
if (handles_left[points.last()] != positions[points.last()]) {
|
|
|
|
tangents[evaluated_points.last()] = math::normalize(positions[points.last()] -
|
|
|
|
handles_left[points.last()]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
this->runtime->tangent_cache_dirty = false;
|
|
|
|
return this->runtime->evaluated_tangent_cache;
|
|
|
|
}
|
|
|
|
|
2022-04-15 09:54:15 -05:00
|
|
|
static void rotate_directions_around_axes(MutableSpan<float3> directions,
|
|
|
|
const Span<float3> axes,
|
|
|
|
const Span<float> angles)
|
|
|
|
{
|
|
|
|
for (const int i : directions.index_range()) {
|
|
|
|
directions[i] = math::rotate_direction_around_axis(directions[i], axes[i], angles[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2022-04-09 12:46:30 -05:00
|
|
|
Span<float3> CurvesGeometry::evaluated_normals() const
|
|
|
|
{
|
|
|
|
if (!this->runtime->normal_cache_dirty) {
|
|
|
|
return this->runtime->evaluated_normal_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A double checked lock. */
|
|
|
|
std::scoped_lock lock{this->runtime->normal_cache_mutex};
|
|
|
|
if (!this->runtime->normal_cache_dirty) {
|
|
|
|
return this->runtime->evaluated_normal_cache;
|
|
|
|
}
|
|
|
|
|
|
|
|
threading::isolate_task([&]() {
|
|
|
|
const Span<float3> evaluated_tangents = this->evaluated_tangents();
|
|
|
|
const VArray<bool> cyclic = this->cyclic();
|
|
|
|
const VArray<int8_t> normal_mode = this->normal_mode();
|
2022-04-15 09:54:15 -05:00
|
|
|
const VArray<int8_t> types = this->curve_types();
|
|
|
|
const VArray<float> tilt = this->tilt();
|
2022-04-09 12:46:30 -05:00
|
|
|
|
|
|
|
this->runtime->evaluated_normal_cache.resize(this->evaluated_points_num());
|
|
|
|
MutableSpan<float3> evaluated_normals = this->runtime->evaluated_normal_cache;
|
|
|
|
|
|
|
|
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
|
2022-04-15 09:54:15 -05:00
|
|
|
/* Reuse a buffer for the evaluated tilts. */
|
|
|
|
Vector<float> evaluated_tilts;
|
|
|
|
|
2022-04-09 12:46:30 -05:00
|
|
|
for (const int curve_index : curves_range) {
|
|
|
|
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
|
|
|
switch (normal_mode[curve_index]) {
|
|
|
|
case NORMAL_MODE_Z_UP:
|
|
|
|
curves::poly::calculate_normals_z_up(evaluated_tangents.slice(evaluated_points),
|
|
|
|
evaluated_normals.slice(evaluated_points));
|
|
|
|
break;
|
|
|
|
case NORMAL_MODE_MINIMUM_TWIST:
|
|
|
|
curves::poly::calculate_normals_minimum(evaluated_tangents.slice(evaluated_points),
|
|
|
|
cyclic[curve_index],
|
|
|
|
evaluated_normals.slice(evaluated_points));
|
|
|
|
break;
|
|
|
|
}
|
2022-04-15 09:54:15 -05:00
|
|
|
|
|
|
|
/* If the "tilt" attribute exists, rotate the normals around the tangents by the
|
|
|
|
* evaluated angles. We can avoid copying the tilts to evaluate them for poly curves. */
|
|
|
|
if (!(tilt.is_single() && tilt.get_internal_single() == 0.0f)) {
|
|
|
|
const IndexRange points = this->points_for_curve(curve_index);
|
|
|
|
Span<float> curve_tilt = tilt.get_internal_span().slice(points);
|
|
|
|
if (types[curve_index] == CURVE_TYPE_POLY) {
|
|
|
|
rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
|
|
|
|
evaluated_tangents.slice(evaluated_points),
|
|
|
|
curve_tilt);
|
|
|
|
}
|
|
|
|
else {
|
|
|
|
evaluated_tilts.clear();
|
|
|
|
evaluated_tilts.resize(evaluated_points.size());
|
|
|
|
this->interpolate_to_evaluated(
|
|
|
|
curve_index, curve_tilt, evaluated_tilts.as_mutable_span());
|
|
|
|
rotate_directions_around_axes(evaluated_normals.slice(evaluated_points),
|
|
|
|
evaluated_tangents.slice(evaluated_points),
|
|
|
|
evaluated_tilts.as_span());
|
|
|
|
}
|
|
|
|
}
|
2022-04-09 12:46:30 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
this->runtime->normal_cache_dirty = false;
|
|
|
|
return this->runtime->evaluated_normal_cache;
|
|
|
|
}
|
|
|
|
|
2022-03-25 09:03:35 -05:00
|
|
|
void CurvesGeometry::interpolate_to_evaluated(const int curve_index,
|
|
|
|
const GSpan src,
|
|
|
|
GMutableSpan dst) const
|
|
|
|
{
|
|
|
|
BLI_assert(!this->runtime->offsets_cache_dirty);
|
|
|
|
BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
|
|
|
|
const IndexRange points = this->points_for_curve(curve_index);
|
|
|
|
BLI_assert(src.size() == points.size());
|
|
|
|
BLI_assert(dst.size() == this->evaluated_points_for_curve(curve_index).size());
|
|
|
|
switch (this->curve_types()[curve_index]) {
|
|
|
|
case CURVE_TYPE_CATMULL_ROM:
|
|
|
|
curves::catmull_rom::interpolate_to_evaluated(
|
|
|
|
src, this->cyclic()[curve_index], this->resolution()[curve_index], dst);
|
2022-03-25 09:12:31 -05:00
|
|
|
return;
|
2022-03-25 09:03:35 -05:00
|
|
|
case CURVE_TYPE_POLY:
|
|
|
|
dst.type().copy_assign_n(src.data(), dst.data(), src.size());
|
2022-03-25 09:12:31 -05:00
|
|
|
return;
|
2022-03-25 09:03:35 -05:00
|
|
|
case CURVE_TYPE_BEZIER:
|
|
|
|
curves::bezier::interpolate_to_evaluated(
|
|
|
|
src, this->runtime->bezier_evaluated_offsets.as_span().slice(points), dst);
|
2022-03-25 09:12:31 -05:00
|
|
|
return;
|
2022-03-25 09:03:35 -05:00
|
|
|
case CURVE_TYPE_NURBS:
|
|
|
|
curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
|
|
|
|
this->nurbs_orders()[curve_index],
|
|
|
|
this->nurbs_weights().slice(points),
|
|
|
|
src,
|
|
|
|
dst);
|
2022-03-25 09:12:31 -05:00
|
|
|
return;
|
2022-03-25 09:03:35 -05:00
|
|
|
}
|
2022-03-25 09:12:31 -05:00
|
|
|
BLI_assert_unreachable();
|
2022-03-25 09:03:35 -05:00
|
|
|
}
|
|
|
|
|
2022-04-15 10:14:54 -05:00
|
|
|
void CurvesGeometry::interpolate_to_evaluated(const GSpan src, GMutableSpan dst) const
|
|
|
|
{
|
|
|
|
BLI_assert(!this->runtime->offsets_cache_dirty);
|
|
|
|
BLI_assert(!this->runtime->nurbs_basis_cache_dirty);
|
|
|
|
const VArray<int8_t> types = this->curve_types();
|
|
|
|
const VArray<int> resolution = this->resolution();
|
|
|
|
const VArray<bool> cyclic = this->cyclic();
|
|
|
|
const VArray<int8_t> nurbs_orders = this->nurbs_orders();
|
|
|
|
const Span<float> nurbs_weights = this->nurbs_weights();
|
|
|
|
|
|
|
|
threading::parallel_for(this->curves_range(), 512, [&](IndexRange curves_range) {
|
|
|
|
for (const int curve_index : curves_range) {
|
|
|
|
const IndexRange points = this->points_for_curve(curve_index);
|
|
|
|
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
|
|
|
switch (types[curve_index]) {
|
|
|
|
case CURVE_TYPE_CATMULL_ROM:
|
|
|
|
curves::catmull_rom::interpolate_to_evaluated(src.slice(points),
|
|
|
|
cyclic[curve_index],
|
|
|
|
resolution[curve_index],
|
|
|
|
dst.slice(evaluated_points));
|
|
|
|
continue;
|
|
|
|
case CURVE_TYPE_POLY:
|
|
|
|
dst.slice(evaluated_points).copy_from(src.slice(points));
|
|
|
|
continue;
|
|
|
|
case CURVE_TYPE_BEZIER:
|
|
|
|
curves::bezier::interpolate_to_evaluated(
|
|
|
|
src.slice(points),
|
|
|
|
this->runtime->bezier_evaluated_offsets.as_span().slice(points),
|
|
|
|
dst.slice(evaluated_points));
|
|
|
|
continue;
|
|
|
|
case CURVE_TYPE_NURBS:
|
|
|
|
curves::nurbs::interpolate_to_evaluated(this->runtime->nurbs_basis_cache[curve_index],
|
|
|
|
nurbs_orders[curve_index],
|
|
|
|
nurbs_weights.slice(points),
|
|
|
|
src.slice(points),
|
|
|
|
dst.slice(evaluated_points));
|
|
|
|
continue;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-03-29 19:44:01 -05:00
|
|
|
void CurvesGeometry::ensure_evaluated_lengths() const
|
|
|
|
{
|
|
|
|
if (!this->runtime->length_cache_dirty) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
/* A double checked lock. */
|
|
|
|
std::scoped_lock lock{this->runtime->length_cache_mutex};
|
|
|
|
if (!this->runtime->length_cache_dirty) {
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
|
|
|
|
threading::isolate_task([&]() {
|
|
|
|
/* Use an extra length value for the final cyclic segment for a consistent size
|
|
|
|
* (see comment on #evaluated_length_cache). */
|
2022-05-11 12:59:58 +10:00
|
|
|
const int total_num = this->evaluated_points_num() + this->curves_num();
|
|
|
|
this->runtime->evaluated_length_cache.resize(total_num);
|
2022-03-29 19:44:01 -05:00
|
|
|
MutableSpan<float> evaluated_lengths = this->runtime->evaluated_length_cache;
|
|
|
|
|
|
|
|
Span<float3> evaluated_positions = this->evaluated_positions();
|
|
|
|
VArray<bool> curves_cyclic = this->cyclic();
|
|
|
|
|
|
|
|
threading::parallel_for(this->curves_range(), 128, [&](IndexRange curves_range) {
|
|
|
|
for (const int curve_index : curves_range) {
|
|
|
|
const bool cyclic = curves_cyclic[curve_index];
|
|
|
|
const IndexRange evaluated_points = this->evaluated_points_for_curve(curve_index);
|
|
|
|
const IndexRange lengths_range = this->lengths_range_for_curve(curve_index, cyclic);
|
|
|
|
length_parameterize::accumulate_lengths(evaluated_positions.slice(evaluated_points),
|
|
|
|
cyclic,
|
|
|
|
evaluated_lengths.slice(lengths_range));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
});
|
|
|
|
|
|
|
|
this->runtime->length_cache_dirty = false;
|
|
|
|
}
|
|
|
|
|
2022-03-16 15:47:00 -05:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Operations
|
|
|
|
* \{ */
|
|
|
|
|
2022-03-24 20:48:08 -05:00
|
|
|
void CurvesGeometry::resize(const int points_num, const int curves_num)
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
2022-05-11 09:58:39 +10:00
|
|
|
if (points_num != this->point_num) {
|
2022-03-24 20:48:08 -05:00
|
|
|
CustomData_realloc(&this->point_data, points_num);
|
2022-05-11 09:58:39 +10:00
|
|
|
this->point_num = points_num;
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
2022-05-11 09:58:39 +10:00
|
|
|
if (curves_num != this->curve_num) {
|
2022-03-24 20:48:08 -05:00
|
|
|
CustomData_realloc(&this->curve_data, curves_num);
|
2022-05-11 09:58:39 +10:00
|
|
|
this->curve_num = curves_num;
|
2022-03-24 20:48:08 -05:00
|
|
|
this->curve_offsets = (int *)MEM_reallocN(this->curve_offsets, sizeof(int) * (curves_num + 1));
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
this->tag_topology_changed();
|
|
|
|
this->update_customdata_pointers();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesGeometry::tag_positions_changed()
|
|
|
|
{
|
|
|
|
this->runtime->position_cache_dirty = true;
|
|
|
|
this->runtime->tangent_cache_dirty = true;
|
|
|
|
this->runtime->normal_cache_dirty = true;
|
2022-03-29 19:44:01 -05:00
|
|
|
this->runtime->length_cache_dirty = true;
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
void CurvesGeometry::tag_topology_changed()
|
|
|
|
{
|
|
|
|
this->runtime->position_cache_dirty = true;
|
|
|
|
this->runtime->tangent_cache_dirty = true;
|
|
|
|
this->runtime->normal_cache_dirty = true;
|
2022-03-16 15:47:00 -05:00
|
|
|
this->runtime->offsets_cache_dirty = true;
|
|
|
|
this->runtime->nurbs_basis_cache_dirty = true;
|
2022-03-29 19:44:01 -05:00
|
|
|
this->runtime->length_cache_dirty = true;
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
void CurvesGeometry::tag_normals_changed()
|
|
|
|
{
|
|
|
|
this->runtime->normal_cache_dirty = true;
|
|
|
|
}
|
|
|
|
|
2022-03-18 10:30:27 -05:00
|
|
|
static void translate_positions(MutableSpan<float3> positions, const float3 &translation)
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
|
|
|
threading::parallel_for(positions.index_range(), 2048, [&](const IndexRange range) {
|
|
|
|
for (float3 &position : positions.slice(range)) {
|
|
|
|
position += translation;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-03-18 10:30:27 -05:00
|
|
|
static void transform_positions(MutableSpan<float3> positions, const float4x4 &matrix)
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
|
|
|
threading::parallel_for(positions.index_range(), 1024, [&](const IndexRange range) {
|
|
|
|
for (float3 &position : positions.slice(range)) {
|
|
|
|
position = matrix * position;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-04-01 08:11:58 -05:00
|
|
|
void CurvesGeometry::calculate_bezier_auto_handles()
|
|
|
|
{
|
2022-04-06 16:30:27 -05:00
|
|
|
const VArray<int8_t> types = this->curve_types();
|
2022-04-01 08:11:58 -05:00
|
|
|
if (types.is_single() && types.get_internal_single() != CURVE_TYPE_BEZIER) {
|
|
|
|
return;
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
if (this->handle_positions_left().is_empty() || this->handle_positions_right().is_empty()) {
|
2022-04-03 12:54:42 -05:00
|
|
|
return;
|
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
const VArray<bool> cyclic = this->cyclic();
|
|
|
|
const VArray_Span<int8_t> types_left{this->handle_types_left()};
|
|
|
|
const VArray_Span<int8_t> types_right{this->handle_types_right()};
|
2022-04-01 08:11:58 -05:00
|
|
|
const Span<float3> positions = this->positions();
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<float3> positions_left = this->handle_positions_left_for_write();
|
|
|
|
MutableSpan<float3> positions_right = this->handle_positions_right_for_write();
|
2022-04-01 08:11:58 -05:00
|
|
|
|
|
|
|
threading::parallel_for(this->curves_range(), 128, [&](IndexRange range) {
|
|
|
|
for (const int i_curve : range) {
|
|
|
|
if (types[i_curve] == CURVE_TYPE_BEZIER) {
|
|
|
|
const IndexRange points = this->points_for_curve(i_curve);
|
|
|
|
curves::bezier::calculate_auto_handles(cyclic[i_curve],
|
|
|
|
types_left.slice(points),
|
|
|
|
types_right.slice(points),
|
|
|
|
positions.slice(points),
|
|
|
|
positions_left.slice(points),
|
|
|
|
positions_right.slice(points));
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
2022-03-18 10:30:27 -05:00
|
|
|
void CurvesGeometry::translate(const float3 &translation)
|
|
|
|
{
|
2022-04-06 16:30:27 -05:00
|
|
|
translate_positions(this->positions_for_write(), translation);
|
|
|
|
if (!this->handle_positions_left().is_empty()) {
|
|
|
|
translate_positions(this->handle_positions_left_for_write(), translation);
|
2022-03-18 10:30:27 -05:00
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
if (!this->handle_positions_right().is_empty()) {
|
|
|
|
translate_positions(this->handle_positions_right_for_write(), translation);
|
2022-03-18 10:30:27 -05:00
|
|
|
}
|
|
|
|
this->tag_positions_changed();
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesGeometry::transform(const float4x4 &matrix)
|
|
|
|
{
|
2022-04-06 16:30:27 -05:00
|
|
|
transform_positions(this->positions_for_write(), matrix);
|
|
|
|
if (!this->handle_positions_left().is_empty()) {
|
|
|
|
transform_positions(this->handle_positions_left_for_write(), matrix);
|
2022-03-18 10:30:27 -05:00
|
|
|
}
|
2022-04-06 16:30:27 -05:00
|
|
|
if (!this->handle_positions_right().is_empty()) {
|
|
|
|
transform_positions(this->handle_positions_right_for_write(), matrix);
|
2022-03-18 10:30:27 -05:00
|
|
|
}
|
|
|
|
this->tag_positions_changed();
|
|
|
|
}
|
|
|
|
|
2022-02-16 11:32:37 -06:00
|
|
|
static std::optional<bounds::MinMaxResult<float3>> curves_bounds(const CurvesGeometry &curves)
|
|
|
|
{
|
|
|
|
Span<float3> positions = curves.positions();
|
|
|
|
if (curves.radius) {
|
2022-03-24 20:48:08 -05:00
|
|
|
Span<float> radii{curves.radius, curves.points_num()};
|
2022-02-16 11:32:37 -06:00
|
|
|
return bounds::min_max_with_radii(positions, radii);
|
|
|
|
}
|
|
|
|
return bounds::min_max(positions);
|
|
|
|
}
|
|
|
|
|
|
|
|
bool CurvesGeometry::bounds_min_max(float3 &min, float3 &max) const
|
|
|
|
{
|
|
|
|
const std::optional<bounds::MinMaxResult<float3>> bounds = curves_bounds(*this);
|
|
|
|
if (!bounds) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
min = math::min(bounds->min, min);
|
|
|
|
max = math::max(bounds->max, max);
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesGeometry::update_customdata_pointers()
|
|
|
|
{
|
|
|
|
this->position = (float(*)[3])CustomData_get_layer_named(
|
|
|
|
&this->point_data, CD_PROP_FLOAT3, ATTR_POSITION.c_str());
|
|
|
|
this->radius = (float *)CustomData_get_layer_named(
|
|
|
|
&this->point_data, CD_PROP_FLOAT, ATTR_RADIUS.c_str());
|
|
|
|
this->curve_type = (int8_t *)CustomData_get_layer_named(
|
|
|
|
&this->point_data, CD_PROP_INT8, ATTR_CURVE_TYPE.c_str());
|
|
|
|
}
|
|
|
|
|
2022-03-10 18:06:43 +01:00
|
|
|
static void *ensure_customdata_layer(CustomData &custom_data,
|
|
|
|
const StringRefNull name,
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType data_type,
|
2022-03-10 18:06:43 +01:00
|
|
|
const int tot_elements)
|
|
|
|
{
|
|
|
|
for (const int other_layer_i : IndexRange(custom_data.totlayer)) {
|
|
|
|
CustomDataLayer &new_layer = custom_data.layers[other_layer_i];
|
|
|
|
if (name == StringRef(new_layer.name)) {
|
|
|
|
return new_layer.data;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return CustomData_add_layer_named(
|
|
|
|
&custom_data, data_type, CD_DEFAULT, nullptr, tot_elements, name.c_str());
|
|
|
|
}
|
|
|
|
|
|
|
|
static CurvesGeometry copy_with_removed_curves(const CurvesGeometry &curves,
|
|
|
|
const IndexMask curves_to_delete)
|
|
|
|
{
|
|
|
|
const Span<int> old_offsets = curves.offsets();
|
|
|
|
const Vector<IndexRange> old_curve_ranges = curves_to_delete.extract_ranges_invert(
|
|
|
|
curves.curves_range(), nullptr);
|
|
|
|
Vector<IndexRange> new_curve_ranges;
|
|
|
|
Vector<IndexRange> old_point_ranges;
|
|
|
|
Vector<IndexRange> new_point_ranges;
|
|
|
|
int new_tot_points = 0;
|
|
|
|
int new_tot_curves = 0;
|
|
|
|
for (const IndexRange &curve_range : old_curve_ranges) {
|
|
|
|
new_curve_ranges.append(IndexRange(new_tot_curves, curve_range.size()));
|
|
|
|
new_tot_curves += curve_range.size();
|
|
|
|
|
2022-03-23 23:01:02 -05:00
|
|
|
const IndexRange old_point_range = curves.points_for_curves(curve_range);
|
2022-03-10 18:06:43 +01:00
|
|
|
old_point_ranges.append(old_point_range);
|
|
|
|
new_point_ranges.append(IndexRange(new_tot_points, old_point_range.size()));
|
|
|
|
new_tot_points += old_point_range.size();
|
|
|
|
}
|
|
|
|
|
|
|
|
CurvesGeometry new_curves{new_tot_points, new_tot_curves};
|
|
|
|
|
|
|
|
threading::parallel_invoke(
|
|
|
|
/* Initialize curve offsets. */
|
|
|
|
[&]() {
|
2022-04-06 16:30:27 -05:00
|
|
|
MutableSpan<int> new_offsets = new_curves.offsets_for_write();
|
2022-03-10 18:06:43 +01:00
|
|
|
new_offsets.last() = new_tot_points;
|
|
|
|
threading::parallel_for(
|
|
|
|
old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
|
|
|
|
for (const int range_i : ranges_range) {
|
|
|
|
const IndexRange old_curve_range = old_curve_ranges[range_i];
|
|
|
|
const IndexRange new_curve_range = new_curve_ranges[range_i];
|
|
|
|
const IndexRange old_point_range = old_point_ranges[range_i];
|
|
|
|
const IndexRange new_point_range = new_point_ranges[range_i];
|
|
|
|
const int offset_shift = new_point_range.start() - old_point_range.start();
|
|
|
|
const int curves_in_range = old_curve_range.size();
|
|
|
|
threading::parallel_for(
|
|
|
|
IndexRange(curves_in_range), 512, [&](const IndexRange range) {
|
|
|
|
for (const int i : range) {
|
|
|
|
const int old_curve_i = old_curve_range[i];
|
|
|
|
const int new_curve_i = new_curve_range[i];
|
|
|
|
const int old_offset = old_offsets[old_curve_i];
|
|
|
|
const int new_offset = old_offset + offset_shift;
|
|
|
|
new_offsets[new_curve_i] = new_offset;
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
},
|
|
|
|
/* Copy over point attributes. */
|
|
|
|
[&]() {
|
|
|
|
const CustomData &old_point_data = curves.point_data;
|
|
|
|
CustomData &new_point_data = new_curves.point_data;
|
|
|
|
for (const int layer_i : IndexRange(old_point_data.totlayer)) {
|
|
|
|
const CustomDataLayer &old_layer = old_point_data.layers[layer_i];
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
|
2022-03-10 18:06:43 +01:00
|
|
|
const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
|
|
|
|
|
|
|
|
const void *src_buffer = old_layer.data;
|
|
|
|
void *dst_buffer = ensure_customdata_layer(
|
|
|
|
new_point_data, old_layer.name, data_type, new_tot_points);
|
|
|
|
|
|
|
|
threading::parallel_for(
|
|
|
|
old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
|
|
|
|
for (const int range_i : ranges_range) {
|
|
|
|
const IndexRange old_point_range = old_point_ranges[range_i];
|
|
|
|
const IndexRange new_point_range = new_point_ranges[range_i];
|
|
|
|
|
|
|
|
type.copy_construct_n(
|
|
|
|
POINTER_OFFSET(src_buffer, type.size() * old_point_range.start()),
|
|
|
|
POINTER_OFFSET(dst_buffer, type.size() * new_point_range.start()),
|
|
|
|
old_point_range.size());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
},
|
|
|
|
/* Copy over curve attributes. */
|
|
|
|
[&]() {
|
|
|
|
const CustomData &old_curve_data = curves.curve_data;
|
|
|
|
CustomData &new_curve_data = new_curves.curve_data;
|
|
|
|
for (const int layer_i : IndexRange(old_curve_data.totlayer)) {
|
|
|
|
const CustomDataLayer &old_layer = old_curve_data.layers[layer_i];
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType data_type = static_cast<eCustomDataType>(old_layer.type);
|
2022-03-10 18:06:43 +01:00
|
|
|
const CPPType &type = *bke::custom_data_type_to_cpp_type(data_type);
|
|
|
|
|
|
|
|
const void *src_buffer = old_layer.data;
|
|
|
|
void *dst_buffer = ensure_customdata_layer(
|
|
|
|
new_curve_data, old_layer.name, data_type, new_tot_points);
|
|
|
|
|
|
|
|
threading::parallel_for(
|
|
|
|
old_curve_ranges.index_range(), 128, [&](const IndexRange ranges_range) {
|
|
|
|
for (const int range_i : ranges_range) {
|
|
|
|
const IndexRange old_curve_range = old_curve_ranges[range_i];
|
|
|
|
const IndexRange new_curve_range = new_curve_ranges[range_i];
|
|
|
|
|
|
|
|
type.copy_construct_n(
|
|
|
|
POINTER_OFFSET(src_buffer, type.size() * old_curve_range.start()),
|
|
|
|
POINTER_OFFSET(dst_buffer, type.size() * new_curve_range.start()),
|
|
|
|
old_curve_range.size());
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
2022-05-04 13:55:08 +02:00
|
|
|
new_curves.update_curve_types();
|
|
|
|
|
2022-03-10 18:06:43 +01:00
|
|
|
return new_curves;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesGeometry::remove_curves(const IndexMask curves_to_delete)
|
|
|
|
{
|
|
|
|
*this = copy_with_removed_curves(*this, curves_to_delete);
|
|
|
|
}
|
|
|
|
|
2022-03-18 12:24:05 -05:00
|
|
|
template<typename T>
|
|
|
|
static void reverse_curve_point_data(const CurvesGeometry &curves,
|
|
|
|
const IndexMask curve_selection,
|
|
|
|
MutableSpan<T> data)
|
|
|
|
{
|
|
|
|
threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) {
|
|
|
|
for (const int curve_i : curve_selection.slice(range)) {
|
2022-03-23 23:01:02 -05:00
|
|
|
data.slice(curves.points_for_curve(curve_i)).reverse();
|
2022-03-18 12:24:05 -05:00
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
template<typename T>
|
|
|
|
static void reverse_swap_curve_point_data(const CurvesGeometry &curves,
|
|
|
|
const IndexMask curve_selection,
|
|
|
|
MutableSpan<T> data_a,
|
|
|
|
MutableSpan<T> data_b)
|
|
|
|
{
|
|
|
|
threading::parallel_for(curve_selection.index_range(), 256, [&](IndexRange range) {
|
|
|
|
for (const int curve_i : curve_selection.slice(range)) {
|
2022-03-23 23:01:02 -05:00
|
|
|
const IndexRange points = curves.points_for_curve(curve_i);
|
2022-03-18 12:24:05 -05:00
|
|
|
MutableSpan<T> a = data_a.slice(points);
|
|
|
|
MutableSpan<T> b = data_b.slice(points);
|
|
|
|
for (const int i : IndexRange(points.size() / 2)) {
|
|
|
|
const int end_index = points.size() - 1 - i;
|
|
|
|
std::swap(a[end_index], b[i]);
|
|
|
|
std::swap(b[end_index], a[i]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
static bool layer_matches_name_and_type(const CustomDataLayer &layer,
|
|
|
|
const StringRef name,
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType type)
|
2022-03-18 12:24:05 -05:00
|
|
|
{
|
|
|
|
if (layer.type != type) {
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return layer.name == name;
|
|
|
|
}
|
|
|
|
|
|
|
|
void CurvesGeometry::reverse_curves(const IndexMask curves_to_reverse)
|
|
|
|
{
|
2022-03-24 20:48:08 -05:00
|
|
|
CustomData_duplicate_referenced_layers(&this->point_data, this->points_num());
|
2022-03-18 12:24:05 -05:00
|
|
|
|
|
|
|
/* Collect the Bezier handle attributes while iterating through the point custom data layers;
|
|
|
|
* they need special treatment later. */
|
|
|
|
MutableSpan<float3> positions_left;
|
|
|
|
MutableSpan<float3> positions_right;
|
|
|
|
MutableSpan<int8_t> types_left;
|
|
|
|
MutableSpan<int8_t> types_right;
|
|
|
|
|
|
|
|
for (const int layer_i : IndexRange(this->point_data.totlayer)) {
|
|
|
|
CustomDataLayer &layer = this->point_data.layers[layer_i];
|
|
|
|
|
|
|
|
if (positions_left.is_empty() &&
|
|
|
|
layer_matches_name_and_type(layer, ATTR_HANDLE_POSITION_LEFT, CD_PROP_FLOAT3)) {
|
2022-03-24 20:48:08 -05:00
|
|
|
positions_left = {static_cast<float3 *>(layer.data), this->points_num()};
|
2022-03-18 12:24:05 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (positions_right.is_empty() &&
|
|
|
|
layer_matches_name_and_type(layer, ATTR_HANDLE_POSITION_RIGHT, CD_PROP_FLOAT3)) {
|
2022-03-24 20:48:08 -05:00
|
|
|
positions_right = {static_cast<float3 *>(layer.data), this->points_num()};
|
2022-03-18 12:24:05 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (types_left.is_empty() &&
|
|
|
|
layer_matches_name_and_type(layer, ATTR_HANDLE_TYPE_LEFT, CD_PROP_INT8)) {
|
2022-03-24 20:48:08 -05:00
|
|
|
types_left = {static_cast<int8_t *>(layer.data), this->points_num()};
|
2022-03-18 12:24:05 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
if (types_right.is_empty() &&
|
|
|
|
layer_matches_name_and_type(layer, ATTR_HANDLE_TYPE_RIGHT, CD_PROP_INT8)) {
|
2022-03-24 20:48:08 -05:00
|
|
|
types_right = {static_cast<int8_t *>(layer.data), this->points_num()};
|
2022-03-18 12:24:05 -05:00
|
|
|
continue;
|
|
|
|
}
|
|
|
|
|
2022-06-01 14:38:06 +10:00
|
|
|
const eCustomDataType data_type = static_cast<eCustomDataType>(layer.type);
|
2022-03-18 12:24:05 -05:00
|
|
|
attribute_math::convert_to_static_type(data_type, [&](auto dummy) {
|
|
|
|
using T = decltype(dummy);
|
|
|
|
reverse_curve_point_data<T>(
|
2022-03-24 20:48:08 -05:00
|
|
|
*this, curves_to_reverse, {static_cast<T *>(layer.data), this->points_num()});
|
2022-03-18 12:24:05 -05:00
|
|
|
});
|
|
|
|
}
|
|
|
|
|
|
|
|
/* In order to maintain the shape of Bezier curves, handle attributes must reverse, but also the
|
|
|
|
* values for the left and right must swap. Use a utility to swap and reverse at the same time,
|
|
|
|
* to avoid loading the attribute twice. Generally we can expect the right layer to exist when
|
|
|
|
* the left does, but there's no need to count on it, so check for both attributes. */
|
|
|
|
|
|
|
|
if (!positions_left.is_empty() && !positions_right.is_empty()) {
|
|
|
|
reverse_swap_curve_point_data(*this, curves_to_reverse, positions_left, positions_right);
|
|
|
|
}
|
|
|
|
if (!types_left.is_empty() && !types_right.is_empty()) {
|
|
|
|
reverse_swap_curve_point_data(*this, curves_to_reverse, types_left, types_right);
|
|
|
|
}
|
|
|
|
|
|
|
|
this->tag_topology_changed();
|
|
|
|
}
|
|
|
|
|
2022-02-16 11:32:37 -06:00
|
|
|
/** \} */
|
|
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
|
|
/** \name Domain Interpolation
|
|
|
|
* \{ */
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Mix together all of a curve's control point values.
|
|
|
|
*
|
|
|
|
* \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_curve_domain_point_to_curve_impl(const CurvesGeometry &curves,
|
|
|
|
const VArray<T> &old_values,
|
|
|
|
MutableSpan<T> r_values)
|
|
|
|
{
|
|
|
|
attribute_math::DefaultMixer<T> mixer(r_values);
|
2022-03-24 20:48:08 -05:00
|
|
|
for (const int i_curve : IndexRange(curves.curves_num())) {
|
2022-03-23 23:01:02 -05:00
|
|
|
for (const int i_point : curves.points_for_curve(i_curve)) {
|
2022-02-16 11:32:37 -06:00
|
|
|
mixer.mix_in(i_curve, old_values[i_point]);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
mixer.finalize();
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* A curve 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_curve_impl(const CurvesGeometry &curves,
|
|
|
|
const VArray<bool> &old_values,
|
|
|
|
MutableSpan<bool> r_values)
|
|
|
|
{
|
|
|
|
r_values.fill(true);
|
2022-03-24 20:48:08 -05:00
|
|
|
for (const int i_curve : IndexRange(curves.curves_num())) {
|
2022-03-23 23:01:02 -05:00
|
|
|
for (const int i_point : curves.points_for_curve(i_curve)) {
|
2022-02-16 11:32:37 -06:00
|
|
|
if (!old_values[i_point]) {
|
|
|
|
r_values[i_curve] = false;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static GVArray adapt_curve_domain_point_to_curve(const CurvesGeometry &curves,
|
|
|
|
const GVArray &varray)
|
|
|
|
{
|
|
|
|
GVArray new_varray;
|
|
|
|
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
|
|
|
|
using T = decltype(dummy);
|
|
|
|
if constexpr (!std::is_void_v<attribute_math::DefaultMixer<T>>) {
|
2022-03-24 20:48:08 -05:00
|
|
|
Array<T> values(curves.curves_num());
|
2022-02-16 11:32:37 -06:00
|
|
|
adapt_curve_domain_point_to_curve_impl<T>(curves, varray.typed<T>(), values);
|
|
|
|
new_varray = VArray<T>::ForContainer(std::move(values));
|
|
|
|
}
|
|
|
|
});
|
|
|
|
return new_varray;
|
|
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
|
|
* Copy the value from a curve to all of its points.
|
|
|
|
*
|
|
|
|
* \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_curve_domain_curve_to_point_impl(const CurvesGeometry &curves,
|
|
|
|
const VArray<T> &old_values,
|
|
|
|
MutableSpan<T> r_values)
|
|
|
|
{
|
2022-03-24 20:48:08 -05:00
|
|
|
for (const int i_curve : IndexRange(curves.curves_num())) {
|
2022-03-23 23:01:02 -05:00
|
|
|
r_values.slice(curves.points_for_curve(i_curve)).fill(old_values[i_curve]);
|
2022-02-16 11:32:37 -06:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static GVArray adapt_curve_domain_curve_to_point(const CurvesGeometry &curves,
|
|
|
|
const GVArray &varray)
|
|
|
|
{
|
|
|
|
GVArray new_varray;
|
|
|
|
attribute_math::convert_to_static_type(varray.type(), [&](auto dummy) {
|
|
|
|
using T = decltype(dummy);
|
2022-03-24 20:48:08 -05:00
|
|
|
Array<T> values(curves.points_num());
|
2022-02-16 11:32:37 -06:00
|
|
|
adapt_curve_domain_curve_to_point_impl<T>(curves, varray.typed<T>(), values);
|
|
|
|
new_varray = VArray<T>::ForContainer(std::move(values));
|
|
|
|
});
|
|
|
|
return new_varray;
|
|
|
|
}
|
|
|
|
|
2022-03-19 08:26:29 +01:00
|
|
|
GVArray CurvesGeometry::adapt_domain(const GVArray &varray,
|
2022-06-01 14:38:06 +10:00
|
|
|
const eAttrDomain from,
|
|
|
|
const eAttrDomain to) const
|
2022-02-16 11:32:37 -06:00
|
|
|
{
|
|
|
|
if (!varray) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
if (varray.is_empty()) {
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
if (from == to) {
|
|
|
|
return varray;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (from == ATTR_DOMAIN_POINT && to == ATTR_DOMAIN_CURVE) {
|
|
|
|
return adapt_curve_domain_point_to_curve(*this, varray);
|
|
|
|
}
|
|
|
|
if (from == ATTR_DOMAIN_CURVE && to == ATTR_DOMAIN_POINT) {
|
|
|
|
return adapt_curve_domain_curve_to_point(*this, varray);
|
|
|
|
}
|
|
|
|
|
|
|
|
BLI_assert_unreachable();
|
|
|
|
return {};
|
|
|
|
}
|
|
|
|
|
|
|
|
/** \} */
|
|
|
|
|
|
|
|
} // namespace blender::bke
|