Blender V5.0
curves_ops.cc
Go to the documentation of this file.
1/* SPDX-FileCopyrightText: 2023 Blender Authors
2 *
3 * SPDX-License-Identifier: GPL-2.0-or-later */
4
8
9#include "BLI_listbase.h"
10#include "BLI_math_geom.h"
11#include "BLI_math_matrix.hh"
12#include "BLI_string.h"
13#include "BLI_utildefines.h"
14#include "BLI_vector_set.hh"
15
16#include "BLT_translation.hh"
17
18#include "ED_curves.hh"
19#include "ED_object.hh"
20#include "ED_screen.hh"
21#include "ED_select_utils.hh"
22#include "ED_view3d.hh"
23
24#include "WM_api.hh"
25
26#include "BKE_attribute_math.hh"
27#include "BKE_bvhutils.hh"
28#include "BKE_context.hh"
29#include "BKE_curves.hh"
30#include "BKE_customdata.hh"
31#include "BKE_geometry_set.hh"
32#include "BKE_layer.hh"
33#include "BKE_lib_id.hh"
34#include "BKE_mesh.hh"
36#include "BKE_mesh_runtime.hh"
37#include "BKE_mesh_sample.hh"
38#include "BKE_object.hh"
39#include "BKE_paint.hh"
40#include "BKE_particle.h"
41#include "BKE_report.hh"
42
43#include "DNA_mesh_types.h"
44#include "DNA_meshdata_types.h"
45#include "DNA_modifier_types.h"
46#include "DNA_object_types.h"
47#include "DNA_particle_types.h"
48#include "DNA_scene_types.h"
49
50#include "DEG_depsgraph.hh"
52
53#include "RNA_access.hh"
54#include "RNA_define.hh"
55#include "RNA_enum_types.hh"
56#include "RNA_prototypes.hh"
57
59#include "UI_resources.hh"
60
63#include "GEO_set_curve_type.hh"
65#include "GEO_transform.hh"
66
74
75namespace blender::ed::curves {
76
77bool object_has_editable_curves(const Main &bmain, const Object &object)
78{
79 if (object.type != OB_CURVES) {
80 return false;
81 }
82 if (!ELEM(object.mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) {
83 return false;
84 }
85 if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) {
86 return false;
87 }
88 return true;
89}
90
92{
93 VectorSet<Curves *> unique_curves;
94
95 const Main &bmain = *CTX_data_main(&C);
96
98 if (object && object_has_editable_curves(bmain, *object)) {
99 unique_curves.add_new(static_cast<Curves *>(object->data));
100 }
101
102 CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
103 if (object_has_editable_curves(bmain, *object)) {
104 unique_curves.add(static_cast<Curves *>(object->data));
105 }
106 }
108
109 return unique_curves;
110}
111
113 const bool check_editable,
114 const bool check_surface,
115 const bool check_edit_mode)
116{
118 if (object == nullptr || object->type != OB_CURVES) {
119 return false;
120 }
121 if (check_editable) {
123 return false;
124 }
125 }
126 if (check_surface) {
127 Curves &curves = *static_cast<Curves *>(object->data);
128 if (curves.surface == nullptr || curves.surface->type != OB_MESH) {
129 CTX_wm_operator_poll_msg_set(C, "Curves must have a mesh surface object set");
130 return false;
131 }
132 }
133 if (check_edit_mode) {
134 if ((object->mode & OB_MODE_EDIT) == 0) {
135 return false;
136 }
137 }
138 return true;
139}
140
142{
143 return curves_poll_impl(C, true, false, true);
144}
145
147{
148 return curves_poll_impl(C, true, true, false);
149}
150
152{
153 return curves_poll_impl(C, false, true, false);
154}
155
157{
158 return curves_poll_impl(C, false, false, false);
159}
160
162{
163 return curves_poll_impl(C, false, false, false);
164}
165
167{
169 return false;
170 }
171 const Curves *curves_id = static_cast<const Curves *>(CTX_data_active_object(C)->data);
173 CTX_wm_operator_poll_msg_set(C, "Only available in point selection mode");
174 return false;
175 }
176 return true;
177}
178
180
182
184 const MFace *mface,
185 const Span<int> possible_mface_indices,
186 const float3 &root_pos)
187{
188 BLI_assert(possible_mface_indices.size() >= 1);
189 if (possible_mface_indices.size() == 1) {
190 return possible_mface_indices.first();
191 }
192 /* Find the closest #MFace to #root_pos. */
193 int mface_i;
194 float best_distance_sq = FLT_MAX;
195 for (const int possible_mface_i : possible_mface_indices) {
196 const MFace &possible_mface = mface[possible_mface_i];
197 {
198 float3 point_in_triangle;
199 closest_on_tri_to_point_v3(point_in_triangle,
200 root_pos,
201 positions[possible_mface.v1],
202 positions[possible_mface.v2],
203 positions[possible_mface.v3]);
204 const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
205 if (distance_sq < best_distance_sq) {
206 best_distance_sq = distance_sq;
207 mface_i = possible_mface_i;
208 }
209 }
210 /* Optionally check the second triangle if the #MFace is a quad. */
211 if (possible_mface.v4) {
212 float3 point_in_triangle;
213 closest_on_tri_to_point_v3(point_in_triangle,
214 root_pos,
215 positions[possible_mface.v1],
216 positions[possible_mface.v3],
217 positions[possible_mface.v4]);
218 const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
219 if (distance_sq < best_distance_sq) {
220 best_distance_sq = distance_sq;
221 mface_i = possible_mface_i;
222 }
223 }
224 }
225 return mface_i;
226}
227
232 const MFace &mface,
233 const float3 &position)
234{
235 float4 mface_weights;
236 if (mface.v4) {
237 float mface_positions_su[4][3];
238 copy_v3_v3(mface_positions_su[0], positions[mface.v1]);
239 copy_v3_v3(mface_positions_su[1], positions[mface.v2]);
240 copy_v3_v3(mface_positions_su[2], positions[mface.v3]);
241 copy_v3_v3(mface_positions_su[3], positions[mface.v4]);
242 interp_weights_poly_v3(mface_weights, mface_positions_su, 4, position);
243 }
244 else {
246 mface_weights, positions[mface.v1], positions[mface.v2], positions[mface.v3], position);
247 mface_weights[3] = 0.0f;
248 }
249 return mface_weights;
250}
251
252static void try_convert_single_object(Object &curves_ob,
253 Main &bmain,
254 Scene &scene,
255 bool *r_could_not_convert_some_curves)
256{
257 if (curves_ob.type != OB_CURVES) {
258 return;
259 }
260 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
261 CurvesGeometry &curves = curves_id.geometry.wrap();
262 if (curves_id.surface == nullptr) {
263 return;
264 }
265 Object &surface_ob = *curves_id.surface;
266 if (surface_ob.type != OB_MESH) {
267 return;
268 }
269 Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
270
271 bke::BVHTreeFromMesh surface_bvh = surface_me.bvh_corner_tris();
272
273 const Span<float3> positions_cu = curves.positions();
274 const Span<int> tri_faces = surface_me.corner_tri_faces();
275
276 if (tri_faces.is_empty()) {
277 *r_could_not_convert_some_curves = true;
278 }
279
280 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
281 IndexMaskMemory memory;
282 const IndexMask multi_point_curves = IndexMask::from_predicate(
283 curves.curves_range(), GrainSize(4096), memory, [&](const int curve_i) {
284 return points_by_curve[curve_i].size() > 1;
285 });
286
287 const int hair_num = multi_point_curves.size();
288
289 if (hair_num == 0) {
290 return;
291 }
292
293 ParticleSystem *particle_system = nullptr;
294 LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) {
295 if (STREQ(psys->name, curves_ob.id.name + 2)) {
296 particle_system = psys;
297 break;
298 }
299 }
300 if (particle_system == nullptr) {
301 ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>(
302 object_add_particle_system(&bmain, &scene, &surface_ob, curves_ob.id.name + 2));
303 particle_system = psmd.psys;
304 particle_system->part->draw_step = 3;
305 }
306
307 ParticleSettings &settings = *particle_system->part;
308
309 psys_free_particles(particle_system);
310 settings.type = PART_HAIR;
311 settings.totpart = 0;
312 psys_changed_type(&surface_ob, particle_system);
313
315 hair_num};
316
317 /* The old hair system still uses #MFace, so make sure those are available on the mesh. */
318 BKE_mesh_tessface_calc(&surface_me);
319
320 /* Prepare utility data structure to map hair roots to #MFace's. */
321 const Span<int> mface_to_poly_map{
322 static_cast<const int *>(CustomData_get_layer(&surface_me.fdata_legacy, CD_ORIGINDEX)),
323 surface_me.totface_legacy};
324 Array<Vector<int>> poly_to_mface_map(surface_me.faces_num);
325 for (const int mface_i : mface_to_poly_map.index_range()) {
326 const int face_i = mface_to_poly_map[mface_i];
327 poly_to_mface_map[face_i].append(mface_i);
328 }
329
330 /* Prepare transformation matrices. */
331 const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
332
333 const MFace *mfaces = (const MFace *)CustomData_get_layer(&surface_me.fdata_legacy, CD_MFACE);
334 const Span<float3> positions = surface_me.vert_positions();
335
336 multi_point_curves.foreach_index([&](const int curve_i, const int new_hair_i) {
337 const IndexRange points = points_by_curve[curve_i];
338
339 const float3 &root_pos_cu = positions_cu[points.first()];
340 const float3 root_pos_su = math::transform_point(transforms.curves_to_surface, root_pos_cu);
341
342 BVHTreeNearest nearest;
343 nearest.dist_sq = FLT_MAX;
345 surface_bvh.tree, root_pos_su, &nearest, surface_bvh.nearest_callback, &surface_bvh);
346 BLI_assert(nearest.index >= 0);
347
348 const int tri_i = nearest.index;
349 const int face_i = tri_faces[tri_i];
350
351 const int mface_i = find_mface_for_root_position(
352 positions, mfaces, poly_to_mface_map[face_i], root_pos_su);
353 const MFace &mface = mfaces[mface_i];
354
355 const float4 mface_weights = compute_mface_weights_for_position(positions, mface, root_pos_su);
356
357 ParticleData &particle = particles[new_hair_i];
358 const int num_keys = points.size();
359 MutableSpan<HairKey> hair_keys{MEM_calloc_arrayN<HairKey>(num_keys, __func__), num_keys};
360
361 particle.hair = hair_keys.data();
362 particle.totkey = hair_keys.size();
363 copy_v4_v4(particle.fuv, mface_weights);
364 particle.num = mface_i;
365 /* Not sure if there is a better way to initialize this. */
366 particle.num_dmcache = DMCACHE_NOTFOUND;
367
368 float4x4 hair_to_surface_mat;
370 &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.ptr());
371 /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */
372 hair_to_surface_mat.location() = root_pos_su;
373 const float4x4 surface_to_hair_mat = math::invert(hair_to_surface_mat);
374
375 for (const int key_i : hair_keys.index_range()) {
376 const float3 &key_pos_cu = positions_cu[points[key_i]];
377 const float3 key_pos_su = math::transform_point(transforms.curves_to_surface, key_pos_cu);
378 const float3 key_pos_ha = math::transform_point(surface_to_hair_mat, key_pos_su);
379
380 HairKey &key = hair_keys[key_i];
381 copy_v3_v3(key.co, key_pos_ha);
382 const float key_fac = key_i / float(hair_keys.size() - 1);
383 key.time = 100.0f * key_fac;
384 key.weight = 1.0f - key_fac;
385 }
386 });
387
388 particle_system->particles = particles.data();
389 particle_system->totpart = particles.size();
390 particle_system->flag |= PSYS_EDITED;
391 particle_system->recalc |= ID_RECALC_PSYS_RESET;
392
395}
396
398{
399 Main &bmain = *CTX_data_main(C);
400 Scene &scene = *CTX_data_scene(C);
401
402 bool could_not_convert_some_curves = false;
403
404 Object &active_object = *CTX_data_active_object(C);
405 try_convert_single_object(active_object, bmain, scene, &could_not_convert_some_curves);
406
407 CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
408 if (curves_ob != &active_object) {
409 try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves);
410 }
411 }
413
414 if (could_not_convert_some_curves) {
416 RPT_INFO,
417 "Some curves could not be converted because they were not attached to the surface");
418 }
419
421
422 return OPERATOR_FINISHED;
423}
424
425} // namespace convert_to_particle_system
426
428{
429 ot->name = "Convert Curves to Particle System";
430 ot->idname = "CURVES_OT_convert_to_particle_system";
431 ot->description = "Add a new or update an existing hair particle system on the surface object";
432
435
437}
438
440
442{
443 ParticleSettings &settings = *psys.part;
444 if (psys.part->type != PART_HAIR) {
445 return {};
446 }
447
448 const bool transfer_parents = (settings.draw & PART_DRAW_PARENT) || settings.childtype == 0;
449
450 const Span<ParticleCacheKey *> parents_cache{psys.pathcache, psys.totcached};
451 const Span<ParticleCacheKey *> children_cache{psys.childcache, psys.totchildcache};
452
453 int points_num = 0;
454 Vector<int> curve_offsets;
455 Vector<int> parents_to_transfer;
456 Vector<int> children_to_transfer;
457 if (transfer_parents) {
458 for (const int parent_i : parents_cache.index_range()) {
459 const int segments = parents_cache[parent_i]->segments;
460 if (segments <= 0) {
461 continue;
462 }
463 parents_to_transfer.append(parent_i);
464 curve_offsets.append(points_num);
465 points_num += segments + 1;
466 }
467 }
468 for (const int child_i : children_cache.index_range()) {
469 const int segments = children_cache[child_i]->segments;
470 if (segments <= 0) {
471 continue;
472 }
473 children_to_transfer.append(child_i);
474 curve_offsets.append(points_num);
475 points_num += segments + 1;
476 }
477 const int curves_num = parents_to_transfer.size() + children_to_transfer.size();
478 curve_offsets.append(points_num);
479 BLI_assert(curve_offsets.size() == curves_num + 1);
480 bke::CurvesGeometry curves(points_num, curves_num);
481 curves.offsets_for_write().copy_from(curve_offsets);
482
483 const float4x4 &object_to_world_mat = object.object_to_world();
484 const float4x4 world_to_object_mat = math::invert(object_to_world_mat);
485
486 MutableSpan<float3> positions = curves.positions_for_write();
487 const OffsetIndices points_by_curve = curves.points_by_curve();
488
489 const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache,
490 const Span<int> indices_to_transfer,
491 const int curve_index_offset) {
492 threading::parallel_for(indices_to_transfer.index_range(), 256, [&](const IndexRange range) {
493 for (const int i : range) {
494 const int hair_i = indices_to_transfer[i];
495 const int curve_i = i + curve_index_offset;
496 const IndexRange points = points_by_curve[curve_i];
497 const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()};
498 for (const int key_i : keys.index_range()) {
499 const float3 key_pos_wo = keys[key_i].co;
500 positions[points[key_i]] = math::transform_point(world_to_object_mat, key_pos_wo);
501 }
502 }
503 });
504 };
505
506 if (transfer_parents) {
507 copy_hair_to_curves(parents_cache, parents_to_transfer, 0);
508 }
509 copy_hair_to_curves(children_cache, children_to_transfer, parents_to_transfer.size());
510
511 curves.update_curve_types();
512 curves.tag_topology_changed();
513 return curves;
514}
515
517{
518 Main &bmain = *CTX_data_main(C);
519 Scene &scene = *CTX_data_scene(C);
520 ViewLayer &view_layer = *CTX_data_view_layer(C);
522 Object *ob_from_orig = object::context_active_object(C);
523 ParticleSystem *psys_orig = static_cast<ParticleSystem *>(
524 CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data);
525 if (psys_orig == nullptr) {
526 psys_orig = psys_get_current(ob_from_orig);
527 }
528 if (psys_orig == nullptr) {
529 return OPERATOR_CANCELLED;
530 }
531 Object *ob_from_eval = DEG_get_evaluated(&depsgraph, ob_from_orig);
532 ParticleSystem *psys_eval = nullptr;
533 LISTBASE_FOREACH (ModifierData *, md, &ob_from_eval->modifiers) {
534 if (md->type != eModifierType_ParticleSystem) {
535 continue;
536 }
537 ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(md);
538 if (!STREQ(psmd->psys->name, psys_orig->name)) {
539 continue;
540 }
541 psys_eval = psmd->psys;
542 }
543
544 Object *ob_new = BKE_object_add(&bmain, &scene, &view_layer, OB_CURVES, psys_eval->name);
545 Curves *curves_id = static_cast<Curves *>(ob_new->data);
546 BKE_object_apply_mat4(ob_new, ob_from_orig->object_to_world().ptr(), true, false);
547 curves_id->geometry.wrap() = particles_to_curves(*ob_from_eval, *psys_eval);
548
551
552 return OPERATOR_FINISHED;
553}
554
559
560} // namespace convert_from_particle_system
561
563{
564 ot->name = "Convert Particle System to Curves";
565 ot->idname = "CURVES_OT_convert_from_particle_system";
566 ot->description = "Add a new curves object based on the current state of the particle system";
567
570
572}
573
575
576enum class AttachMode {
579};
580
582 const Object &surface_ob,
583 const AttachMode attach_mode,
584 bool *r_invalid_uvs,
585 bool *r_missing_uvs)
586{
587 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
588 CurvesGeometry &curves = curves_id.geometry.wrap();
589
590 const Mesh &surface_mesh = *static_cast<const Mesh *>(surface_ob.data);
591 const Span<float3> surface_positions = surface_mesh.vert_positions();
592 const Span<int> corner_verts = surface_mesh.corner_verts();
593 const Span<int3> surface_corner_tris = surface_mesh.corner_tris();
594 VArraySpan<float2> surface_uv_map;
595 if (curves_id.surface_uv_map != nullptr) {
596 const bke::AttributeAccessor surface_attributes = surface_mesh.attributes();
597 surface_uv_map = *surface_attributes.lookup<float2>(curves_id.surface_uv_map,
599 }
600
601 const OffsetIndices points_by_curve = curves.points_by_curve();
602 MutableSpan<float3> positions_cu = curves.positions_for_write();
603 MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
604
605 const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
606
607 switch (attach_mode) {
608 case AttachMode::Nearest: {
609 bke::BVHTreeFromMesh surface_bvh = surface_mesh.bvh_corner_tris();
610
611 threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
612 for (const int curve_i : curves_range) {
613 const IndexRange points = points_by_curve[curve_i];
614 const int first_point_i = points.first();
615 const float3 old_first_point_pos_cu = positions_cu[first_point_i];
616 const float3 old_first_point_pos_su = math::transform_point(transforms.curves_to_surface,
617 old_first_point_pos_cu);
618
619 BVHTreeNearest nearest;
620 nearest.index = -1;
621 nearest.dist_sq = FLT_MAX;
622 BLI_bvhtree_find_nearest(surface_bvh.tree,
623 old_first_point_pos_su,
624 &nearest,
625 surface_bvh.nearest_callback,
626 &surface_bvh);
627 const int tri_index = nearest.index;
628 if (tri_index == -1) {
629 continue;
630 }
631
632 const float3 new_first_point_pos_su = nearest.co;
633 const float3 new_first_point_pos_cu = math::transform_point(transforms.surface_to_curves,
634 new_first_point_pos_su);
635 const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
636
637 for (float3 &pos_cu : positions_cu.slice(points)) {
638 pos_cu += pos_diff_cu;
639 }
640
641 if (!surface_uv_map.is_empty()) {
642 const int3 &tri = surface_corner_tris[tri_index];
643 const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
644 surface_positions, corner_verts, tri, new_first_point_pos_su);
645 const float2 uv = bke::mesh_surface_sample::sample_corner_attribute_with_bary_coords(
646 bary_coords, tri, surface_uv_map);
647 surface_uv_coords[curve_i] = uv;
648 }
649 }
650 });
651 break;
652 }
653 case AttachMode::Deform: {
654 if (surface_uv_map.is_empty()) {
655 *r_missing_uvs = true;
656 break;
657 }
659 ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_corner_tris};
660
661 threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
662 for (const int curve_i : curves_range) {
663 const IndexRange points = points_by_curve[curve_i];
664 const int first_point_i = points.first();
665 const float3 old_first_point_pos_cu = positions_cu[first_point_i];
666
667 const float2 uv = surface_uv_coords[curve_i];
668 ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
669 if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
670 *r_invalid_uvs = true;
671 continue;
672 }
673
674 const int3 &tri = surface_corner_tris[lookup_result.tri_index];
675 const float3 &bary_coords = lookup_result.bary_weights;
676
677 const float3 &p0_su = surface_positions[corner_verts[tri[0]]];
678 const float3 &p1_su = surface_positions[corner_verts[tri[1]]];
679 const float3 &p2_su = surface_positions[corner_verts[tri[2]]];
680
681 float3 new_first_point_pos_su;
682 interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
683 const float3 new_first_point_pos_cu = math::transform_point(transforms.surface_to_curves,
684 new_first_point_pos_su);
685
686 const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
687 for (float3 &pos_cu : positions_cu.slice(points)) {
688 pos_cu += pos_diff_cu;
689 }
690 }
691 });
692 break;
693 }
694 }
695
696 curves.tag_positions_changed();
698}
699
701{
702 const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode"));
703
704 bool found_invalid_uvs = false;
705 bool found_missing_uvs = false;
706
707 CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
708 if (curves_ob->type != OB_CURVES) {
709 continue;
710 }
711 Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
712 if (curves_id.surface == nullptr) {
713 continue;
714 }
715 if (curves_id.surface->type != OB_MESH) {
716 continue;
717 }
719 *curves_ob, *curves_id.surface, attach_mode, &found_invalid_uvs, &found_missing_uvs);
720 }
722
723 if (found_missing_uvs) {
725 RPT_ERROR,
726 "Curves do not have attachment information that can be used for deformation");
727 }
728 if (found_invalid_uvs) {
729 BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
730 }
731
732 /* Refresh the entire window to also clear eventual modifier and nodes editor warnings. */
734
735 return OPERATOR_FINISHED;
736}
737
738} // namespace snap_curves_to_surface
739
741{
742 using namespace snap_curves_to_surface;
743
744 ot->name = "Snap Curves to Surface";
745 ot->idname = "CURVES_OT_snap_curves_to_surface";
746 ot->description = "Move curves so that the first point is exactly on the surface mesh";
747
749 ot->exec = snap_curves_to_surface_exec;
750
752
753 static const EnumPropertyItem attach_mode_items[] = {
754 {int(AttachMode::Nearest),
755 "NEAREST",
756 0,
757 "Nearest",
758 "Find the closest point on the surface for the root point of every curve and move the root "
759 "there"},
760 {int(AttachMode::Deform),
761 "DEFORM",
762 0,
763 "Deform",
764 "Re-attach curves to a deformed surface using the existing attachment information. This "
765 "only works when the topology of the surface mesh has not changed"},
766 {0, nullptr, 0, nullptr, nullptr},
767 };
768
769 RNA_def_enum(ot->srna,
770 "attach_mode",
771 attach_mode_items,
772 int(AttachMode::Nearest),
773 "Attach Mode",
774 "How to find the point on the surface to attach to");
775}
776
778
780{
781 const bke::AttrDomain domain = bke::AttrDomain(RNA_enum_get(op->ptr, "domain"));
782
783 for (Curves *curves_id : get_unique_editable_curves(*C)) {
784 if (bke::AttrDomain(curves_id->selection_domain) == domain) {
785 continue;
786 }
787
788 curves_id->selection_domain = char(domain);
789
790 CurvesGeometry &curves = curves_id->geometry.wrap();
791 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
792 if (curves.is_empty()) {
793 continue;
794 }
795
796 /* Adding and removing attributes with the C++ API doesn't affect the active attribute index.
797 * In order to make the active attribute consistent before and after the change, save the name
798 * and reset the active item afterwards.
799 *
800 * This would be unnecessary if the active attribute were stored as a string on the ID. */
801 AttributeOwner owner = AttributeOwner::from_id(&curves_id->id);
802 const std::string active_attribute = BKE_attributes_active_name_get(owner).value_or("");
803 for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
804 if (const GVArray src = *attributes.lookup(selection_name, domain)) {
805 const CPPType &type = src.type();
806 void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size, __func__);
807 src.materialize(dst);
808
809 attributes.remove(selection_name);
810 if (!attributes.add(selection_name,
811 domain,
814 {
815 MEM_freeN(dst);
816 }
817 }
818 }
819 if (!active_attribute.empty()) {
820 BKE_attributes_active_set(owner, active_attribute);
821 }
822
823 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
824 * attribute for now. */
825 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
826 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
827 }
828
830
831 return OPERATOR_FINISHED;
832}
833
834} // namespace set_selection_domain
835
837{
838 PropertyRNA *prop;
839
840 ot->name = "Set Select Mode";
841 ot->idname = __func__;
842 ot->description = "Change the mode used for selection masking in curves sculpt mode";
843
845 ot->poll = editable_curves_poll;
846
848
849 ot->prop = prop = RNA_def_enum(
850 ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", "");
852}
853
854static bool has_anything_selected(const Span<Curves *> curves_ids)
855{
856 return std::any_of(curves_ids.begin(), curves_ids.end(), [](const Curves *curves_id) {
857 return has_anything_selected(curves_id->geometry.wrap());
858 });
859}
860
862{
863 int action = RNA_enum_get(op->ptr, "action");
864
866
867 if (action == SEL_TOGGLE) {
868 action = has_anything_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
869 }
870
871 for (Curves *curves_id : unique_curves) {
872 /* (De)select all the curves. */
873 select_all(curves_id->geometry.wrap(), bke::AttrDomain(curves_id->selection_domain), action);
874
875 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
876 * attribute for now. */
877 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
878 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
879 }
880
881 return OPERATOR_FINISHED;
882}
883
885{
886 ot->name = "(De)select All";
887 ot->idname = "CURVES_OT_select_all";
888 ot->description = "(De)select all control points";
889
890 ot->exec = select_all_exec;
891 ot->poll = editable_curves_poll;
892
894
896}
897
899{
901
902 const int seed = RNA_int_get(op->ptr, "seed");
903 const float probability = RNA_float_get(op->ptr, "probability");
904
905 for (Curves *curves_id : unique_curves) {
906 CurvesGeometry &curves = curves_id->geometry.wrap();
907 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id->selection_domain);
908 const int domain_size = curves.attributes().domain_size(selection_domain);
909
910 IndexMaskMemory memory;
911 const IndexMask inv_random_elements = random_mask(domain_size, seed, probability, memory)
912 .complement(IndexRange(domain_size), memory);
913
914 const bool was_anything_selected = has_anything_selected(curves);
916 curves, selection_domain, bke::AttrType::Bool);
917 if (!was_anything_selected) {
919 }
920
921 curves::fill_selection_false(selection.span, inv_random_elements);
922 selection.finish();
923
924 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
925 * attribute for now. */
926 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
927 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
928 }
929 return OPERATOR_FINISHED;
930}
931
932static void select_random_ui(bContext * /*C*/, wmOperator *op)
933{
934 uiLayout *layout = op->layout;
935
936 layout->prop(op->ptr, "seed", UI_ITEM_NONE, std::nullopt, ICON_NONE);
937 layout->prop(op->ptr, "probability", UI_ITEM_R_SLIDER, IFACE_("Probability"), ICON_NONE);
938}
939
941{
942 ot->name = "Select Random";
943 ot->idname = __func__;
944 ot->description = "Randomizes existing selection or create new random selection";
945
946 ot->exec = select_random_exec;
948 ot->ui = select_random_ui;
949
951
952 RNA_def_int(ot->srna,
953 "seed",
954 0,
955 INT32_MIN,
956 INT32_MAX,
957 "Seed",
958 "Source of randomness",
959 INT32_MIN,
960 INT32_MAX);
961 RNA_def_float(ot->srna,
962 "probability",
963 0.5f,
964 0.0f,
965 1.0f,
966 "Probability",
967 "Chance of every point or curve being included in the selection",
968 0.0f,
969 1.0f);
970}
971
973{
975 const int amount_start = RNA_int_get(op->ptr, "amount_start");
976 const int amount_end = RNA_int_get(op->ptr, "amount_end");
977
978 for (Curves *curves_id : unique_curves) {
979 CurvesGeometry &curves = curves_id->geometry.wrap();
980
981 IndexMaskMemory memory;
982 const IndexMask inverted_end_points_mask = end_points(
983 curves, amount_start, amount_end, true, memory);
984
985 const bool was_anything_selected = has_anything_selected(curves);
988 if (!was_anything_selected) {
989 fill_selection_true(selection.span);
990 }
991
992 if (selection.span.type().is<bool>()) {
993 index_mask::masked_fill(selection.span.typed<bool>(), false, inverted_end_points_mask);
994 }
995 if (selection.span.type().is<float>()) {
996 index_mask::masked_fill(selection.span.typed<float>(), 0.0f, inverted_end_points_mask);
997 }
998 selection.finish();
999
1000 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
1001 * attribute for now. */
1002 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1003 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1004 }
1005
1006 return OPERATOR_FINISHED;
1007}
1008
1009static void select_ends_ui(bContext * /*C*/, wmOperator *op)
1010{
1011 uiLayout *layout = op->layout;
1012
1013 layout->use_property_split_set(true);
1014
1015 uiLayout *col = &layout->column(true);
1016 col->use_property_decorate_set(false);
1017 col->prop(op->ptr, "amount_start", UI_ITEM_NONE, IFACE_("Amount Start"), ICON_NONE);
1018 col->prop(op->ptr, "amount_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
1019}
1020
1022{
1023 ot->name = "Select Ends";
1024 ot->idname = __func__;
1025 ot->description = "Select end points of curves";
1026
1027 ot->exec = select_ends_exec;
1028 ot->ui = select_ends_ui;
1030
1031 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1032
1033 RNA_def_int(ot->srna,
1034 "amount_start",
1035 0,
1036 0,
1037 INT32_MAX,
1038 "Amount Front",
1039 "Number of points to select from the front",
1040 0,
1041 INT32_MAX);
1042 RNA_def_int(ot->srna,
1043 "amount_end",
1044 1,
1045 0,
1046 INT32_MAX,
1047 "Amount Back",
1048 "Number of points to select from the back",
1049 0,
1050 INT32_MAX);
1051}
1052
1054{
1056 for (Curves *curves_id : unique_curves) {
1057 CurvesGeometry &curves = curves_id->geometry.wrap();
1059 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
1060 * attribute for now. */
1061 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1062 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1063 }
1064
1065 return OPERATOR_FINISHED;
1066}
1067
1069{
1070 ot->name = "Select Linked";
1071 ot->idname = __func__;
1072 ot->description = "Select all points in curves with any point selection";
1073
1074 ot->exec = select_linked_exec;
1076
1077 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1078}
1079
1081{
1083 for (Curves *curves_id : unique_curves) {
1084 CurvesGeometry &curves = curves_id->geometry.wrap();
1085 select_adjacent(curves, false);
1086 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
1087 * attribute for now. */
1088 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1089 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1090 }
1091
1092 return OPERATOR_FINISHED;
1093}
1094
1096{
1097 ot->name = "Select More";
1098 ot->idname = __func__;
1099 ot->description = "Grow the selection by one point";
1100
1101 ot->exec = select_more_exec;
1103
1104 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1105}
1106
1108{
1110 for (Curves *curves_id : unique_curves) {
1111 CurvesGeometry &curves = curves_id->geometry.wrap();
1112 select_adjacent(curves, true);
1113 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
1114 * attribute for now. */
1115 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1116 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1117 }
1118
1119 return OPERATOR_FINISHED;
1120}
1121
1123{
1124 ot->name = "Select Less";
1125 ot->idname = __func__;
1126 ot->description = "Shrink the selection by one point";
1127
1128 ot->exec = select_less_exec;
1130
1131 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1132}
1133
1134namespace split {
1135
1137{
1138 View3D *v3d = CTX_wm_view3d(C);
1140 for (Curves *curves_id : unique_curves) {
1141 CurvesGeometry &curves = curves_id->geometry.wrap();
1142 IndexMaskMemory memory;
1143 const IndexMask points_to_split = retrieve_all_selected_points(
1144 curves, v3d->overlay.handle_display, memory);
1145 if (points_to_split.is_empty()) {
1146 continue;
1147 }
1148 curves = split_points(curves, points_to_split);
1149
1150 curves.calculate_bezier_auto_handles();
1151
1152 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1153 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1154 }
1155
1156 return OPERATOR_FINISHED;
1157}
1158
1159} // namespace split
1160
1162{
1163 ot->name = "Split";
1164 ot->idname = __func__;
1165 ot->description = "Split selected points";
1166
1167 ot->exec = split::split_exec;
1169
1170 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1171}
1172
1173namespace surface_set {
1174
1176{
1177 const Object *object = CTX_data_active_object(C);
1178 if (object == nullptr) {
1179 return false;
1180 }
1181 if (object->type != OB_MESH) {
1182 return false;
1183 }
1184 return true;
1185}
1186
1188{
1189 Main *bmain = CTX_data_main(C);
1190 Scene *scene = CTX_data_scene(C);
1191
1192 Object &new_surface_ob = *CTX_data_active_object(C);
1193
1194 Mesh &new_surface_mesh = *static_cast<Mesh *>(new_surface_ob.data);
1195 const char *new_uv_map_name = CustomData_get_active_layer_name(&new_surface_mesh.corner_data,
1197
1198 CTX_DATA_BEGIN (C, Object *, selected_ob, selected_objects) {
1199 if (selected_ob->type != OB_CURVES) {
1200 continue;
1201 }
1202 Object &curves_ob = *selected_ob;
1203 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
1204
1205 MEM_SAFE_FREE(curves_id.surface_uv_map);
1206 if (new_uv_map_name != nullptr) {
1207 curves_id.surface_uv_map = BLI_strdup(new_uv_map_name);
1208 }
1209
1210 bool missing_uvs;
1211 bool invalid_uvs;
1213 curves_ob,
1214 new_surface_ob,
1216 &invalid_uvs,
1217 &missing_uvs);
1218
1219 /* Add deformation modifier if necessary. */
1221
1222 curves_id.surface = &new_surface_ob;
1224 C,
1225 scene,
1226 &curves_ob,
1227 &new_surface_ob,
1229 false,
1230 true,
1231 nullptr);
1232
1234 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
1236
1237 /* Required for deformation. */
1239 DEG_id_tag_update(&new_surface_ob.id, ID_RECALC_GEOMETRY);
1240 }
1242
1244
1245 return OPERATOR_FINISHED;
1246}
1247
1248} // namespace surface_set
1249
1251{
1252 ot->name = "Set Curves Surface Object";
1253 ot->idname = __func__;
1254 ot->description =
1255 "Use the active object as surface for selected curves objects and set it as the parent";
1256
1259
1260 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1261}
1262
1263namespace curves_delete {
1264
1266{
1267 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1268 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1269 if (remove_selection(curves, bke::AttrDomain(curves_id->selection_domain))) {
1270 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1271 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1272 }
1273 }
1274
1275 return OPERATOR_FINISHED;
1276}
1277
1278} // namespace curves_delete
1279
1281{
1282 ot->name = "Delete";
1283 ot->idname = __func__;
1284 ot->description = "Remove selected control points or curves";
1285
1288
1289 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1290}
1291
1293
1295{
1296 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1297 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1298 IndexMaskMemory memory;
1299 switch (bke::AttrDomain(curves_id->selection_domain)) {
1301 duplicate_points(curves, retrieve_selected_points(*curves_id, memory));
1302 break;
1304 duplicate_curves(curves, retrieve_selected_curves(*curves_id, memory));
1305 break;
1306 default:
1308 break;
1309 }
1310 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1311 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1312 }
1313 return OPERATOR_FINISHED;
1314}
1315
1316} // namespace curves_duplicate
1317
1319{
1320 ot->name = "Duplicate";
1321 ot->idname = __func__;
1322 ot->description = "Copy selected points or curves";
1323
1326
1327 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1328}
1329
1330namespace clear_tilt {
1331
1333{
1334 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1335 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1336 IndexMaskMemory memory;
1337 const IndexMask selection = retrieve_selected_points(*curves_id, memory);
1338 if (selection.is_empty()) {
1339 continue;
1340 }
1341
1342 if (selection.size() == curves.points_num()) {
1343 curves.attributes_for_write().remove("tilt");
1344 }
1345 else {
1346 index_mask::masked_fill(curves.tilt_for_write(), 0.0f, selection);
1347 }
1348
1349 curves.tag_normals_changed();
1350 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1351 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1352 }
1353 return OPERATOR_FINISHED;
1354}
1355
1356} // namespace clear_tilt
1357
1359{
1360 ot->name = "Clear Tilt";
1361 ot->idname = __func__;
1362 ot->description = "Clear the tilt of selected control points";
1363
1364 ot->exec = clear_tilt::exec;
1366
1367 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1368}
1369
1370namespace cyclic_toggle {
1371
1373{
1374 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1375 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1376 IndexMaskMemory memory;
1377 const IndexMask selection = retrieve_selected_curves(*curves_id, memory);
1378 if (selection.is_empty()) {
1379 continue;
1380 }
1381
1382 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1383
1385 "cyclic", bke::AttrDomain::Curve);
1386 selection.foreach_index(GrainSize(4096),
1387 [&](const int i) { cyclic.span[i] = !cyclic.span[i]; });
1388 cyclic.finish();
1389
1390 if (!cyclic.span.contains(true)) {
1391 attributes.remove("cyclic");
1392 }
1393
1394 curves.calculate_bezier_auto_handles();
1395
1396 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1397 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1398 }
1399 return OPERATOR_FINISHED;
1400}
1401
1402} // namespace cyclic_toggle
1403
1405{
1406 ot->name = "Toggle Cyclic";
1407 ot->idname = __func__;
1408 ot->description = "Make active curve closed/opened loop";
1409
1410 ot->exec = cyclic_toggle::exec;
1412
1413 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1414}
1415
1417
1419{
1420 const CurveType dst_type = CurveType(RNA_enum_get(op->ptr, "type"));
1421 const bool use_handles = RNA_boolean_get(op->ptr, "use_handles");
1422
1423 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1424 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1425 IndexMaskMemory memory;
1426 const IndexMask selection = retrieve_selected_curves(*curves_id, memory);
1427 if (selection.is_empty()) {
1428 continue;
1429 }
1430
1432 options.convert_bezier_handles_to_poly_points = use_handles;
1433 options.convert_bezier_handles_to_catmull_rom_points = use_handles;
1434 options.keep_bezier_shape_as_nurbs = use_handles;
1435 options.keep_catmull_rom_shape_as_nurbs = use_handles;
1436
1437 curves = geometry::convert_curves(curves, selection, dst_type, {}, options);
1438
1439 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1440 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1441 }
1442 return OPERATOR_FINISHED;
1443}
1444
1445} // namespace curve_type_set
1446
1448{
1449 ot->name = "Set Curve Type";
1450 ot->idname = __func__;
1451 ot->description = "Set type of selected curves";
1452
1453 ot->exec = curve_type_set::exec;
1455
1456 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1457
1458 ot->prop = RNA_def_enum(
1459 ot->srna, "type", rna_enum_curves_type_items, CURVE_TYPE_POLY, "Type", "Curve type");
1460
1461 RNA_def_boolean(ot->srna,
1462 "use_handles",
1463 false,
1464 "Handles",
1465 "Take handle information into account in the conversion");
1466}
1467
1469
1471{
1472 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1473 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1474 IndexMaskMemory memory;
1475 const IndexMask selection = retrieve_selected_curves(*curves_id, memory);
1476 if (selection.is_empty()) {
1477 continue;
1478 }
1479
1480 curves.reverse_curves(selection);
1481
1482 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1483 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1484 }
1485 return OPERATOR_FINISHED;
1486}
1487
1488} // namespace switch_direction
1489
1491{
1492 ot->name = "Switch Direction";
1493 ot->idname = __func__;
1494 ot->description = "Reverse the direction of the selected curves";
1495
1496 ot->exec = switch_direction::exec;
1498
1499 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1500}
1501
1502namespace subdivide {
1503
1505{
1506 const int number_cuts = RNA_int_get(op->ptr, "number_cuts");
1507
1508 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1509 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1510 const int points_num = curves.points_num();
1511 IndexMaskMemory memory;
1512 const IndexMask points_selection = retrieve_selected_points(*curves_id, memory);
1513 if (points_selection.is_empty()) {
1514 continue;
1515 }
1516
1517 Array<bool> points_selection_span(points_num);
1518 points_selection.to_bools(points_selection_span);
1519
1520 Array<int> segment_cuts(points_num, number_cuts);
1521
1522 const OffsetIndices points_by_curve = curves.points_by_curve();
1523 threading::parallel_for(points_by_curve.index_range(), 512, [&](const IndexRange range) {
1524 for (const int curve_i : range) {
1525 const IndexRange points = points_by_curve[curve_i];
1526 if (points.size() <= 1) {
1527 continue;
1528 }
1529 for (const int point_i : points.drop_back(1)) {
1530 if (!points_selection_span[point_i] || !points_selection_span[point_i + 1]) {
1531 segment_cuts[point_i] = 0;
1532 }
1533 }
1534 /* Cyclic segment. Doesn't matter if it is computed even if the curve is not cyclic. */
1535 if (!points_selection_span[points.last()] || !points_selection_span[points.first()]) {
1536 segment_cuts[points.last()] = 0;
1537 }
1538 }
1539 });
1540
1542 curves, curves.curves_range(), VArray<int>::from_span(segment_cuts), {});
1543
1544 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1545 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1546 }
1547 return OPERATOR_FINISHED;
1548}
1549
1550} // namespace subdivide
1551
1553{
1554 ot->name = "Subdivide";
1555 ot->idname = __func__;
1556 ot->description = "Subdivide selected curve segments";
1557
1558 ot->exec = subdivide::exec;
1560
1561 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1562
1563 PropertyRNA *prop;
1564 prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10);
1565 /* Avoid re-using last value because it can cause an unexpectedly high number of subdivisions. */
1567}
1568
1571 Curves &curves_id,
1572 CurvesGeometry new_curves,
1573 wmOperator &op)
1574{
1575 const int new_points_num = new_curves.points_num();
1576 const int new_curves_num = new_curves.curves_num();
1577
1578 /* Create geometry sets so that generic join code can be used. */
1582 bke::curves_new_nomain(std::move(new_curves)));
1583
1584 /* Transform primitive according to settings. */
1585 float3 location;
1586 float3 rotation;
1587 float3 scale;
1588 object::add_generic_get_opts(C, &op, 'Z', location, rotation, scale, nullptr, nullptr, nullptr);
1590 location, math::EulerXYZ(rotation), scale);
1592
1593 bke::GeometrySet joined_geometry = geometry::join_geometries({old_geometry, new_geometry}, {});
1594 Curves *joined_curves_id = joined_geometry.get_curves_for_write();
1595 CurvesGeometry &dst_curves = curves_id.geometry.wrap();
1596 dst_curves = std::move(joined_curves_id->geometry.wrap());
1597
1598 /* Only select the new curves. */
1599 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
1600 const int new_element_num = selection_domain == bke::AttrDomain::Point ? new_points_num :
1601 new_curves_num;
1603 dst_curves, selection_domain, [&](bke::GSpanAttributeWriter &selection) {
1604 fill_selection_false(selection.span.drop_back(new_element_num));
1605 fill_selection_true(selection.span.take_back(new_element_num));
1606 });
1607
1608 dst_curves.tag_topology_changed();
1609}
1610
1611namespace add_circle {
1612
1614{
1615 CurvesGeometry curves{4, 1};
1616
1617 MutableSpan<int> offsets = curves.offsets_for_write();
1618 offsets[0] = 0;
1619 offsets[1] = 4;
1620
1621 curves.fill_curve_types(CURVE_TYPE_BEZIER);
1622 curves.cyclic_for_write().fill(true);
1623 curves.handle_types_left_for_write().fill(BEZIER_HANDLE_AUTO);
1624 curves.handle_types_right_for_write().fill(BEZIER_HANDLE_AUTO);
1625 curves.resolution_for_write().fill(12);
1626
1627 MutableSpan<float3> positions = curves.positions_for_write();
1628 positions[0] = float3(-radius, 0, 0);
1629 positions[1] = float3(0, radius, 0);
1630 positions[2] = float3(radius, 0, 0);
1631 positions[3] = float3(0, -radius, 0);
1632
1633 /* Ensure these attributes exist. */
1634 curves.handle_positions_left_for_write();
1635 curves.handle_positions_right_for_write();
1636
1637 curves.calculate_bezier_auto_handles();
1638
1639 return curves;
1640}
1641
1643{
1644 Object *object = CTX_data_edit_object(C);
1645 Curves *active_curves_id = static_cast<Curves *>(object->data);
1646
1647 const float radius = RNA_float_get(op->ptr, "radius");
1648 append_primitive_curve(C, *active_curves_id, generate_circle_primitive(radius), *op);
1649
1650 DEG_id_tag_update(&active_curves_id->id, ID_RECALC_GEOMETRY);
1651 WM_event_add_notifier(C, NC_GEOM | ND_DATA, active_curves_id);
1652 return OPERATOR_FINISHED;
1653}
1654
1655} // namespace add_circle
1656
1658{
1659 ot->name = "Add Circle";
1660 ot->idname = __func__;
1661 ot->description = "Add new circle curve";
1662
1663 ot->exec = add_circle::exec;
1665
1666 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1667
1670}
1671
1672namespace add_bezier {
1673
1675{
1676 CurvesGeometry curves{2, 1};
1677
1678 MutableSpan<int> offsets = curves.offsets_for_write();
1679 offsets[0] = 0;
1680 offsets[1] = 2;
1681
1682 curves.fill_curve_types(CURVE_TYPE_BEZIER);
1683 curves.handle_types_left_for_write().fill(BEZIER_HANDLE_ALIGN);
1684 curves.handle_types_right_for_write().fill(BEZIER_HANDLE_ALIGN);
1685 curves.resolution_for_write().fill(12);
1686
1687 MutableSpan<float3> positions = curves.positions_for_write();
1688 MutableSpan<float3> left_handles = curves.handle_positions_left_for_write();
1689 MutableSpan<float3> right_handles = curves.handle_positions_right_for_write();
1690
1691 left_handles[0] = float3(-1.5f, -0.5, 0) * radius;
1692 positions[0] = float3(-1.0f, 0, 0) * radius;
1693 right_handles[0] = float3(-0.5f, 0.5f, 0) * radius;
1694
1695 left_handles[1] = float3(0, 0, 0) * radius;
1696 positions[1] = float3(1.0f, 0, 0) * radius;
1697 right_handles[1] = float3(2.0f, 0, 0) * radius;
1698
1699 return curves;
1700}
1701
1703{
1704 Object *object = CTX_data_edit_object(C);
1705 Curves *active_curves_id = static_cast<Curves *>(object->data);
1706
1707 const float radius = RNA_float_get(op->ptr, "radius");
1708 append_primitive_curve(C, *active_curves_id, generate_bezier_primitive(radius), *op);
1709
1710 DEG_id_tag_update(&active_curves_id->id, ID_RECALC_GEOMETRY);
1711 WM_event_add_notifier(C, NC_GEOM | ND_DATA, active_curves_id);
1712 return OPERATOR_FINISHED;
1713}
1714
1715} // namespace add_bezier
1716
1718{
1719 ot->name = "Add Bézier";
1720 ot->idname = __func__;
1721 ot->description = "Add new Bézier curve";
1722
1723 ot->exec = add_bezier::exec;
1725
1726 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1727
1730}
1731
1733
1735{
1736 const SetHandleType dst_type = SetHandleType(RNA_enum_get(op->ptr, "type"));
1737
1738 auto new_handle_type = [&](const int8_t handle_type) {
1739 switch (dst_type) {
1741 return int8_t(BEZIER_HANDLE_FREE);
1743 return int8_t(BEZIER_HANDLE_AUTO);
1745 return int8_t(BEZIER_HANDLE_VECTOR);
1747 return int8_t(BEZIER_HANDLE_ALIGN);
1749 return int8_t(handle_type == BEZIER_HANDLE_FREE ? BEZIER_HANDLE_ALIGN :
1751 }
1753 return int8_t(0);
1754 };
1755
1756 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1757 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1758 const bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1759
1760 const VArraySpan<bool> selection = *attributes.lookup_or_default<bool>(
1761 ".selection", bke::AttrDomain::Point, true);
1762 const VArraySpan<bool> selection_left = *attributes.lookup_or_default<bool>(
1763 ".selection_handle_left", bke::AttrDomain::Point, true);
1764 const VArraySpan<bool> selection_right = *attributes.lookup_or_default<bool>(
1765 ".selection_handle_right", bke::AttrDomain::Point, true);
1766
1767 MutableSpan<int8_t> handle_types_left = curves.handle_types_left_for_write();
1768 MutableSpan<int8_t> handle_types_right = curves.handle_types_right_for_write();
1769
1770 threading::parallel_for(curves.points_range(), 4096, [&](const IndexRange range) {
1771 for (const int point_i : range) {
1772 if (selection_left[point_i] || selection[point_i]) {
1773 handle_types_left[point_i] = new_handle_type(handle_types_left[point_i]);
1774 }
1775 if (selection_right[point_i] || selection[point_i]) {
1776 handle_types_right[point_i] = new_handle_type(handle_types_right[point_i]);
1777 }
1778 }
1779 });
1780
1781 curves.calculate_bezier_auto_handles();
1782 curves.tag_topology_changed();
1783
1784 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1785 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1786 }
1787 return OPERATOR_FINISHED;
1788}
1789
1790} // namespace set_handle_type
1791
1793 {int(SetHandleType::Auto),
1794 "AUTO",
1795 0,
1796 "Auto",
1797 "The location is automatically calculated to be smooth"},
1799 "VECTOR",
1800 0,
1801 "Vector",
1802 "The location is calculated to point to the next/previous control point"},
1804 "ALIGN",
1805 0,
1806 "Align",
1807 "The location is constrained to point in the opposite direction as the other handle"},
1808 {int(SetHandleType::Free),
1809 "FREE_ALIGN",
1810 0,
1811 "Free",
1812 "The handle can be moved anywhere, and does not influence the point's other handle"},
1814 "TOGGLE_FREE_ALIGN",
1815 0,
1816 "Toggle Free/Align",
1817 "Replace Free handles with Align, and all Align with Free handles"},
1818 {0, nullptr, 0, nullptr, nullptr},
1819};
1820
1822{
1823 ot->name = "Set Handle Type";
1824 ot->idname = __func__;
1825 ot->description = "Set the handle type for bezier curves";
1826
1827 ot->invoke = WM_menu_invoke;
1828 ot->exec = set_handle_type::exec;
1830
1831 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1832
1833 ot->prop = RNA_def_enum(ot->srna,
1834 "type",
1837 "Type",
1838 nullptr);
1839}
1840
1842{
1870
1872}
1873
1875{
1877 wmOperatorTypeMacro *otmacro;
1878
1879 ot = WM_operatortype_append_macro("CURVES_OT_duplicate_move",
1880 "Duplicate",
1881 "Make copies of selected elements and move them",
1883 WM_operatortype_macro_define(ot, "CURVES_OT_duplicate");
1884 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1885 RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
1886 RNA_boolean_set(otmacro->ptr, "mirror", false);
1887
1888 ot = WM_operatortype_append_macro("CURVES_OT_extrude_move",
1889 "Extrude Curve and Move",
1890 "Extrude curve and move result",
1892 WM_operatortype_macro_define(ot, "CURVES_OT_extrude");
1893 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1894 RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
1895 RNA_boolean_set(otmacro->ptr, "mirror", false);
1896}
1897
1899{
1900 /* Only set in editmode curves, by space_view3d listener. */
1901 wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Curves", SPACE_EMPTY, RGN_TYPE_WINDOW);
1903
1905}
1906
1907} // namespace blender::ed::curves
std::optional< blender::StringRefNull > BKE_attributes_active_name_get(AttributeOwner &owner)
Definition attribute.cc:784
void BKE_attributes_active_set(AttributeOwner &owner, blender::StringRef name)
Definition attribute.cc:830
#define CTX_DATA_BEGIN(C, Type, instance, member)
PointerRNA CTX_data_pointer_get_type(const bContext *C, const char *member, StructRNA *type)
void CTX_wm_operator_poll_msg_set(bContext *C, const char *msg)
Depsgraph * CTX_data_depsgraph_pointer(const bContext *C)
Object * CTX_data_active_object(const bContext *C)
Scene * CTX_data_scene(const bContext *C)
Object * CTX_data_edit_object(const bContext *C)
Main * CTX_data_main(const bContext *C)
#define CTX_DATA_END
View3D * CTX_wm_view3d(const bContext *C)
ViewLayer * CTX_data_view_layer(const bContext *C)
Low-level operations for curves.
CustomData interface, see also DNA_customdata_types.h.
const void * CustomData_get_layer(const CustomData *data, eCustomDataType type)
const char * CustomData_get_active_layer_name(const CustomData *data, eCustomDataType type)
bool BKE_id_is_editable(const Main *bmain, const ID *id)
Definition lib_id.cc:2523
void BKE_mesh_tessface_calc(Mesh *mesh)
General operations, lookup, etc. for blender objects.
void BKE_object_apply_mat4(Object *ob, const float mat[4][4], bool use_compat, bool use_parent)
Object * BKE_object_add(Main *bmain, Scene *scene, ViewLayer *view_layer, int type, const char *name) ATTR_NONNULL(1
struct ModifierData * object_add_particle_system(struct Main *bmain, const struct Scene *scene, struct Object *ob, const char *name)
#define DMCACHE_NOTFOUND
void psys_mat_hair_to_object(struct Object *ob, struct Mesh *mesh, short from, struct ParticleData *pa, float hairmat[4][4])
Definition particle.cc:3844
void psys_free_particles(struct ParticleSystem *psys)
Definition particle.cc:932
void psys_changed_type(struct Object *ob, struct ParticleSystem *psys)
struct ParticleSystem * psys_get_current(struct Object *ob)
Definition particle.cc:538
@ RPT_INFO
Definition BKE_report.hh:35
@ RPT_ERROR
Definition BKE_report.hh:39
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:153
#define BLI_assert_unreachable()
Definition BLI_assert.h:93
#define BLI_assert(a)
Definition BLI_assert.h:46
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
#define LISTBASE_FOREACH(type, var, list)
void interp_weights_tri_v3(float w[3], const float v1[3], const float v2[3], const float v3[3], const float co[3])
void closest_on_tri_to_point_v3(float r[3], const float p[3], const float v1[3], const float v2[3], const float v3[3])
void interp_weights_poly_v3(float w[], float v[][3], int n, const float co[3])
MINLINE void copy_v4_v4(float r[4], const float a[4])
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void copy_v3_v3(float r[3], const float a[3])
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.cc:41
#define ELEM(...)
#define STREQ(a, b)
#define IFACE_(msgid)
void DEG_id_tag_update(ID *id, unsigned int flags)
void DEG_relations_tag_update(Main *bmain)
T * DEG_get_evaluated(const Depsgraph *depsgraph, T *id)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1054
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1118
@ ID_RECALC_PSYS_RESET
Definition DNA_ID.h:1083
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1074
@ BEZIER_HANDLE_FREE
@ BEZIER_HANDLE_ALIGN
@ BEZIER_HANDLE_VECTOR
@ BEZIER_HANDLE_AUTO
@ CD_PROP_FLOAT2
@ eModifierType_ParticleSystem
@ OB_MODE_EDIT
@ OB_MODE_SCULPT_CURVES
Object is a sort of wrapper for general info.
@ OB_MODIFIER_FLAG_ADD_REST_POSITION
@ OB_MESH
@ OB_CURVES
@ PSYS_EDITED
@ PART_DRAW_PARENT
@ PART_HAIR
@ PART_FROM_FACE
@ RGN_TYPE_WINDOW
@ SPACE_EMPTY
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
bool ED_operator_object_active_editable_ex(bContext *C, const Object *ob)
@ SEL_SELECT
@ SEL_DESELECT
@ SEL_TOGGLE
#define MEM_SAFE_FREE(v)
@ PROP_SKIP_SAVE
Definition RNA_types.hh:344
@ PROP_HIDDEN
Definition RNA_types.hh:338
#define C
Definition RandGen.cpp:29
@ UI_ITEM_R_SLIDER
#define UI_ITEM_NONE
#define NC_WINDOW
Definition WM_types.hh:375
#define NC_NODE
Definition WM_types.hh:394
#define NC_GEOM
Definition WM_types.hh:393
#define ND_DRAW
Definition WM_types.hh:461
#define ND_DATA
Definition WM_types.hh:509
#define NA_ADDED
Definition WM_types.hh:586
@ OPTYPE_UNDO
Definition WM_types.hh:182
@ OPTYPE_REGISTER
Definition WM_types.hh:180
#define NA_EDITED
Definition WM_types.hh:584
#define ND_PARTICLE
Definition WM_types.hh:465
#define ND_SPACE_VIEW3D
Definition WM_types.hh:528
#define NC_OBJECT
Definition WM_types.hh:379
#define NC_SPACE
Definition WM_types.hh:392
BMesh const char void * data
BPy_StructRNA * depsgraph
static unsigned long seed
Definition btSoftBody.h:39
static AttributeOwner from_id(ID *id)
Definition attribute.cc:44
AttributeSet attributes
bool is() const
GMutableSpan take_back(const int64_t n) const
GMutableSpan drop_back(const int64_t n) const
const CPPType & type() const
MutableSpan< T > typed() const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
constexpr int64_t first() const
constexpr int64_t size() const
constexpr int64_t size() const
Definition BLI_span.hh:493
constexpr T * data() const
Definition BLI_span.hh:539
constexpr IndexRange index_range() const
Definition BLI_span.hh:670
constexpr const T & first() const
Definition BLI_span.hh:315
constexpr int64_t size() const
Definition BLI_span.hh:252
constexpr const T * end() const
Definition BLI_span.hh:224
constexpr IndexRange index_range() const
Definition BLI_span.hh:401
constexpr const T * begin() const
Definition BLI_span.hh:220
constexpr bool is_empty() const
Definition BLI_span.hh:260
static VArray from_span(Span< T > values)
bool add(const Key &key)
void add_new(const Key &key)
int64_t size() const
void append(const T &value)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType data_type, const void *default_value=nullptr) const
GAttributeReader lookup(const StringRef attribute_id) const
int domain_size(const AttrDomain domain) const
bool add(const StringRef attribute_id, const AttrDomain domain, const AttrType data_type, const AttributeInit &initializer)
GSpanAttributeWriter lookup_or_add_for_write_span(StringRef attribute_id, AttrDomain domain, AttrType data_type, const AttributeInit &initializer=AttributeInitDefaultValue())
bool remove(const StringRef attribute_id)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void to_bools(MutableSpan< bool > r_bools) const
void foreach_index(Fn &&fn) const
nullptr float
CCL_NAMESPACE_BEGIN struct Options options
static wmOperatorStatus duplicate_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_linked_exec(bContext *C, wmOperator *)
static wmOperatorStatus delete_exec(bContext *C, wmOperator *op)
Definition editfont.cc:1717
#define INT32_MAX
#define INT32_MIN
uint col
VecBase< float, 3 > float3
void * MEM_calloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:123
void * MEM_malloc_arrayN(size_t len, size_t size, const char *str)
Definition mallocn.cc:133
void MEM_freeN(void *vmemh)
Definition mallocn.cc:113
static wmOperatorStatus select_all_exec(bContext *C, wmOperator *op)
AttrType cpp_type_to_attribute_type(const CPPType &type)
Curves * curves_new_nomain(int points_num, int curves_num)
static wmOperatorStatus exec(bContext *C, wmOperator *op)
static CurvesGeometry generate_bezier_primitive(const float radius)
static CurvesGeometry generate_circle_primitive(const float radius)
static wmOperatorStatus exec(bContext *C, wmOperator *op)
static wmOperatorStatus exec(bContext *C, wmOperator *)
static wmOperatorStatus curves_convert_from_particle_system_exec(bContext *C, wmOperator *)
static bke::CurvesGeometry particles_to_curves(Object &object, ParticleSystem &psys)
static bool curves_convert_from_particle_system_poll(bContext *C)
static float4 compute_mface_weights_for_position(const Span< float3 > positions, const MFace &mface, const float3 &position)
static int find_mface_for_root_position(const Span< float3 > positions, const MFace *mface, const Span< int > possible_mface_indices, const float3 &root_pos)
static wmOperatorStatus curves_convert_to_particle_system_exec(bContext *C, wmOperator *op)
static void try_convert_single_object(Object &curves_ob, Main &bmain, Scene &scene, bool *r_could_not_convert_some_curves)
static wmOperatorStatus exec(bContext *C, wmOperator *op)
static wmOperatorStatus delete_exec(bContext *C, wmOperator *)
static wmOperatorStatus duplicate_exec(bContext *C, wmOperator *)
static wmOperatorStatus exec(bContext *C, wmOperator *)
static wmOperatorStatus exec(bContext *C, wmOperator *op)
static wmOperatorStatus curves_set_selection_domain_exec(bContext *C, wmOperator *op)
static wmOperatorStatus snap_curves_to_surface_exec(bContext *C, wmOperator *op)
static void snap_curves_to_surface_exec_object(Object &curves_ob, const Object &surface_ob, const AttachMode attach_mode, bool *r_invalid_uvs, bool *r_missing_uvs)
static wmOperatorStatus split_exec(bContext *C, wmOperator *)
static wmOperatorStatus exec(bContext *C, wmOperator *op)
static wmOperatorStatus surface_set_exec(bContext *C, wmOperator *op)
static bool surface_set_poll(bContext *C)
static wmOperatorStatus exec(bContext *C, wmOperator *)
static void CURVES_OT_surface_set(wmOperatorType *ot)
void select_linked(bke::CurvesGeometry &curves, const IndexMask &curves_mask)
const EnumPropertyItem rna_enum_set_handle_type_items[]
static wmOperatorStatus select_linked_exec(bContext *C, wmOperator *)
bool remove_selection(bke::CurvesGeometry &curves, const bke::AttrDomain selection_domain)
static void CURVES_OT_convert_from_particle_system(wmOperatorType *ot)
void keymap_curves(wmKeyConfig *keyconf)
void CURVES_OT_draw(wmOperatorType *ot)
static bool has_anything_selected(const Span< Curves * > curves_ids)
static void CURVES_OT_select_less(wmOperatorType *ot)
IndexMask retrieve_all_selected_points(const bke::CurvesGeometry &curves, const int handle_display, IndexMaskMemory &memory)
static void CURVES_OT_convert_to_particle_system(wmOperatorType *ot)
static void CURVES_OT_select_random(wmOperatorType *ot)
IndexMask retrieve_selected_curves(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
static void CURVES_OT_select_ends(wmOperatorType *ot)
void select_all(bke::CurvesGeometry &curves, const IndexMask &mask, const bke::AttrDomain selection_domain, int action)
static bool editable_curves_point_domain_poll(bContext *C)
void duplicate_curves(bke::CurvesGeometry &curves, const IndexMask &mask)
static void CURVES_OT_add_bezier(wmOperatorType *ot)
void duplicate_points(bke::CurvesGeometry &curves, const IndexMask &mask)
static void CURVES_OT_select_linked(wmOperatorType *ot)
VectorSet< Curves * > get_unique_editable_curves(const bContext &C)
Definition curves_ops.cc:91
bool editable_curves_poll(bContext *C)
void select_adjacent(bke::CurvesGeometry &curves, const IndexMask &curves_mask, const bool deselect)
static void CURVES_OT_handle_type_set(wmOperatorType *ot)
static void CURVES_OT_subdivide(wmOperatorType *ot)
void foreach_selection_attribute_writer(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, blender::FunctionRef< void(bke::GSpanAttributeWriter &selection)> fn)
static void CURVES_OT_select_more(wmOperatorType *ot)
static void CURVES_OT_select_all(wmOperatorType *ot)
static void CURVES_OT_switch_direction(wmOperatorType *ot)
bke::CurvesGeometry split_points(const bke::CurvesGeometry &curves, const IndexMask &points_to_split)
void fill_selection_false(GMutableSpan selection)
bool object_has_editable_curves(const Main &bmain, const Object &object)
Definition curves_ops.cc:77
static void select_random_ui(bContext *, wmOperator *op)
void ED_curves_pentool_modal_keymap(wmKeyConfig *keyconf)
bool editable_curves_in_edit_mode_poll(bContext *C)
static void CURVES_OT_split(wmOperatorType *ot)
static void CURVES_OT_tilt_clear(wmOperatorType *ot)
static void CURVES_OT_duplicate(wmOperatorType *ot)
static wmOperatorStatus select_all_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_more_exec(bContext *C, wmOperator *)
void fill_selection_true(GMutableSpan selection)
static bool curves_poll_impl(bContext *C, const bool check_editable, const bool check_surface, const bool check_edit_mode)
Span< StringRef > get_curves_selection_attribute_names(const bke::CurvesGeometry &curves)
static void CURVES_OT_curve_type_set(wmOperatorType *ot)
static wmOperatorStatus select_random_exec(bContext *C, wmOperator *op)
static void CURVES_OT_cyclic_toggle(wmOperatorType *ot)
void ED_operatortypes_curves_pen()
static wmOperatorStatus select_ends_exec(bContext *C, wmOperator *op)
static void CURVES_OT_delete(wmOperatorType *ot)
static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
static wmOperatorStatus select_less_exec(bContext *C, wmOperator *)
void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob)
Definition curves_add.cc:64
static void append_primitive_curve(bContext *C, Curves &curves_id, CurvesGeometry new_curves, wmOperator &op)
IndexMask end_points(const bke::CurvesGeometry &curves, const IndexMask &curves_mask, const int amount_start, const int amount_end, const bool inverted, IndexMaskMemory &memory)
bool curves_with_surface_poll(bContext *C)
static void select_ends_ui(bContext *, wmOperator *op)
void CURVES_OT_separate(wmOperatorType *ot)
void CURVES_OT_attribute_set(wmOperatorType *ot)
bool curves_poll(bContext *C)
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 CURVES_OT_select_linked_pick(wmOperatorType *ot)
static void CURVES_OT_set_selection_domain(wmOperatorType *ot)
static void CURVES_OT_add_circle(wmOperatorType *ot)
bool editable_curves_with_surface_poll(bContext *C)
void add_unit_props_radius(wmOperatorType *ot)
bool parent_set(ReportList *reports, const bContext *C, Scene *scene, Object *const ob, Object *const par, int partype, bool xmirror, bool keep_transform, const int vert_par[3])
Object * context_active_object(const bContext *C)
void add_generic_props(wmOperatorType *ot, bool do_editmode)
void add_generic_get_opts(bContext *C, wmOperator *op, char view_align_axis, float r_loc[3], float r_rot[3], float r_scale[3], bool *r_enter_editmode, unsigned short *r_local_view_bits, bool *r_is_view_aligned)
bke::GeometrySet join_geometries(Span< bke::GeometrySet > geometries, const bke::AttributeFilter &attribute_filter, const std::optional< Span< bke::GeometryComponent::Type > > &component_types_to_join=std::nullopt, bool allow_merging_instance_references=true)
std::optional< TransformGeometryErrors > transform_geometry(bke::GeometrySet &geometry, const float4x4 &transform)
bke::CurvesGeometry subdivide_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, const VArray< int > &cuts, const bke::AttributeFilter &attribute_filter={})
bke::CurvesGeometry convert_curves(const bke::CurvesGeometry &src_curves, const IndexMask &selection, CurveType dst_type, const bke::AttributeFilter &attribute_filter, const ConvertCurvesOptions &options={})
void masked_fill(MutableSpan< T > data, const T &value, const IndexMask &mask)
EulerXYZBase< float > EulerXYZ
CartesianBasis invert(const CartesianBasis &basis)
MatT from_loc_rot_scale(const typename MatT::loc_type &location, const RotationT &rotation, const VecBase< typename MatT::base_type, ScaleDim > &scale)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
Definition BLI_task.hh:93
MatBase< float, 4, 4 > float4x4
VecBase< float, 4 > float4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
static void exec(void *data, int, bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
static wmOperatorStatus select_random_exec(bContext *C, wmOperator *op)
static wmOperatorStatus select_less_exec(bContext *C, wmOperator *)
static wmOperatorStatus select_more_exec(bContext *C, wmOperator *)
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
int RNA_int_get(PointerRNA *ptr, const char *name)
float RNA_float_get(PointerRNA *ptr, const char *name)
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
int RNA_enum_get(PointerRNA *ptr, const char *name)
const EnumPropertyItem rna_enum_attribute_curves_domain_items[]
const EnumPropertyItem rna_enum_curves_type_items[]
Definition rna_curves.cc:24
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, const float default_value, const float hardmin, const float hardmax, const char *ui_name, const char *ui_description, const float softmin, const float softmax)
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, const int default_value, const char *ui_name, const char *ui_description)
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, const bool default_value, const char *ui_name, const char *ui_description)
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, const int default_value, const int hardmin, const int hardmax, const char *ui_name, const char *ui_description, const int softmin, const int softmax)
#define FLT_MAX
Definition stdcycles.h:14
CurvesGeometry geometry
char selection_domain
struct Object * surface
char * surface_uv_map
Definition DNA_ID.h:414
char name[258]
Definition DNA_ID.h:432
unsigned int v2
unsigned int v1
unsigned int v4
unsigned int v3
CustomData corner_data
CustomData fdata_legacy
int totface_legacy
int faces_num
ListBase particlesystem
ListBase modifiers
uint8_t modifier_flag
struct ParticleSystem * psys
ParticleData * particles
ParticleSettings * part
struct ParticleCacheKey ** childcache
struct ParticleCacheKey ** pathcache
void * data
Definition RNA_types.hh:53
View3DOverlay overlay
const c_style_mat & ptr() const
BVHTree_NearestPointCallback nearest_callback
static GeometrySet from_curves(Curves *curves, GeometryOwnershipType ownership=GeometryOwnershipType::Owned)
uiLayout & column(bool align)
void use_property_split_set(bool value)
void prop(PointerRNA *ptr, PropertyRNA *prop, int index, int value, eUI_Item_Flag flag, std::optional< blender::StringRef > name_opt, int icon, std::optional< blender::StringRef > placeholder=std::nullopt)
bool(* poll)(struct bContext *)
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
i
Definition text_draw.cc:230
void WM_main_add_notifier(uint type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition wm_files.cc:4237
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:895
void WM_operator_properties_select_all(wmOperatorType *ot)
wmOperatorTypeMacro * WM_operatortype_macro_define(wmOperatorType *ot, const char *idname)
void WM_operatortype_append(void(*opfunc)(wmOperatorType *))
wmOperatorType * WM_operatortype_append_macro(const char *idname, const char *name, const char *description, int flag)
wmOperatorStatus WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)