Blender V4.3
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
9#include <atomic>
10
11#include "BLI_array_utils.hh"
13#include "BLI_kdtree.h"
14#include "BLI_math_geom.h"
15#include "BLI_math_matrix.hh"
16#include "BLI_rand.hh"
17#include "BLI_string.h"
18#include "BLI_utildefines.h"
19#include "BLI_vector_set.hh"
20
21#include "BLT_translation.hh"
22
23#include "ED_curves.hh"
24#include "ED_object.hh"
25#include "ED_screen.hh"
26#include "ED_select_utils.hh"
27#include "ED_view3d.hh"
28
29#include "WM_api.hh"
30
31#include "BKE_attribute_math.hh"
32#include "BKE_bvhutils.hh"
33#include "BKE_context.hh"
34#include "BKE_curves.hh"
35#include "BKE_customdata.hh"
36#include "BKE_geometry_set.hh"
37#include "BKE_layer.hh"
38#include "BKE_lib_id.hh"
39#include "BKE_mesh.hh"
41#include "BKE_mesh_runtime.hh"
42#include "BKE_mesh_sample.hh"
43#include "BKE_object.hh"
44#include "BKE_paint.hh"
45#include "BKE_particle.h"
46#include "BKE_report.hh"
47
48#include "DNA_mesh_types.h"
49#include "DNA_meshdata_types.h"
50#include "DNA_modifier_types.h"
51#include "DNA_object_types.h"
52#include "DNA_particle_types.h"
53#include "DNA_scene_types.h"
54
55#include "DEG_depsgraph.hh"
57
58#include "RNA_access.hh"
59#include "RNA_define.hh"
60#include "RNA_enum_types.hh"
61#include "RNA_prototypes.hh"
62
63#include "UI_interface.hh"
64#include "UI_resources.hh"
65
68#include "GEO_set_curve_type.hh"
70#include "GEO_transform.hh"
71
80namespace blender::ed::curves {
81
82bool object_has_editable_curves(const Main &bmain, const Object &object)
83{
84 if (object.type != OB_CURVES) {
85 return false;
86 }
87 if (!ELEM(object.mode, OB_MODE_SCULPT_CURVES, OB_MODE_EDIT)) {
88 return false;
89 }
90 if (!BKE_id_is_editable(&bmain, static_cast<const ID *>(object.data))) {
91 return false;
92 }
93 return true;
94}
95
97{
98 VectorSet<Curves *> unique_curves;
99
100 const Main &bmain = *CTX_data_main(&C);
101
102 Object *object = CTX_data_active_object(&C);
103 if (object && object_has_editable_curves(bmain, *object)) {
104 unique_curves.add_new(static_cast<Curves *>(object->data));
105 }
106
107 CTX_DATA_BEGIN (&C, Object *, object, selected_objects) {
108 if (object_has_editable_curves(bmain, *object)) {
109 unique_curves.add(static_cast<Curves *>(object->data));
110 }
111 }
113
114 return unique_curves;
115}
116
118 const bool check_editable,
119 const bool check_surface,
120 const bool check_edit_mode)
121{
122 Object *object = CTX_data_active_object(C);
123 if (object == nullptr || object->type != OB_CURVES) {
124 return false;
125 }
126 if (check_editable) {
128 return false;
129 }
130 }
131 if (check_surface) {
132 Curves &curves = *static_cast<Curves *>(object->data);
133 if (curves.surface == nullptr || curves.surface->type != OB_MESH) {
134 CTX_wm_operator_poll_msg_set(C, "Curves must have a mesh surface object set");
135 return false;
136 }
137 }
138 if (check_edit_mode) {
139 if ((object->mode & OB_MODE_EDIT) == 0) {
140 return false;
141 }
142 }
143 return true;
144}
145
147{
148 return curves_poll_impl(C, true, false, true);
149}
150
152{
153 return curves_poll_impl(C, true, true, false);
154}
155
157{
158 return curves_poll_impl(C, false, true, false);
159}
160
162{
163 return curves_poll_impl(C, false, false, false);
164}
165
167{
168 return curves_poll_impl(C, false, false, false);
169}
170
172{
174 return false;
175 }
176 const Curves *curves_id = static_cast<const Curves *>(CTX_data_active_object(C)->data);
178 CTX_wm_operator_poll_msg_set(C, "Only available in point selection mode");
179 return false;
180 }
181 return true;
182}
183
185
186namespace convert_to_particle_system {
187
189 const MFace *mface,
190 const Span<int> possible_mface_indices,
191 const float3 &root_pos)
192{
193 BLI_assert(possible_mface_indices.size() >= 1);
194 if (possible_mface_indices.size() == 1) {
195 return possible_mface_indices.first();
196 }
197 /* Find the closest #MFace to #root_pos. */
198 int mface_i;
199 float best_distance_sq = FLT_MAX;
200 for (const int possible_mface_i : possible_mface_indices) {
201 const MFace &possible_mface = mface[possible_mface_i];
202 {
203 float3 point_in_triangle;
204 closest_on_tri_to_point_v3(point_in_triangle,
205 root_pos,
206 positions[possible_mface.v1],
207 positions[possible_mface.v2],
208 positions[possible_mface.v3]);
209 const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
210 if (distance_sq < best_distance_sq) {
211 best_distance_sq = distance_sq;
212 mface_i = possible_mface_i;
213 }
214 }
215 /* Optionally check the second triangle if the #MFace is a quad. */
216 if (possible_mface.v4) {
217 float3 point_in_triangle;
218 closest_on_tri_to_point_v3(point_in_triangle,
219 root_pos,
220 positions[possible_mface.v1],
221 positions[possible_mface.v3],
222 positions[possible_mface.v4]);
223 const float distance_sq = len_squared_v3v3(root_pos, point_in_triangle);
224 if (distance_sq < best_distance_sq) {
225 best_distance_sq = distance_sq;
226 mface_i = possible_mface_i;
227 }
228 }
229 }
230 return mface_i;
231}
232
237 const MFace &mface,
238 const float3 &position)
239{
240 float4 mface_weights;
241 if (mface.v4) {
242 float mface_positions_su[4][3];
243 copy_v3_v3(mface_positions_su[0], positions[mface.v1]);
244 copy_v3_v3(mface_positions_su[1], positions[mface.v2]);
245 copy_v3_v3(mface_positions_su[2], positions[mface.v3]);
246 copy_v3_v3(mface_positions_su[3], positions[mface.v4]);
247 interp_weights_poly_v3(mface_weights, mface_positions_su, 4, position);
248 }
249 else {
251 mface_weights, positions[mface.v1], positions[mface.v2], positions[mface.v3], position);
252 mface_weights[3] = 0.0f;
253 }
254 return mface_weights;
255}
256
257static void try_convert_single_object(Object &curves_ob,
258 Main &bmain,
259 Scene &scene,
260 bool *r_could_not_convert_some_curves)
261{
262 if (curves_ob.type != OB_CURVES) {
263 return;
264 }
265 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
266 CurvesGeometry &curves = curves_id.geometry.wrap();
267 if (curves_id.surface == nullptr) {
268 return;
269 }
270 Object &surface_ob = *curves_id.surface;
271 if (surface_ob.type != OB_MESH) {
272 return;
273 }
274 Mesh &surface_me = *static_cast<Mesh *>(surface_ob.data);
275
276 BVHTreeFromMesh surface_bvh;
277 BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_me, BVHTREE_FROM_CORNER_TRIS, 2);
278 BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
279
280 const Span<float3> positions_cu = curves.positions();
281 const Span<int> tri_faces = surface_me.corner_tri_faces();
282
283 if (tri_faces.is_empty()) {
284 *r_could_not_convert_some_curves = true;
285 }
286
287 const OffsetIndices<int> points_by_curve = curves.points_by_curve();
288 IndexMaskMemory memory;
289 const IndexMask multi_point_curves = IndexMask::from_predicate(
290 curves.curves_range(), GrainSize(4096), memory, [&](const int curve_i) {
291 return points_by_curve[curve_i].size() > 1;
292 });
293
294 const int hair_num = multi_point_curves.size();
295
296 if (hair_num == 0) {
297 return;
298 }
299
300 ParticleSystem *particle_system = nullptr;
301 LISTBASE_FOREACH (ParticleSystem *, psys, &surface_ob.particlesystem) {
302 if (STREQ(psys->name, curves_ob.id.name + 2)) {
303 particle_system = psys;
304 break;
305 }
306 }
307 if (particle_system == nullptr) {
308 ParticleSystemModifierData &psmd = *reinterpret_cast<ParticleSystemModifierData *>(
309 object_add_particle_system(&bmain, &scene, &surface_ob, curves_ob.id.name + 2));
310 particle_system = psmd.psys;
311 particle_system->part->draw_step = 3;
312 }
313
314 ParticleSettings &settings = *particle_system->part;
315
316 psys_free_particles(particle_system);
317 settings.type = PART_HAIR;
318 settings.totpart = 0;
319 psys_changed_type(&surface_ob, particle_system);
320
322 static_cast<ParticleData *>(MEM_calloc_arrayN(hair_num, sizeof(ParticleData), __func__)),
323 hair_num};
324
325 /* The old hair system still uses #MFace, so make sure those are available on the mesh. */
326 BKE_mesh_tessface_calc(&surface_me);
327
328 /* Prepare utility data structure to map hair roots to #MFace's. */
329 const Span<int> mface_to_poly_map{
330 static_cast<const int *>(CustomData_get_layer(&surface_me.fdata_legacy, CD_ORIGINDEX)),
331 surface_me.totface_legacy};
332 Array<Vector<int>> poly_to_mface_map(surface_me.faces_num);
333 for (const int mface_i : mface_to_poly_map.index_range()) {
334 const int face_i = mface_to_poly_map[mface_i];
335 poly_to_mface_map[face_i].append(mface_i);
336 }
337
338 /* Prepare transformation matrices. */
339 const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
340
341 const MFace *mfaces = (const MFace *)CustomData_get_layer(&surface_me.fdata_legacy, CD_MFACE);
342 const Span<float3> positions = surface_me.vert_positions();
343
344 multi_point_curves.foreach_index([&](const int curve_i, const int new_hair_i) {
345 const IndexRange points = points_by_curve[curve_i];
346
347 const float3 &root_pos_cu = positions_cu[points.first()];
348 const float3 root_pos_su = math::transform_point(transforms.curves_to_surface, root_pos_cu);
349
350 BVHTreeNearest nearest;
351 nearest.dist_sq = FLT_MAX;
353 surface_bvh.tree, root_pos_su, &nearest, surface_bvh.nearest_callback, &surface_bvh);
354 BLI_assert(nearest.index >= 0);
355
356 const int tri_i = nearest.index;
357 const int face_i = tri_faces[tri_i];
358
359 const int mface_i = find_mface_for_root_position(
360 positions, mfaces, poly_to_mface_map[face_i], root_pos_su);
361 const MFace &mface = mfaces[mface_i];
362
363 const float4 mface_weights = compute_mface_weights_for_position(positions, mface, root_pos_su);
364
365 ParticleData &particle = particles[new_hair_i];
366 const int num_keys = points.size();
367 MutableSpan<HairKey> hair_keys{
368 static_cast<HairKey *>(MEM_calloc_arrayN(num_keys, sizeof(HairKey), __func__)), num_keys};
369
370 particle.hair = hair_keys.data();
371 particle.totkey = hair_keys.size();
372 copy_v4_v4(particle.fuv, mface_weights);
373 particle.num = mface_i;
374 /* Not sure if there is a better way to initialize this. */
375 particle.num_dmcache = DMCACHE_NOTFOUND;
376
377 float4x4 hair_to_surface_mat;
379 &surface_ob, &surface_me, PART_FROM_FACE, &particle, hair_to_surface_mat.ptr());
380 /* In theory, #psys_mat_hair_to_object should handle this, but it doesn't right now. */
381 hair_to_surface_mat.location() = root_pos_su;
382 const float4x4 surface_to_hair_mat = math::invert(hair_to_surface_mat);
383
384 for (const int key_i : hair_keys.index_range()) {
385 const float3 &key_pos_cu = positions_cu[points[key_i]];
386 const float3 key_pos_su = math::transform_point(transforms.curves_to_surface, key_pos_cu);
387 const float3 key_pos_ha = math::transform_point(surface_to_hair_mat, key_pos_su);
388
389 HairKey &key = hair_keys[key_i];
390 copy_v3_v3(key.co, key_pos_ha);
391 const float key_fac = key_i / float(hair_keys.size() - 1);
392 key.time = 100.0f * key_fac;
393 key.weight = 1.0f - key_fac;
394 }
395 });
396
397 particle_system->particles = particles.data();
398 particle_system->totpart = particles.size();
399 particle_system->flag |= PSYS_EDITED;
400 particle_system->recalc |= ID_RECALC_PSYS_RESET;
401
404}
405
407{
408 Main &bmain = *CTX_data_main(C);
409 Scene &scene = *CTX_data_scene(C);
410
411 bool could_not_convert_some_curves = false;
412
413 Object &active_object = *CTX_data_active_object(C);
414 try_convert_single_object(active_object, bmain, scene, &could_not_convert_some_curves);
415
416 CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
417 if (curves_ob != &active_object) {
418 try_convert_single_object(*curves_ob, bmain, scene, &could_not_convert_some_curves);
419 }
420 }
422
423 if (could_not_convert_some_curves) {
425 RPT_INFO,
426 "Some curves could not be converted because they were not attached to the surface");
427 }
428
430
431 return OPERATOR_FINISHED;
432}
433
434} // namespace convert_to_particle_system
435
437{
438 ot->name = "Convert Curves to Particle System";
439 ot->idname = "CURVES_OT_convert_to_particle_system";
440 ot->description = "Add a new or update an existing hair particle system on the surface object";
441
444
446}
447
448namespace convert_from_particle_system {
449
451{
452 ParticleSettings &settings = *psys.part;
453 if (psys.part->type != PART_HAIR) {
454 return {};
455 }
456
457 const bool transfer_parents = (settings.draw & PART_DRAW_PARENT) || settings.childtype == 0;
458
459 const Span<ParticleCacheKey *> parents_cache{psys.pathcache, psys.totcached};
460 const Span<ParticleCacheKey *> children_cache{psys.childcache, psys.totchildcache};
461
462 int points_num = 0;
463 Vector<int> curve_offsets;
464 Vector<int> parents_to_transfer;
465 Vector<int> children_to_transfer;
466 if (transfer_parents) {
467 for (const int parent_i : parents_cache.index_range()) {
468 const int segments = parents_cache[parent_i]->segments;
469 if (segments <= 0) {
470 continue;
471 }
472 parents_to_transfer.append(parent_i);
473 curve_offsets.append(points_num);
474 points_num += segments + 1;
475 }
476 }
477 for (const int child_i : children_cache.index_range()) {
478 const int segments = children_cache[child_i]->segments;
479 if (segments <= 0) {
480 continue;
481 }
482 children_to_transfer.append(child_i);
483 curve_offsets.append(points_num);
484 points_num += segments + 1;
485 }
486 const int curves_num = parents_to_transfer.size() + children_to_transfer.size();
487 curve_offsets.append(points_num);
488 BLI_assert(curve_offsets.size() == curves_num + 1);
489 bke::CurvesGeometry curves(points_num, curves_num);
490 curves.offsets_for_write().copy_from(curve_offsets);
491
492 const float4x4 &object_to_world_mat = object.object_to_world();
493 const float4x4 world_to_object_mat = math::invert(object_to_world_mat);
494
495 MutableSpan<float3> positions = curves.positions_for_write();
496 const OffsetIndices points_by_curve = curves.points_by_curve();
497
498 const auto copy_hair_to_curves = [&](const Span<ParticleCacheKey *> hair_cache,
499 const Span<int> indices_to_transfer,
500 const int curve_index_offset) {
501 threading::parallel_for(indices_to_transfer.index_range(), 256, [&](const IndexRange range) {
502 for (const int i : range) {
503 const int hair_i = indices_to_transfer[i];
504 const int curve_i = i + curve_index_offset;
505 const IndexRange points = points_by_curve[curve_i];
506 const Span<ParticleCacheKey> keys{hair_cache[hair_i], points.size()};
507 for (const int key_i : keys.index_range()) {
508 const float3 key_pos_wo = keys[key_i].co;
509 positions[points[key_i]] = math::transform_point(world_to_object_mat, key_pos_wo);
510 }
511 }
512 });
513 };
514
515 if (transfer_parents) {
516 copy_hair_to_curves(parents_cache, parents_to_transfer, 0);
517 }
518 copy_hair_to_curves(children_cache, children_to_transfer, parents_to_transfer.size());
519
520 curves.update_curve_types();
521 curves.tag_topology_changed();
522 return curves;
523}
524
526{
527 Main &bmain = *CTX_data_main(C);
528 Scene &scene = *CTX_data_scene(C);
529 ViewLayer &view_layer = *CTX_data_view_layer(C);
530 Depsgraph &depsgraph = *CTX_data_depsgraph_pointer(C);
531 Object *ob_from_orig = object::context_active_object(C);
532 ParticleSystem *psys_orig = static_cast<ParticleSystem *>(
533 CTX_data_pointer_get_type(C, "particle_system", &RNA_ParticleSystem).data);
534 if (psys_orig == nullptr) {
535 psys_orig = psys_get_current(ob_from_orig);
536 }
537 if (psys_orig == nullptr) {
538 return OPERATOR_CANCELLED;
539 }
540 Object *ob_from_eval = DEG_get_evaluated_object(&depsgraph, ob_from_orig);
541 ParticleSystem *psys_eval = nullptr;
542 LISTBASE_FOREACH (ModifierData *, md, &ob_from_eval->modifiers) {
543 if (md->type != eModifierType_ParticleSystem) {
544 continue;
545 }
546 ParticleSystemModifierData *psmd = reinterpret_cast<ParticleSystemModifierData *>(md);
547 if (!STREQ(psmd->psys->name, psys_orig->name)) {
548 continue;
549 }
550 psys_eval = psmd->psys;
551 }
552
553 Object *ob_new = BKE_object_add(&bmain, &scene, &view_layer, OB_CURVES, psys_eval->name);
554 Curves *curves_id = static_cast<Curves *>(ob_new->data);
555 BKE_object_apply_mat4(ob_new, ob_from_orig->object_to_world().ptr(), true, false);
556 curves_id->geometry.wrap() = particles_to_curves(*ob_from_eval, *psys_eval);
557
560
561 return OPERATOR_FINISHED;
562}
563
568
569} // namespace convert_from_particle_system
570
572{
573 ot->name = "Convert Particle System to Curves";
574 ot->idname = "CURVES_OT_convert_from_particle_system";
575 ot->description = "Add a new curves object based on the current state of the particle system";
576
577 ot->poll = convert_from_particle_system::curves_convert_from_particle_system_poll;
578 ot->exec = convert_from_particle_system::curves_convert_from_particle_system_exec;
579
581}
582
583namespace snap_curves_to_surface {
584
585enum class AttachMode {
586 Nearest = 0,
587 Deform = 1,
588};
589
591 const Object &surface_ob,
592 const AttachMode attach_mode,
593 bool *r_invalid_uvs,
594 bool *r_missing_uvs)
595{
596 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
597 CurvesGeometry &curves = curves_id.geometry.wrap();
598
599 const Mesh &surface_mesh = *static_cast<const Mesh *>(surface_ob.data);
600 const Span<float3> surface_positions = surface_mesh.vert_positions();
601 const Span<int> corner_verts = surface_mesh.corner_verts();
602 const Span<int3> surface_corner_tris = surface_mesh.corner_tris();
603 VArraySpan<float2> surface_uv_map;
604 if (curves_id.surface_uv_map != nullptr) {
605 const bke::AttributeAccessor surface_attributes = surface_mesh.attributes();
606 surface_uv_map = *surface_attributes.lookup<float2>(curves_id.surface_uv_map,
607 bke::AttrDomain::Corner);
608 }
609
610 const OffsetIndices points_by_curve = curves.points_by_curve();
611 MutableSpan<float3> positions_cu = curves.positions_for_write();
612 MutableSpan<float2> surface_uv_coords = curves.surface_uv_coords_for_write();
613
614 const bke::CurvesSurfaceTransforms transforms{curves_ob, &surface_ob};
615
616 switch (attach_mode) {
617 case AttachMode::Nearest: {
618 BVHTreeFromMesh surface_bvh;
619 BKE_bvhtree_from_mesh_get(&surface_bvh, &surface_mesh, BVHTREE_FROM_CORNER_TRIS, 2);
620 BLI_SCOPED_DEFER([&]() { free_bvhtree_from_mesh(&surface_bvh); });
621
622 threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
623 for (const int curve_i : curves_range) {
624 const IndexRange points = points_by_curve[curve_i];
625 const int first_point_i = points.first();
626 const float3 old_first_point_pos_cu = positions_cu[first_point_i];
627 const float3 old_first_point_pos_su = math::transform_point(transforms.curves_to_surface,
628 old_first_point_pos_cu);
629
630 BVHTreeNearest nearest;
631 nearest.index = -1;
632 nearest.dist_sq = FLT_MAX;
633 BLI_bvhtree_find_nearest(surface_bvh.tree,
634 old_first_point_pos_su,
635 &nearest,
636 surface_bvh.nearest_callback,
637 &surface_bvh);
638 const int tri_index = nearest.index;
639 if (tri_index == -1) {
640 continue;
641 }
642
643 const float3 new_first_point_pos_su = nearest.co;
644 const float3 new_first_point_pos_cu = math::transform_point(transforms.surface_to_curves,
645 new_first_point_pos_su);
646 const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
647
648 for (float3 &pos_cu : positions_cu.slice(points)) {
649 pos_cu += pos_diff_cu;
650 }
651
652 if (!surface_uv_map.is_empty()) {
653 const int3 &tri = surface_corner_tris[tri_index];
654 const float3 bary_coords = bke::mesh_surface_sample::compute_bary_coord_in_triangle(
655 surface_positions, corner_verts, tri, new_first_point_pos_su);
656 const float2 uv = bke::mesh_surface_sample::sample_corner_attribute_with_bary_coords(
657 bary_coords, tri, surface_uv_map);
658 surface_uv_coords[curve_i] = uv;
659 }
660 }
661 });
662 break;
663 }
664 case AttachMode::Deform: {
665 if (surface_uv_map.is_empty()) {
666 *r_missing_uvs = true;
667 break;
668 }
670 ReverseUVSampler reverse_uv_sampler{surface_uv_map, surface_corner_tris};
671
672 threading::parallel_for(curves.curves_range(), 256, [&](const IndexRange curves_range) {
673 for (const int curve_i : curves_range) {
674 const IndexRange points = points_by_curve[curve_i];
675 const int first_point_i = points.first();
676 const float3 old_first_point_pos_cu = positions_cu[first_point_i];
677
678 const float2 uv = surface_uv_coords[curve_i];
679 ReverseUVSampler::Result lookup_result = reverse_uv_sampler.sample(uv);
680 if (lookup_result.type != ReverseUVSampler::ResultType::Ok) {
681 *r_invalid_uvs = true;
682 continue;
683 }
684
685 const int3 &tri = surface_corner_tris[lookup_result.tri_index];
686 const float3 &bary_coords = lookup_result.bary_weights;
687
688 const float3 &p0_su = surface_positions[corner_verts[tri[0]]];
689 const float3 &p1_su = surface_positions[corner_verts[tri[1]]];
690 const float3 &p2_su = surface_positions[corner_verts[tri[2]]];
691
692 float3 new_first_point_pos_su;
693 interp_v3_v3v3v3(new_first_point_pos_su, p0_su, p1_su, p2_su, bary_coords);
694 const float3 new_first_point_pos_cu = math::transform_point(transforms.surface_to_curves,
695 new_first_point_pos_su);
696
697 const float3 pos_diff_cu = new_first_point_pos_cu - old_first_point_pos_cu;
698 for (float3 &pos_cu : positions_cu.slice(points)) {
699 pos_cu += pos_diff_cu;
700 }
701 }
702 });
703 break;
704 }
705 }
706
707 curves.tag_positions_changed();
709}
710
712{
713 const AttachMode attach_mode = static_cast<AttachMode>(RNA_enum_get(op->ptr, "attach_mode"));
714
715 bool found_invalid_uvs = false;
716 bool found_missing_uvs = false;
717
718 CTX_DATA_BEGIN (C, Object *, curves_ob, selected_objects) {
719 if (curves_ob->type != OB_CURVES) {
720 continue;
721 }
722 Curves &curves_id = *static_cast<Curves *>(curves_ob->data);
723 if (curves_id.surface == nullptr) {
724 continue;
725 }
726 if (curves_id.surface->type != OB_MESH) {
727 continue;
728 }
730 *curves_ob, *curves_id.surface, attach_mode, &found_invalid_uvs, &found_missing_uvs);
731 }
733
734 if (found_missing_uvs) {
736 RPT_ERROR,
737 "Curves do not have attachment information that can be used for deformation");
738 }
739 if (found_invalid_uvs) {
740 BKE_report(op->reports, RPT_INFO, "Could not snap some curves to the surface");
741 }
742
743 /* Refresh the entire window to also clear eventual modifier and nodes editor warnings. */
744 WM_event_add_notifier(C, NC_WINDOW, nullptr);
745
746 return OPERATOR_FINISHED;
747}
748
749} // namespace snap_curves_to_surface
750
752{
753 using namespace snap_curves_to_surface;
754
755 ot->name = "Snap Curves to Surface";
756 ot->idname = "CURVES_OT_snap_curves_to_surface";
757 ot->description = "Move curves so that the first point is exactly on the surface mesh";
758
760 ot->exec = snap_curves_to_surface_exec;
761
763
764 static const EnumPropertyItem attach_mode_items[] = {
765 {int(AttachMode::Nearest),
766 "NEAREST",
767 0,
768 "Nearest",
769 "Find the closest point on the surface for the root point of every curve and move the root "
770 "there"},
771 {int(AttachMode::Deform),
772 "DEFORM",
773 0,
774 "Deform",
775 "Re-attach curves to a deformed surface using the existing attachment information. This "
776 "only works when the topology of the surface mesh has not changed"},
777 {0, nullptr, 0, nullptr, nullptr},
778 };
779
781 "attach_mode",
782 attach_mode_items,
783 int(AttachMode::Nearest),
784 "Attach Mode",
785 "How to find the point on the surface to attach to");
786}
787
788namespace set_selection_domain {
789
791{
792 const bke::AttrDomain domain = bke::AttrDomain(RNA_enum_get(op->ptr, "domain"));
793
794 for (Curves *curves_id : get_unique_editable_curves(*C)) {
795 if (bke::AttrDomain(curves_id->selection_domain) == domain) {
796 continue;
797 }
798
799 curves_id->selection_domain = char(domain);
800
801 CurvesGeometry &curves = curves_id->geometry.wrap();
802 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
803 if (curves.points_num() == 0) {
804 continue;
805 }
806
807 /* Adding and removing attributes with the C++ API doesn't affect the active attribute index.
808 * In order to make the active attribute consistent before and after the change, save the name
809 * and reset the active item afterwards.
810 *
811 * This would be unnecessary if the active attribute were stored as a string on the ID. */
812 std::string active_attribute;
813 AttributeOwner owner = AttributeOwner::from_id(&curves_id->id);
814 const CustomDataLayer *layer = BKE_attributes_active_get(owner);
815 if (layer) {
816 active_attribute = layer->name;
817 }
818 for (const StringRef selection_name : get_curves_selection_attribute_names(curves)) {
819 if (const GVArray src = *attributes.lookup(selection_name, domain)) {
820 const CPPType &type = src.type();
821 void *dst = MEM_malloc_arrayN(attributes.domain_size(domain), type.size(), __func__);
822 src.materialize(dst);
823
824 attributes.remove(selection_name);
825 if (!attributes.add(selection_name,
826 domain,
827 bke::cpp_type_to_custom_data_type(type),
829 {
830 MEM_freeN(dst);
831 }
832 }
833 }
834 if (!active_attribute.empty()) {
835 BKE_attributes_active_set(owner, active_attribute.c_str());
836 }
837
838 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
839 * attribute for now. */
840 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
841 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
842 }
843
845
846 return OPERATOR_FINISHED;
847}
848
849} // namespace set_selection_domain
850
852{
853 PropertyRNA *prop;
854
855 ot->name = "Set Select Mode";
856 ot->idname = __func__;
857 ot->description = "Change the mode used for selection masking in curves sculpt mode";
858
859 ot->exec = set_selection_domain::curves_set_selection_domain_exec;
861
863
864 ot->prop = prop = RNA_def_enum(
865 ot->srna, "domain", rna_enum_attribute_curves_domain_items, 0, "Domain", "");
867}
868
869static bool has_anything_selected(const Span<Curves *> curves_ids)
870{
871 return std::any_of(curves_ids.begin(), curves_ids.end(), [](const Curves *curves_id) {
872 return has_anything_selected(curves_id->geometry.wrap());
873 });
874}
875
877{
878 int action = RNA_enum_get(op->ptr, "action");
879
881
882 if (action == SEL_TOGGLE) {
883 action = has_anything_selected(unique_curves) ? SEL_DESELECT : SEL_SELECT;
884 }
885
886 for (Curves *curves_id : unique_curves) {
887 /* (De)select all the curves. */
888 select_all(curves_id->geometry.wrap(), bke::AttrDomain(curves_id->selection_domain), action);
889
890 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
891 * attribute for now. */
892 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
893 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
894 }
895
896 return OPERATOR_FINISHED;
897}
898
900{
901 ot->name = "(De)select All";
902 ot->idname = "CURVES_OT_select_all";
903 ot->description = "(De)select all control points";
904
907
909
911}
912
914{
915 VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C);
916
917 const int seed = RNA_int_get(op->ptr, "seed");
918 const float probability = RNA_float_get(op->ptr, "probability");
919
920 for (Curves *curves_id : unique_curves) {
921 CurvesGeometry &curves = curves_id->geometry.wrap();
922 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id->selection_domain);
923 const int domain_size = curves.attributes().domain_size(selection_domain);
924
925 IndexMaskMemory memory;
926 const IndexMask inv_random_elements = random_mask(
927 curves, selection_domain, seed, probability, memory)
928 .complement(IndexRange(domain_size), memory);
929
930 const bool was_anything_selected = has_anything_selected(curves);
932 curves, selection_domain, CD_PROP_BOOL);
933 if (!was_anything_selected) {
934 curves::fill_selection_true(selection.span);
935 }
936
937 curves::fill_selection_false(selection.span, inv_random_elements);
938 selection.finish();
939
940 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
941 * attribute for now. */
942 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
943 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
944 }
945 return OPERATOR_FINISHED;
946}
947
948static void select_random_ui(bContext * /*C*/, wmOperator *op)
949{
950 uiLayout *layout = op->layout;
951
952 uiItemR(layout, op->ptr, "seed", UI_ITEM_NONE, nullptr, ICON_NONE);
953 uiItemR(layout, op->ptr, "probability", UI_ITEM_R_SLIDER, IFACE_("Probability"), ICON_NONE);
954}
955
957{
958 ot->name = "Select Random";
959 ot->idname = __func__;
960 ot->description = "Randomizes existing selection or create new random selection";
961
963 ot->poll = curves::editable_curves_poll;
965
967
969 "seed",
970 0,
971 INT32_MIN,
972 INT32_MAX,
973 "Seed",
974 "Source of randomness",
975 INT32_MIN,
976 INT32_MAX);
978 "probability",
979 0.5f,
980 0.0f,
981 1.0f,
982 "Probability",
983 "Chance of every point or curve being included in the selection",
984 0.0f,
985 1.0f);
986}
987
989{
990 VectorSet<Curves *> unique_curves = curves::get_unique_editable_curves(*C);
991 const int amount_start = RNA_int_get(op->ptr, "amount_start");
992 const int amount_end = RNA_int_get(op->ptr, "amount_end");
993
994 for (Curves *curves_id : unique_curves) {
995 CurvesGeometry &curves = curves_id->geometry.wrap();
996
997 IndexMaskMemory memory;
998 const IndexMask inverted_end_points_mask = end_points(
999 curves, amount_start, amount_end, true, memory);
1000
1001 const bool was_anything_selected = has_anything_selected(curves);
1003 curves, bke::AttrDomain::Point, CD_PROP_BOOL);
1004 if (!was_anything_selected) {
1005 fill_selection_true(selection.span);
1006 }
1007
1008 if (selection.span.type().is<bool>()) {
1009 index_mask::masked_fill(selection.span.typed<bool>(), false, inverted_end_points_mask);
1010 }
1011 if (selection.span.type().is<float>()) {
1012 index_mask::masked_fill(selection.span.typed<float>(), 0.0f, inverted_end_points_mask);
1013 }
1014 selection.finish();
1015
1016 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
1017 * attribute for now. */
1018 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1019 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1020 }
1021
1022 return OPERATOR_FINISHED;
1023}
1024
1025static void select_ends_ui(bContext * /*C*/, wmOperator *op)
1026{
1027 uiLayout *layout = op->layout;
1028
1029 uiLayoutSetPropSep(layout, true);
1030
1031 uiLayout *col = uiLayoutColumn(layout, true);
1033 uiItemR(col, op->ptr, "amount_start", UI_ITEM_NONE, IFACE_("Amount Start"), ICON_NONE);
1034 uiItemR(col, op->ptr, "amount_end", UI_ITEM_NONE, IFACE_("End"), ICON_NONE);
1035}
1036
1038{
1039 ot->name = "Select Ends";
1040 ot->idname = __func__;
1041 ot->description = "Select end points of curves";
1042
1044 ot->ui = select_ends_ui;
1046
1048
1050 "amount_start",
1051 0,
1052 0,
1053 INT32_MAX,
1054 "Amount Front",
1055 "Number of points to select from the front",
1056 0,
1057 INT32_MAX);
1059 "amount_end",
1060 1,
1061 0,
1062 INT32_MAX,
1063 "Amount Back",
1064 "Number of points to select from the back",
1065 0,
1066 INT32_MAX);
1067}
1068
1070{
1072 for (Curves *curves_id : unique_curves) {
1073 CurvesGeometry &curves = curves_id->geometry.wrap();
1074 select_linked(curves);
1075 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
1076 * attribute for now. */
1077 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1078 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1079 }
1080
1081 return OPERATOR_FINISHED;
1082}
1083
1085{
1086 ot->name = "Select Linked";
1087 ot->idname = __func__;
1088 ot->description = "Select all points in curves with any point selection";
1089
1092
1094}
1095
1096static int select_more_exec(bContext *C, wmOperator * /*op*/)
1097{
1099 for (Curves *curves_id : unique_curves) {
1100 CurvesGeometry &curves = curves_id->geometry.wrap();
1101 select_adjacent(curves, false);
1102 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
1103 * attribute for now. */
1104 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1105 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1106 }
1107
1108 return OPERATOR_FINISHED;
1109}
1110
1112{
1113 ot->name = "Select More";
1114 ot->idname = __func__;
1115 ot->description = "Grow the selection by one point";
1116
1119
1121}
1122
1123static int select_less_exec(bContext *C, wmOperator * /*op*/)
1124{
1126 for (Curves *curves_id : unique_curves) {
1127 CurvesGeometry &curves = curves_id->geometry.wrap();
1128 select_adjacent(curves, true);
1129 /* Use #ID_RECALC_GEOMETRY instead of #ID_RECALC_SELECT because it is handled as a generic
1130 * attribute for now. */
1131 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1132 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1133 }
1134
1135 return OPERATOR_FINISHED;
1136}
1137
1139{
1140 ot->name = "Select Less";
1141 ot->idname = __func__;
1142 ot->description = "Shrink the selection by one point";
1143
1146
1148}
1149
1150namespace surface_set {
1151
1153{
1154 const Object *object = CTX_data_active_object(C);
1155 if (object == nullptr) {
1156 return false;
1157 }
1158 if (object->type != OB_MESH) {
1159 return false;
1160 }
1161 return true;
1162}
1163
1165{
1166 Main *bmain = CTX_data_main(C);
1167 Scene *scene = CTX_data_scene(C);
1168
1169 Object &new_surface_ob = *CTX_data_active_object(C);
1170
1171 Mesh &new_surface_mesh = *static_cast<Mesh *>(new_surface_ob.data);
1172 const char *new_uv_map_name = CustomData_get_active_layer_name(&new_surface_mesh.corner_data,
1174
1175 CTX_DATA_BEGIN (C, Object *, selected_ob, selected_objects) {
1176 if (selected_ob->type != OB_CURVES) {
1177 continue;
1178 }
1179 Object &curves_ob = *selected_ob;
1180 Curves &curves_id = *static_cast<Curves *>(curves_ob.data);
1181
1182 MEM_SAFE_FREE(curves_id.surface_uv_map);
1183 if (new_uv_map_name != nullptr) {
1184 curves_id.surface_uv_map = BLI_strdup(new_uv_map_name);
1185 }
1186
1187 bool missing_uvs;
1188 bool invalid_uvs;
1189 snap_curves_to_surface::snap_curves_to_surface_exec_object(
1190 curves_ob,
1191 new_surface_ob,
1192 snap_curves_to_surface::AttachMode::Nearest,
1193 &invalid_uvs,
1194 &missing_uvs);
1195
1196 /* Add deformation modifier if necessary. */
1198
1199 curves_id.surface = &new_surface_ob;
1200 object::parent_set(op->reports,
1201 C,
1202 scene,
1203 &curves_ob,
1204 &new_surface_ob,
1205 object::PAR_OBJECT,
1206 false,
1207 true,
1208 nullptr);
1209
1211 WM_event_add_notifier(C, NC_GEOM | ND_DATA, &curves_id);
1212 WM_event_add_notifier(C, NC_NODE | NA_ADDED, nullptr);
1213
1214 /* Required for deformation. */
1216 DEG_id_tag_update(&new_surface_ob.id, ID_RECALC_GEOMETRY);
1217 }
1219
1221
1222 return OPERATOR_FINISHED;
1223}
1224
1225} // namespace surface_set
1226
1228{
1229 ot->name = "Set Curves Surface Object";
1230 ot->idname = __func__;
1231 ot->description =
1232 "Use the active object as surface for selected curves objects and set it as the parent";
1233
1234 ot->exec = surface_set::surface_set_exec;
1235 ot->poll = surface_set::surface_set_poll;
1236
1238}
1239
1240namespace curves_delete {
1241
1242static int delete_exec(bContext *C, wmOperator * /*op*/)
1243{
1244 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1245 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1246 if (remove_selection(curves, bke::AttrDomain(curves_id->selection_domain))) {
1247 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1248 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1249 }
1250 }
1251
1252 return OPERATOR_FINISHED;
1253}
1254
1255} // namespace curves_delete
1256
1258{
1259 ot->name = "Delete";
1260 ot->idname = __func__;
1261 ot->description = "Remove selected control points or curves";
1262
1263 ot->exec = curves_delete::delete_exec;
1265
1267}
1268
1269namespace curves_duplicate {
1270
1271static int delete_exec(bContext *C, wmOperator * /*op*/)
1272{
1273 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1274 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1275 IndexMaskMemory memory;
1276 switch (bke::AttrDomain(curves_id->selection_domain)) {
1277 case bke::AttrDomain::Point:
1278 duplicate_points(curves, retrieve_selected_points(*curves_id, memory));
1279 break;
1280 case bke::AttrDomain::Curve:
1281 duplicate_curves(curves, retrieve_selected_curves(*curves_id, memory));
1282 break;
1283 default:
1285 break;
1286 }
1287 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1288 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1289 }
1290 return OPERATOR_FINISHED;
1291}
1292
1293} // namespace curves_duplicate
1294
1296{
1297 ot->name = "Duplicate";
1298 ot->idname = __func__;
1299 ot->description = "Copy selected points or curves";
1300
1301 ot->exec = curves_duplicate::delete_exec;
1303
1305}
1306
1307namespace clear_tilt {
1308
1309static int exec(bContext *C, wmOperator * /*op*/)
1310{
1311 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1312 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1313 IndexMaskMemory memory;
1314 const IndexMask selection = retrieve_selected_points(*curves_id, memory);
1315 if (selection.is_empty()) {
1316 continue;
1317 }
1318
1319 if (selection.size() == curves.points_num()) {
1320 curves.attributes_for_write().remove("tilt");
1321 }
1322 else {
1323 index_mask::masked_fill(curves.tilt_for_write(), 0.0f, selection);
1324 }
1325
1326 curves.tag_normals_changed();
1327 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1328 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1329 }
1330 return OPERATOR_FINISHED;
1331}
1332
1333} // namespace clear_tilt
1334
1336{
1337 ot->name = "Clear Tilt";
1338 ot->idname = __func__;
1339 ot->description = "Clear the tilt of selected control points";
1340
1341 ot->exec = clear_tilt::exec;
1343
1345}
1346
1347namespace cyclic_toggle {
1348
1349static int exec(bContext *C, wmOperator * /*op*/)
1350{
1351 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1352 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1353 IndexMaskMemory memory;
1354 const IndexMask selection = retrieve_selected_curves(*curves_id, memory);
1355 if (selection.is_empty()) {
1356 continue;
1357 }
1358
1359 bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1360
1361 bke::SpanAttributeWriter<bool> cyclic = attributes.lookup_or_add_for_write_span<bool>(
1362 "cyclic", bke::AttrDomain::Curve);
1363 selection.foreach_index(GrainSize(4096),
1364 [&](const int i) { cyclic.span[i] = !cyclic.span[i]; });
1365 cyclic.finish();
1366
1367 if (!cyclic.span.as_span().contains(true)) {
1368 attributes.remove("cyclic");
1369 }
1370
1371 curves.calculate_bezier_auto_handles();
1372
1373 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1374 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1375 }
1376 return OPERATOR_FINISHED;
1377}
1378
1379} // namespace cyclic_toggle
1380
1382{
1383 ot->name = "Toggle Cyclic";
1384 ot->idname = __func__;
1385 ot->description = "Make active curve closed/opened loop";
1386
1387 ot->exec = cyclic_toggle::exec;
1389
1391}
1392
1393namespace curve_type_set {
1394
1395static int exec(bContext *C, wmOperator *op)
1396{
1397 const CurveType dst_type = CurveType(RNA_enum_get(op->ptr, "type"));
1398 const bool use_handles = RNA_boolean_get(op->ptr, "use_handles");
1399
1400 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1401 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1402 IndexMaskMemory memory;
1403 const IndexMask selection = retrieve_selected_curves(*curves_id, memory);
1404 if (selection.is_empty()) {
1405 continue;
1406 }
1407
1409 options.convert_bezier_handles_to_poly_points = use_handles;
1410 options.convert_bezier_handles_to_catmull_rom_points = use_handles;
1411 options.keep_bezier_shape_as_nurbs = use_handles;
1412 options.keep_catmull_rom_shape_as_nurbs = use_handles;
1413
1414 curves = geometry::convert_curves(curves, selection, dst_type, {}, options);
1415
1416 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1417 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1418 }
1419 return OPERATOR_FINISHED;
1420}
1421
1422} // namespace curve_type_set
1423
1425{
1426 ot->name = "Set Curve Type";
1427 ot->idname = __func__;
1428 ot->description = "Set type of selected curves";
1429
1430 ot->exec = curve_type_set::exec;
1432
1434
1435 ot->prop = RNA_def_enum(
1436 ot->srna, "type", rna_enum_curves_type_items, CURVE_TYPE_POLY, "Type", "Curve type");
1437
1439 "use_handles",
1440 false,
1441 "Handles",
1442 "Take handle information into account in the conversion");
1443}
1444
1445namespace switch_direction {
1446
1447static int exec(bContext *C, wmOperator * /*op*/)
1448{
1449 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1450 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1451 IndexMaskMemory memory;
1452 const IndexMask selection = retrieve_selected_curves(*curves_id, memory);
1453 if (selection.is_empty()) {
1454 continue;
1455 }
1456
1457 curves.reverse_curves(selection);
1458
1459 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1460 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1461 }
1462 return OPERATOR_FINISHED;
1463}
1464
1465} // namespace switch_direction
1466
1468{
1469 ot->name = "Switch Direction";
1470 ot->idname = __func__;
1471 ot->description = "Reverse the direction of the selected curves";
1472
1473 ot->exec = switch_direction::exec;
1475
1477}
1478
1479namespace subdivide {
1480
1481static int exec(bContext *C, wmOperator *op)
1482{
1483 const int number_cuts = RNA_int_get(op->ptr, "number_cuts");
1484
1485 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1486 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1487 const int points_num = curves.points_num();
1488 IndexMaskMemory memory;
1489 const IndexMask points_selection = retrieve_selected_points(*curves_id, memory);
1490 if (points_selection.is_empty()) {
1491 continue;
1492 }
1493
1494 Array<bool> points_selection_span(points_num);
1495 points_selection.to_bools(points_selection_span);
1496
1497 Array<int> segment_cuts(points_num, number_cuts);
1498
1499 const OffsetIndices points_by_curve = curves.points_by_curve();
1500 threading::parallel_for(points_by_curve.index_range(), 512, [&](const IndexRange range) {
1501 for (const int curve_i : range) {
1502 const IndexRange points = points_by_curve[curve_i];
1503 if (points.size() <= 1) {
1504 continue;
1505 }
1506 for (const int point_i : points.drop_back(1)) {
1507 if (!points_selection_span[point_i] || !points_selection_span[point_i + 1]) {
1508 segment_cuts[point_i] = 0;
1509 }
1510 }
1511 /* Cyclic segment. Doesn't matter if it is computed even if the curve is not cyclic. */
1512 if (!points_selection_span[points.last()] || !points_selection_span[points.first()]) {
1513 segment_cuts[points.last()] = 0;
1514 }
1515 }
1516 });
1517
1518 curves = geometry::subdivide_curves(
1519 curves, curves.curves_range(), VArray<int>::ForSpan(segment_cuts), {});
1520
1521 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1522 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1523 }
1524 return OPERATOR_FINISHED;
1525}
1526
1527} // namespace subdivide
1528
1530{
1531 ot->name = "Subdivide";
1532 ot->idname = __func__;
1533 ot->description = "Subdivide selected curve segments";
1534
1535 ot->exec = subdivide::exec;
1537
1539
1540 PropertyRNA *prop;
1541 prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 1000, "Number of Cuts", "", 1, 10);
1542 /* Avoid re-using last value because it can cause an unexpectedly high number of subdivisions. */
1544}
1545
1548 Curves &curves_id,
1549 CurvesGeometry new_curves,
1550 wmOperator &op)
1551{
1552 const int new_points_num = new_curves.points_num();
1553 const int new_curves_num = new_curves.curves_num();
1554
1555 /* Create geometry sets so that generic join code can be used. */
1556 bke::GeometrySet old_geometry = bke::GeometrySet::from_curves(
1557 &curves_id, bke::GeometryOwnershipType::ReadOnly);
1558 bke::GeometrySet new_geometry = bke::GeometrySet::from_curves(
1559 bke::curves_new_nomain(std::move(new_curves)));
1560
1561 /* Transform primitive according to settings. */
1562 float3 location;
1563 float3 rotation;
1564 float3 scale;
1565 object::add_generic_get_opts(C, &op, 'Z', location, rotation, scale, nullptr, nullptr, nullptr);
1567 location, math::EulerXYZ(rotation), scale);
1568 geometry::transform_geometry(new_geometry, transform);
1569
1570 bke::GeometrySet joined_geometry = geometry::join_geometries({old_geometry, new_geometry}, {});
1571 Curves *joined_curves_id = joined_geometry.get_curves_for_write();
1572 CurvesGeometry &dst_curves = curves_id.geometry.wrap();
1573 dst_curves = std::move(joined_curves_id->geometry.wrap());
1574
1575 /* Only select the new curves. */
1576 const bke::AttrDomain selection_domain = bke::AttrDomain(curves_id.selection_domain);
1577 const int new_element_num = selection_domain == bke::AttrDomain::Point ? new_points_num :
1578 new_curves_num;
1580 dst_curves, selection_domain, [&](bke::GSpanAttributeWriter &selection) {
1581 fill_selection_false(selection.span.drop_back(new_element_num));
1582 fill_selection_true(selection.span.take_back(new_element_num));
1583 });
1584
1585 dst_curves.tag_topology_changed();
1586}
1587
1588namespace add_circle {
1589
1591{
1592 CurvesGeometry curves{4, 1};
1593
1594 MutableSpan<int> offsets = curves.offsets_for_write();
1595 offsets[0] = 0;
1596 offsets[1] = 4;
1597
1598 curves.fill_curve_types(CURVE_TYPE_BEZIER);
1599 curves.cyclic_for_write().fill(true);
1600 curves.handle_types_left_for_write().fill(BEZIER_HANDLE_AUTO);
1601 curves.handle_types_right_for_write().fill(BEZIER_HANDLE_AUTO);
1602 curves.resolution_for_write().fill(12);
1603
1604 MutableSpan<float3> positions = curves.positions_for_write();
1605 positions[0] = float3(-radius, 0, 0);
1606 positions[1] = float3(0, radius, 0);
1607 positions[2] = float3(radius, 0, 0);
1608 positions[3] = float3(0, -radius, 0);
1609
1610 /* Ensure these attributes exist. */
1611 curves.handle_positions_left_for_write();
1612 curves.handle_positions_right_for_write();
1613
1614 curves.calculate_bezier_auto_handles();
1615
1616 return curves;
1617}
1618
1619static int exec(bContext *C, wmOperator *op)
1620{
1621 Object *object = CTX_data_edit_object(C);
1622 Curves *active_curves_id = static_cast<Curves *>(object->data);
1623
1624 const float radius = RNA_float_get(op->ptr, "radius");
1625 append_primitive_curve(C, *active_curves_id, generate_circle_primitive(radius), *op);
1626
1627 DEG_id_tag_update(&active_curves_id->id, ID_RECALC_GEOMETRY);
1628 WM_event_add_notifier(C, NC_GEOM | ND_DATA, active_curves_id);
1629 return OPERATOR_FINISHED;
1630}
1631
1632} // namespace add_circle
1633
1635{
1636 ot->name = "Add Circle";
1637 ot->idname = __func__;
1638 ot->description = "Add new circle curve";
1639
1640 ot->exec = add_circle::exec;
1642
1644
1645 object::add_unit_props_radius(ot);
1646 object::add_generic_props(ot, true);
1647}
1648
1649namespace add_bezier {
1650
1652{
1653 CurvesGeometry curves{2, 1};
1654
1655 MutableSpan<int> offsets = curves.offsets_for_write();
1656 offsets[0] = 0;
1657 offsets[1] = 2;
1658
1659 curves.fill_curve_types(CURVE_TYPE_BEZIER);
1660 curves.handle_types_left_for_write().fill(BEZIER_HANDLE_ALIGN);
1661 curves.handle_types_right_for_write().fill(BEZIER_HANDLE_ALIGN);
1662 curves.resolution_for_write().fill(12);
1663
1664 MutableSpan<float3> positions = curves.positions_for_write();
1665 MutableSpan<float3> left_handles = curves.handle_positions_left_for_write();
1666 MutableSpan<float3> right_handles = curves.handle_positions_right_for_write();
1667
1668 left_handles[0] = float3(-1.5f, -0.5, 0) * radius;
1669 positions[0] = float3(-1.0f, 0, 0) * radius;
1670 right_handles[0] = float3(-0.5f, 0.5f, 0) * radius;
1671
1672 left_handles[1] = float3(0, 0, 0) * radius;
1673 positions[1] = float3(1.0f, 0, 0) * radius;
1674 right_handles[1] = float3(2.0f, 0, 0) * radius;
1675
1676 return curves;
1677}
1678
1679static int exec(bContext *C, wmOperator *op)
1680{
1681 Object *object = CTX_data_edit_object(C);
1682 Curves *active_curves_id = static_cast<Curves *>(object->data);
1683
1684 const float radius = RNA_float_get(op->ptr, "radius");
1685 append_primitive_curve(C, *active_curves_id, generate_bezier_primitive(radius), *op);
1686
1687 DEG_id_tag_update(&active_curves_id->id, ID_RECALC_GEOMETRY);
1688 WM_event_add_notifier(C, NC_GEOM | ND_DATA, active_curves_id);
1689 return OPERATOR_FINISHED;
1690}
1691
1692} // namespace add_bezier
1693
1695{
1696 ot->name = "Add Bezier";
1697 ot->idname = __func__;
1698 ot->description = "Add new bezier curve";
1699
1700 ot->exec = add_bezier::exec;
1702
1704
1705 object::add_unit_props_radius(ot);
1706 object::add_generic_props(ot, true);
1707}
1708
1709namespace set_handle_type {
1710
1711static int exec(bContext *C, wmOperator *op)
1712{
1713 const HandleType dst_handle_type = HandleType(RNA_enum_get(op->ptr, "type"));
1714
1715 for (Curves *curves_id : get_unique_editable_curves(*C)) {
1716 bke::CurvesGeometry &curves = curves_id->geometry.wrap();
1717 const bke::MutableAttributeAccessor attributes = curves.attributes_for_write();
1718
1719 const VArraySpan<bool> selection = *attributes.lookup_or_default<bool>(
1720 ".selection", bke::AttrDomain::Point, true);
1721 const VArraySpan<bool> selection_left = *attributes.lookup_or_default<bool>(
1722 ".selection_handle_left", bke::AttrDomain::Point, true);
1723 const VArraySpan<bool> selection_right = *attributes.lookup_or_default<bool>(
1724 ".selection_handle_right", bke::AttrDomain::Point, true);
1725
1726 MutableSpan<int8_t> handle_types_left = curves.handle_types_left_for_write();
1727 MutableSpan<int8_t> handle_types_right = curves.handle_types_right_for_write();
1728
1729 threading::parallel_for(curves.points_range(), 4096, [&](const IndexRange range) {
1730 for (const int point_i : range) {
1731 if (selection_left[point_i] || selection[point_i]) {
1732 handle_types_left[point_i] = int8_t(dst_handle_type);
1733 }
1734 if (selection_right[point_i] || selection[point_i]) {
1735 handle_types_right[point_i] = int8_t(dst_handle_type);
1736 }
1737 }
1738 });
1739
1740 curves.calculate_bezier_auto_handles();
1741 curves.tag_topology_changed();
1742
1743 DEG_id_tag_update(&curves_id->id, ID_RECALC_GEOMETRY);
1744 WM_event_add_notifier(C, NC_GEOM | ND_DATA, curves_id);
1745 }
1746 return OPERATOR_FINISHED;
1747}
1748
1749} // namespace set_handle_type
1750
1752{
1753 ot->name = "Set Handle Type";
1754 ot->idname = __func__;
1755 ot->description = "Set the handle type for bezier curves";
1756
1758 ot->exec = set_handle_type::exec;
1760
1762
1763 ot->prop = RNA_def_enum(
1764 ot->srna, "type", rna_enum_curves_handle_type_items, CURVE_TYPE_POLY, "Type", nullptr);
1765}
1766
1794
1796{
1798 wmOperatorTypeMacro *otmacro;
1799
1800 /* Duplicate + Move = Interactively place newly duplicated strokes */
1801 ot = WM_operatortype_append_macro("CURVES_OT_duplicate_move",
1802 "Duplicate",
1803 "Make copies of selected elements and move them",
1805 WM_operatortype_macro_define(ot, "CURVES_OT_duplicate");
1806 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1807 RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
1808 RNA_boolean_set(otmacro->ptr, "mirror", false);
1809
1810 /* Extrude + Move */
1811 ot = WM_operatortype_append_macro("CURVES_OT_extrude_move",
1812 "Extrude Curve and Move",
1813 "Extrude curve and move result",
1815 WM_operatortype_macro_define(ot, "CURVES_OT_extrude");
1816 otmacro = WM_operatortype_macro_define(ot, "TRANSFORM_OT_translate");
1817 RNA_boolean_set(otmacro->ptr, "use_proportional_edit", false);
1818 RNA_boolean_set(otmacro->ptr, "mirror", false);
1819}
1820
1822{
1823 /* Only set in editmode curves, by space_view3d listener. */
1824 wmKeyMap *keymap = WM_keymap_ensure(keyconf, "Curves", SPACE_EMPTY, RGN_TYPE_WINDOW);
1826}
1827
1828} // namespace blender::ed::curves
void BKE_attributes_active_set(AttributeOwner &owner, const char *name)
Definition attribute.cc:817
struct CustomDataLayer * BKE_attributes_active_get(AttributeOwner &owner)
Definition attribute.cc:781
void free_bvhtree_from_mesh(BVHTreeFromMesh *data)
Definition bvhutils.cc:1160
BVHTree * BKE_bvhtree_from_mesh_get(BVHTreeFromMesh *data, const Mesh *mesh, BVHCacheType bvh_cache_type, int tree_type)
Definition bvhutils.cc:899
@ BVHTREE_FROM_CORNER_TRIS
#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
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:2456
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:3851
void psys_free_particles(struct ParticleSystem *psys)
Definition particle.cc:929
void psys_changed_type(struct Object *ob, struct ParticleSystem *psys)
struct ParticleSystem * psys_get_current(struct Object *ob)
Definition particle.cc:534
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition report.cc:125
#define BLI_assert_unreachable()
Definition BLI_assert.h:97
#define BLI_assert(a)
Definition BLI_assert.h:50
int BLI_bvhtree_find_nearest(const BVHTree *tree, const float co[3], BVHTreeNearest *nearest, BVHTree_NearestPointCallback callback, void *userdata)
A KD-tree for nearest neighbor search.
#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])
#define BLI_SCOPED_DEFER(function_to_defer)
char * BLI_strdup(const char *str) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC
Definition string.c:40
#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)
Object * DEG_get_evaluated_object(const Depsgraph *depsgraph, Object *object)
@ ID_RECALC_TRANSFORM
Definition DNA_ID.h:1021
@ ID_RECALC_SYNC_TO_EVAL
Definition DNA_ID.h:1085
@ ID_RECALC_PSYS_RESET
Definition DNA_ID.h:1050
@ ID_RECALC_GEOMETRY
Definition DNA_ID.h:1041
CurveType
@ CURVE_TYPE_BEZIER
@ CURVE_TYPE_POLY
HandleType
@ BEZIER_HANDLE_ALIGN
@ 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
@ PART_FROM_FACE
@ PART_HAIR
@ PART_DRAW_PARENT
@ PSYS_EDITED
@ RGN_TYPE_WINDOW
@ SPACE_EMPTY
bool ED_operator_object_active_editable_ex(bContext *C, const Object *ob)
@ SEL_SELECT
@ SEL_DESELECT
@ SEL_TOGGLE
#define MEM_SAFE_FREE(v)
PropertyFlag
Definition RNA_types.hh:201
@ PROP_SKIP_SAVE
Definition RNA_types.hh:245
@ PROP_HIDDEN
Definition RNA_types.hh:239
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
#define UI_ITEM_NONE
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutColumn(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, PointerRNA *ptr, const char *propname, eUI_Item_Flag flag, const char *name, int icon)
@ UI_ITEM_R_SLIDER
@ OPTYPE_UNDO
Definition WM_types.hh:162
@ OPTYPE_REGISTER
Definition WM_types.hh:160
#define NC_WINDOW
Definition WM_types.hh:342
#define NC_NODE
Definition WM_types.hh:361
#define NC_GEOM
Definition WM_types.hh:360
#define ND_DRAW
Definition WM_types.hh:428
#define ND_DATA
Definition WM_types.hh:475
#define NA_ADDED
Definition WM_types.hh:552
#define NA_EDITED
Definition WM_types.hh:550
#define ND_PARTICLE
Definition WM_types.hh:432
#define ND_SPACE_VIEW3D
Definition WM_types.hh:494
#define NC_OBJECT
Definition WM_types.hh:346
#define NC_SPACE
Definition WM_types.hh:359
static unsigned long seed
Definition btSoftBody.h:39
static AttributeOwner from_id(ID *id)
Definition attribute.cc:43
AttributeSet attributes
constexpr void fill(const T &value) const
Definition BLI_span.hh:518
constexpr const T & first() const
Definition BLI_span.hh:316
constexpr int64_t size() const
Definition BLI_span.hh:253
constexpr const T * end() const
Definition BLI_span.hh:225
constexpr IndexRange index_range() const
Definition BLI_span.hh:402
constexpr const T * begin() const
Definition BLI_span.hh:221
constexpr bool is_empty() const
Definition BLI_span.hh:261
bool add(const Key &key)
void add_new(const Key &key)
int64_t size() const
void append(const T &value)
GAttributeReader lookup(const StringRef attribute_id) const
static IndexMask from_predicate(const IndexMask &universe, GrainSize grain_size, IndexMaskMemory &memory, Fn &&predicate)
IndexMask complement(const IndexMask &universe, IndexMaskMemory &memory) const
void to_bools(MutableSpan< bool > r_bools) const
void foreach_index(Fn &&fn) const
CCL_NAMESPACE_BEGIN struct Options options
const Depsgraph * depsgraph
static int select_linked_exec(bContext *C, wmOperator *)
static int delete_exec(bContext *C, wmOperator *op)
Definition editfont.cc:1670
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
uint col
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:45
void *(* MEM_calloc_arrayN)(size_t len, size_t size, const char *str)
Definition mallocn.cc:43
void MEM_freeN(void *vmemh)
Definition mallocn.cc:105
static int select_all_exec(bContext *C, wmOperator *op)
static CurvesGeometry generate_bezier_primitive(const float radius)
static CurvesGeometry generate_circle_primitive(const float radius)
static int 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 curves_convert_to_particle_system_exec(bContext *C, wmOperator *op)
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 void try_convert_single_object(Object &curves_ob, Main &bmain, Scene &scene, bool *r_could_not_convert_some_curves)
static int curves_set_selection_domain_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 int snap_curves_to_surface_exec(bContext *C, wmOperator *op)
static int surface_set_exec(bContext *C, wmOperator *op)
static bool surface_set_poll(bContext *C)
static void CURVES_OT_surface_set(wmOperatorType *ot)
void select_linked(bke::CurvesGeometry &curves, const IndexMask &curves_mask)
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 int select_ends_exec(bContext *C, wmOperator *op)
static void CURVES_OT_select_less(wmOperatorType *ot)
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:96
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)
void fill_selection_false(GMutableSpan selection)
bool object_has_editable_curves(const Main &bmain, const Object &object)
Definition curves_ops.cc:82
static void select_random_ui(bContext *, wmOperator *op)
bool editable_curves_in_edit_mode_poll(bContext *C)
static void CURVES_OT_tilt_clear(wmOperatorType *ot)
static void CURVES_OT_duplicate(wmOperatorType *ot)
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 void CURVES_OT_cyclic_toggle(wmOperatorType *ot)
static void CURVES_OT_delete(wmOperatorType *ot)
static void CURVES_OT_snap_curves_to_surface(wmOperatorType *ot)
void ensure_surface_deformation_node_exists(bContext &C, Object &curves_ob)
Definition curves_add.cc:61
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)
bke::GSpanAttributeWriter ensure_selection_attribute(bke::CurvesGeometry &curves, bke::AttrDomain selection_domain, eCustomDataType create_type, StringRef attribute_name)
void CURVES_OT_attribute_set(wmOperatorType *ot)
bool curves_poll(bContext *C)
IndexMask retrieve_selected_points(const bke::CurvesGeometry &curves, IndexMaskMemory &memory)
void CURVES_OT_extrude(wmOperatorType *ot)
IndexMask random_mask(const bke::CurvesGeometry &curves, const IndexMask &mask, const bke::AttrDomain selection_domain, const uint32_t random_seed, const float probability, IndexMaskMemory &memory)
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)
Object * context_active_object(const bContext *C)
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:95
static void exec(void *data, int, bNode *node, bNodeExecData *execdata, bNodeStack **in, bNodeStack **out)
static int select_random_exec(bContext *C, wmOperator *op)
static int select_more_exec(bContext *C, wmOperator *)
static int select_less_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_handle_type_items[]
Definition rna_curves.cc:30
const EnumPropertyItem rna_enum_curves_type_items[]
Definition rna_curves.cc:22
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
#define INT32_MAX
Definition stdint.h:137
#define INT32_MIN
Definition stdint.h:136
BVHTree_NearestPointCallback nearest_callback
CurvesGeometry geometry
char selection_domain
struct Object * surface
char * surface_uv_map
Definition DNA_ID.h:413
char name[66]
Definition DNA_ID.h:425
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:42
const c_style_mat & ptr() const
bool(* poll)(struct bContext *)
const char * name
Definition WM_types.hh:990
bool(* poll)(bContext *C) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1042
const char * idname
Definition WM_types.hh:992
int(* invoke)(bContext *C, wmOperator *op, const wmEvent *event) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1022
int(* exec)(bContext *C, wmOperator *op) ATTR_WARN_UNUSED_RESULT
Definition WM_types.hh:1006
const char * description
Definition WM_types.hh:996
void(* ui)(bContext *C, wmOperator *op)
Definition WM_types.hh:1053
PropertyRNA * prop
Definition WM_types.hh:1092
StructRNA * srna
Definition WM_types.hh:1080
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
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:4125
wmKeyMap * WM_keymap_ensure(wmKeyConfig *keyconf, const char *idname, int spaceid, int regionid)
Definition wm_keymap.cc:897
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)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *)