25 const int *src = intervals.
data();
29 int *dst = intervals.
data() + intervals_by_curve[0].
size() - 1;
32 const IndexRange range = intervals_by_curve[curve];
34 const int width = range.
size() - 2;
35 std::copy_n(src + range.
first() + 1, width, dst);
38 (*dst) = src[intervals_by_curve[intervals_by_curve.
size() - 1].last()];
39 return {intervals.
data(), dst - intervals.
data() + 1};
48 int current_endpoint_index = 0;
49 curves_intervals_offsets.
first() = 0;
58 is_first_selected[curve] = first_range.
first() == curve_points.
start() &&
59 first_range.
size() == 1 &&
62 curve_points.
size() != 1;
63 current_endpoint_index += !is_first_selected[curve];
64 copy_intervals[curves_intervals_offsets[curve]] = curve_points.
start();
66 for (
const IndexRange range : selected_point_ranges) {
67 copy_intervals[current_endpoint_index++] = range.
first();
68 copy_intervals[current_endpoint_index++] = range.
last();
71 const int last_interval_index = current_endpoint_index - 1;
72 if (copy_intervals[last_interval_index] != curve_points.
last() ||
73 copy_intervals[last_interval_index - 1] != copy_intervals[last_interval_index])
77 copy_intervals[current_endpoint_index++] = curve_points.
last();
80 curves_intervals_offsets[curve + 1] = current_endpoint_index;
83 for (
const int curve :
curves) {
84 const IndexRange curve_points = points_by_curve[curve];
86 is_first_selected[curve] =
false;
87 copy_intervals[current_endpoint_index++] = curve_points.
first();
88 copy_intervals[current_endpoint_index++] = curve_points.
last();
89 curves_intervals_offsets[curve + 1] = current_endpoint_index;
98 new_offsets.
first() = 0;
101 for (
const int i : index_range) {
106 new_offsets[
i] = old_offsets[
i] + curves_intervals_offsets[
i] - 2 *
i;
121 return math::abs(value) < 0.00001 ? 0.0 : value;
131 const IndexMask custom_knot_curves =
curves.nurbs_custom_knot_curves(memory);
140 const int order = orders[curve];
141 const bool is_first_interval_selected = is_first_selected[curve];
148 curve_span_data[span] =
clamp_to_zero(src_curve_knots[1] - src_curve_knots[0]);
149 span_multiplicity[span] = 1;
152 const float span_value =
clamp_to_zero(src_curve_knots[
i + 1] - src_curve_knots[
i]);
153 const bool is_new =
abs(curve_span_data[span] - span_value) >= 0.00001;
155 curve_span_data[span] = span_value;
156 span_multiplicity[span]++;
161 const IndexRange curve_intervals = intervals_by_curve[curve];
163 copy_intervals.
data().slice(curve_intervals).drop_front(1).drop_back(1);
164 const int first_curve_point = copy_intervals.
data()[curve_intervals.
first()];
166 increase_span_multiplicity.
reserve(duplicated_points.
size());
167 int first_span_knot = 0;
171 const bool is_selected = bool(
i % 2) != is_first_interval_selected;
172 const int point = duplicated_points[
i] - first_curve_point;
173 while (first_span_knot + span_multiplicity[span] <= point) {
174 first_span_knot += span_multiplicity[span];
178 int multiplicity = point - first_span_knot;
179 int point_span = span;
180 std::array<int, 2> side_spans{point_span, point_span};
182 for ([[maybe_unused]]
const int i :
IndexRange(order)) {
184 if (multiplicity > span_multiplicity[point_span]) {
188 if (curve_spans[point_span] == 0.0) {
191 side_spans[side] = point_span;
193 side_spans[side] = point_span;
195 increase_span_multiplicity.
append(side_spans[is_selected]);
197 for (
const int span : increase_span_multiplicity) {
198 span_multiplicity[span]++;
204 float knot_value = src_curve_knots[knot];
205 dst_curve_knots[knot++] = knot_value;
207 for ([[maybe_unused]]
const int k :
IndexRange(span_multiplicity[span])) {
208 knot_value += curve_spans[span];
209 dst_curve_knots[knot++] = knot_value;
221 const int curves_num =
curves.curves_num();
228 Array<int> copy_interval_offsets(extruded_points.
size() * 2 + curves_num * 2);
233 Array<int> curves_intervals_offsets(curves_num + 1);
242 copy_interval_offsets,
243 curves_intervals_offsets,
252 std::array<GVArraySpan, 3> src_selection;
253 std::array<bke::GSpanAttributeWriter, 3> dst_selections;
256 for (
const int selection_i : selection_attr_names.
index_range()) {
257 const StringRef selection_name = selection_attr_names[selection_i];
260 if (!src_selection_array) {
264 src_selection[selection_i] = src_selection_array;
274 0, curves_intervals_offsets.
last());
277 for (const int curve : curves_range) {
278 const int first_index = intervals_by_curve[curve].start();
279 const int first_value = copy_intervals[first_index].start();
280 const bool first_selected = is_first_selected[curve];
282 for (const int i : intervals_by_curve[curve].drop_back(1)) {
283 const bool is_selected = bool((i - first_index) % 2) != first_selected;
284 const IndexRange src = shift_end_by(copy_intervals[i], 1);
285 const IndexRange dst = src.shift(new_offsets[curve] - first_value + i - first_index);
287 for (const int selection_i : selection_attr_names.index_range()) {
288 GMutableSpan dst_span = dst_selections[selection_i].span.slice(dst);
290 GSpan src_span = src_selection[selection_i].slice(src);
291 src_selection[selection_i].type().copy_assign_n(
292 src_span.data(), dst_span.data(), src.size());
295 fill_selection(dst_span, false);
302 for (
const int selection_i : selection_attr_names.index_range()) {
303 dst_selections[selection_i].finish();
306 if (curves.nurbs_has_custom_knots()) {
307 extrude_knots(curves, intervals_by_curve, copy_intervals, is_first_selected, new_curves);
311 copy_interval_offsets);
313 bke::MutableAttributeAccessor dst_attributes = new_curves.attributes_for_write();
321 const CPPType &type = attribute.src.type();
323 for (const int i : range) {
324 const IndexRange src = shift_end_by(compact_intervals[i], 1);
325 const IndexRange dst = src.shift(i);
327 attribute.src.slice(src).data(), attribute.dst.span.slice(dst).data(), src.size());
330 attribute.dst.finish();
337 bool extruded =
false;
360 ot->name =
"Extrude";
361 ot->description =
"Extrude selected control point(s)";
362 ot->idname =
"CURVES_OT_extrude";
Low-level operations for curves.
void DEG_id_tag_update(ID *id, unsigned int flags)
Span< T > as_span() const
MutableSpan< T > as_mutable_span()
const T & last(const int64_t n=0) const
const CPPType & type() const
constexpr int64_t first() const
constexpr IndexRange shift(int64_t n) const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
static constexpr IndexRange from_begin_size(const int64_t begin, const int64_t size)
constexpr int64_t start() const
constexpr IndexRange drop_front(int64_t n) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
constexpr T * data() const
constexpr T & first() const
constexpr IndexRange index_range() const
constexpr T & last(const int64_t n=0) const
constexpr Span slice(int64_t start, int64_t size) const
constexpr const T & first() const
constexpr int64_t size() const
constexpr IndexRange index_range() const
static VArray from_single(T value, const int64_t size)
void append(const T &value)
void reserve(const int64_t min_capacity)
GAttributeReader lookup(const StringRef attribute_id) const
MutableSpan< float > nurbs_custom_knots_for_write()
void nurbs_custom_knots_update_size()
void resize(int points_num, int curves_num)
MutableSpan< int > offsets_for_write()
OffsetIndices< int > nurbs_custom_knots_by_curve() const
void foreach_index(Fn &&fn) const
IndexRange index_range() const
void foreach_selected_point_ranges_per_curve(const IndexMask &mask, OffsetIndices< int > points_by_curve, SelectedCallback selected_fn)
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
Vector< AttributeTransferData > retrieve_attributes_for_transfer(const AttributeAccessor src_attributes, MutableAttributeAccessor dst_attributes, AttrDomainMask domain_mask, const AttributeFilter &attribute_filter={})
auto attribute_filter_from_skip_ref(const Span< StringRef > skip)
static bke::CurvesGeometry extrude_curves(const bke::CurvesGeometry &curves, const IndexMask &extruded_points)
static void calc_new_offsets(const Span< int > old_offsets, const Span< int > curves_intervals_offsets, MutableSpan< int > new_offsets)
VectorSet< Curves * > get_unique_editable_curves(const bContext &C)
static void calc_curves_extrusion(const IndexMask &selection, const OffsetIndices< int > points_by_curve, MutableSpan< int > copy_intervals, MutableSpan< int > curves_intervals_offsets, MutableSpan< bool > is_first_selected)
static float clamp_to_zero(const float value)
bool editable_curves_in_edit_mode_poll(bContext *C)
static void extrude_knots(const bke::CurvesGeometry &curves, const OffsetIndices< int > intervals_by_curve, const OffsetIndices< int > copy_intervals, const Span< bool > is_first_selected, bke::CurvesGeometry &dst_curves)
Span< StringRef > get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
static Span< int > compress_intervals(const OffsetIndices< int > intervals_by_curve, MutableSpan< int > intervals)
static wmOperatorStatus curves_extrude_exec(bContext *C, wmOperator *)
static IndexRange shift_end_by(const IndexRange &range, const int n)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, bke::AttrType create_type, StringRef attribute_name)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
void CURVES_OT_extrude(wmOperatorType *ot)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))