diff options
Diffstat (limited to 'source/blender/geometry')
-rw-r--r-- | source/blender/geometry/CMakeLists.txt | 2 | ||||
-rw-r--r-- | source/blender/geometry/GEO_resample_curves.hh | 15 | ||||
-rw-r--r-- | source/blender/geometry/GEO_trim_curves.hh | 24 | ||||
-rw-r--r-- | source/blender/geometry/intern/add_curves_on_mesh.cc | 9 | ||||
-rw-r--r-- | source/blender/geometry/intern/fillet_curves.cc | 6 | ||||
-rw-r--r-- | source/blender/geometry/intern/mesh_merge_by_distance.cc | 33 | ||||
-rw-r--r-- | source/blender/geometry/intern/mesh_primitive_cuboid.cc | 6 | ||||
-rw-r--r-- | source/blender/geometry/intern/mesh_to_curve_convert.cc | 16 | ||||
-rw-r--r-- | source/blender/geometry/intern/mesh_to_volume.cc | 14 | ||||
-rw-r--r-- | source/blender/geometry/intern/realize_instances.cc | 84 | ||||
-rw-r--r-- | source/blender/geometry/intern/resample_curves.cc | 186 | ||||
-rw-r--r-- | source/blender/geometry/intern/set_curve_type.cc | 13 | ||||
-rw-r--r-- | source/blender/geometry/intern/trim_curves.cc | 1031 | ||||
-rw-r--r-- | source/blender/geometry/intern/uv_parametrizer.cc | 44 |
14 files changed, 1297 insertions, 186 deletions
diff --git a/source/blender/geometry/CMakeLists.txt b/source/blender/geometry/CMakeLists.txt index 0f06890cbfa..9e1929b60a8 100644 --- a/source/blender/geometry/CMakeLists.txt +++ b/source/blender/geometry/CMakeLists.txt @@ -27,6 +27,7 @@ set(SRC intern/reverse_uv_sampler.cc intern/set_curve_type.cc intern/subdivide_curves.cc + intern/trim_curves.cc intern/uv_parametrizer.cc GEO_add_curves_on_mesh.hh @@ -41,6 +42,7 @@ set(SRC GEO_reverse_uv_sampler.hh GEO_set_curve_type.hh GEO_subdivide_curves.hh + GEO_trim_curves.hh GEO_uv_parametrizer.h ) diff --git a/source/blender/geometry/GEO_resample_curves.hh b/source/blender/geometry/GEO_resample_curves.hh index 7ecfb5c26ec..35365167eba 100644 --- a/source/blender/geometry/GEO_resample_curves.hh +++ b/source/blender/geometry/GEO_resample_curves.hh @@ -4,12 +4,18 @@ #include "FN_field.hh" +#include "BKE_anonymous_attribute.hh" #include "BKE_curves.hh" namespace blender::geometry { using bke::CurvesGeometry; +struct ResampleCurvesOutputAttributeIDs { + bke::AttributeIDRef tangent_id; + bke::AttributeIDRef normal_id; +}; + /** * Create new curves where the selected curves have been resampled with a number of uniform-length * samples defined by the count field. Interpolate attributes to the result, with an accuracy that @@ -19,7 +25,8 @@ using bke::CurvesGeometry; */ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<int> &count_field); + const fn::Field<int> &count_field, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); /** * Create new curves resampled to make each segment have the length specified by the @@ -28,12 +35,14 @@ CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, */ CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<float> &segment_length_field); + const fn::Field<float> &segment_length_field, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); /** * Evaluate each selected curve to its implicit evaluated points. */ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, - const fn::Field<bool> &selection_field); + const fn::Field<bool> &selection_field, + const ResampleCurvesOutputAttributeIDs &output_ids = {}); } // namespace blender::geometry diff --git a/source/blender/geometry/GEO_trim_curves.hh b/source/blender/geometry/GEO_trim_curves.hh new file mode 100644 index 00000000000..197ef79c25b --- /dev/null +++ b/source/blender/geometry/GEO_trim_curves.hh @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +#pragma once + +#include "BLI_span.hh" +#include "DNA_node_types.h" + +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_set.hh" + +namespace blender::geometry { + +/* + * Create a new Curves instance by trimming the input curves. Copying the selected splines + * between the start and end points. + */ +bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, + IndexMask selection, + const VArray<float> &starts, + const VArray<float> &ends, + GeometryNodeCurveSampleMode mode); + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/add_curves_on_mesh.cc b/source/blender/geometry/intern/add_curves_on_mesh.cc index bb5e2a0a28a..a03c9b994a9 100644 --- a/source/blender/geometry/intern/add_curves_on_mesh.cc +++ b/source/blender/geometry/intern/add_curves_on_mesh.cc @@ -51,7 +51,7 @@ static void initialize_straight_curve_positions(const float3 &p1, const float3 &p2, MutableSpan<float3> r_positions) { - const float step = 1.0f / (float)(r_positions.size() - 1); + const float step = 1.0f / float(r_positions.size() - 1); for (const int i : r_positions.index_range()) { r_positions[i] = math::interpolate(p1, p2, i * step); } @@ -385,10 +385,11 @@ AddCurvesOnMeshOutputs add_curves_on_mesh(CurvesGeometry &curves, return true; } bke::GSpanAttributeWriter attribute = attributes.lookup_for_write_span(id); - const int new_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? new_points_num : - new_curves_num; + /* The new elements are added at the end of the array. */ + const int old_elements_num = attribute.domain == ATTR_DOMAIN_POINT ? old_points_num : + old_curves_num; const CPPType &type = attribute.span.type(); - GMutableSpan new_data = attribute.span.take_back(new_elements_num); + GMutableSpan new_data = attribute.span.drop_front(old_elements_num); type.fill_assign_n(type.default_value(), new_data.data(), new_data.size()); attribute.finish(); return true; diff --git a/source/blender/geometry/intern/fillet_curves.cc b/source/blender/geometry/intern/fillet_curves.cc index 1bbbee6edef..2479458f88d 100644 --- a/source/blender/geometry/intern/fillet_curves.cc +++ b/source/blender/geometry/intern/fillet_curves.cc @@ -148,12 +148,14 @@ static float limit_radius(const float3 &position_prev, const float displacement_prev = radius_prev * std::tan(angle_prev / 2.0f); const float segment_length_prev = math::distance(position, position_prev); const float total_displacement_prev = displacement_prev + displacement; - const float factor_prev = std::clamp(segment_length_prev / total_displacement_prev, 0.0f, 1.0f); + const float factor_prev = std::clamp( + safe_divide(segment_length_prev, total_displacement_prev), 0.0f, 1.0f); const float displacement_next = radius_next * std::tan(angle_next / 2.0f); const float segment_length_next = math::distance(position, position_next); const float total_displacement_next = displacement_next + displacement; - const float factor_next = std::clamp(segment_length_next / total_displacement_next, 0.0f, 1.0f); + const float factor_next = std::clamp( + safe_divide(segment_length_next, total_displacement_next), 0.0f, 1.0f); return radius * std::min(factor_prev, factor_next); } diff --git a/source/blender/geometry/intern/mesh_merge_by_distance.cc b/source/blender/geometry/intern/mesh_merge_by_distance.cc index 17318c277aa..288fd407641 100644 --- a/source/blender/geometry/intern/mesh_merge_by_distance.cc +++ b/source/blender/geometry/intern/mesh_merge_by_distance.cc @@ -21,11 +21,11 @@ namespace blender::geometry { /* Indicates when the element was not computed. */ -#define OUT_OF_CONTEXT (int)(-1) +#define OUT_OF_CONTEXT int(-1) /* Indicates if the edge or face will be collapsed. */ -#define ELEM_COLLAPSED (int)(-2) +#define ELEM_COLLAPSED int(-2) /* indicates whether an edge or vertex in groups_map will be merged. */ -#define ELEM_MERGED (int)(-2) +#define ELEM_MERGED int(-2) /* Used to indicate a range in an array specifying a group. */ struct WeldGroup { @@ -702,7 +702,7 @@ static bool weld_iter_loop_of_poly_next(WeldLoopOfPolyIter &iter) else { const MLoop &ml = iter.mloop[l]; #ifdef USE_WELD_DEBUG - BLI_assert((uint)iter.v != ml.v); + BLI_assert(uint(iter.v) != ml.v); #endif iter.v = ml.v; iter.e = ml.e; @@ -1232,7 +1232,6 @@ static void customdata_weld( #ifdef USE_WELD_NORMALS float no[3] = {0.0f, 0.0f, 0.0f}; #endif - int crease = 0; short flag = 0; /* interpolates a layer at a time */ @@ -1266,13 +1265,11 @@ static void customdata_weld( no[1] += mv_src_no[1]; no[2] += mv_src_no[2]; #endif - flag |= mv_src->flag; } } else if (type == CD_MEDGE) { for (j = 0; j < count; j++) { MEdge *me_src = &((MEdge *)src_data)[src_indices[j]]; - crease += me_src->crease; flag |= me_src->flag; } } @@ -1283,10 +1280,10 @@ static void customdata_weld( else if (CustomData_layer_has_math(dest, dest_i)) { const int size = CustomData_sizeof(type); void *dst_data = dest->layers[dest_i].data; - void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); + void *v_dst = POINTER_OFFSET(dst_data, size_t(dest_index) * size); for (j = 0; j < count; j++) { CustomData_data_add( - type, v_dst, POINTER_OFFSET(src_data, (size_t)src_indices[j] * size)); + type, v_dst, POINTER_OFFSET(src_data, size_t(src_indices[j]) * size)); } } else { @@ -1314,19 +1311,13 @@ static void customdata_weld( #ifdef USE_WELD_NORMALS mul_v3_fl(no, fac); short *mv_no = mv->no; - mv_no[0] = (short)no[0]; - mv_no[1] = (short)no[1]; - mv_no[2] = (short)no[2]; + mv_no[0] = short(no[0]); + mv_no[1] = short(no[1]); + mv_no[2] = short(no[2]); #endif - - mv->flag = (char)flag; } else if (type == CD_MEDGE) { MEdge *me = &((MEdge *)layer_dst->data)[dest_index]; - crease *= fac; - CLAMP_MAX(crease, 255); - - me->crease = (char)crease; me->flag = flag; } else if (CustomData_layer_has_interp(dest, dest_i)) { @@ -1335,7 +1326,7 @@ static void customdata_weld( else if (CustomData_layer_has_math(dest, dest_i)) { const int size = CustomData_sizeof(type); void *dst_data = layer_dst->data; - void *v_dst = POINTER_OFFSET(dst_data, (size_t)dest_index * size); + void *v_dst = POINTER_OFFSET(dst_data, size_t(dest_index) * size); CustomData_data_multiply(type, v_dst, fac); } } @@ -1541,7 +1532,7 @@ static Mesh *create_merged_mesh(const Mesh &mesh, r_i++; } - BLI_assert((int)r_i == result_npolys); + BLI_assert(int(r_i) == result_npolys); BLI_assert(loop_cur == result_nloops); return result; @@ -1639,7 +1630,7 @@ std::optional<Mesh *> mesh_merge_by_distance_connected(const Mesh &mesh, const float dist_sq = len_squared_v3(edgedir); if (dist_sq <= merge_dist_sq) { float influence = (v2_cluster->merged_verts + 1) / - (float)(v1_cluster->merged_verts + v2_cluster->merged_verts + 2); + float(v1_cluster->merged_verts + v2_cluster->merged_verts + 2); madd_v3_v3fl(v1_cluster->co, edgedir, influence); v1_cluster->merged_verts += v2_cluster->merged_verts + 1; diff --git a/source/blender/geometry/intern/mesh_primitive_cuboid.cc b/source/blender/geometry/intern/mesh_primitive_cuboid.cc index 39571f2931e..a014c488a3b 100644 --- a/source/blender/geometry/intern/mesh_primitive_cuboid.cc +++ b/source/blender/geometry/intern/mesh_primitive_cuboid.cc @@ -328,9 +328,9 @@ static void calculate_uvs(const CuboidConfig &config, Mesh *mesh, const bke::Att int loop_index = 0; - const float x_delta = 0.25f / static_cast<float>(config.edges_x); - const float y_delta = 0.25f / static_cast<float>(config.edges_y); - const float z_delta = 0.25f / static_cast<float>(config.edges_z); + const float x_delta = 0.25f / float(config.edges_x); + const float y_delta = 0.25f / float(config.edges_y); + const float z_delta = 0.25f / float(config.edges_z); /* Calculate bottom face UVs. */ for (const int y : IndexRange(config.edges_y)) { diff --git a/source/blender/geometry/intern/mesh_to_curve_convert.cc b/source/blender/geometry/intern/mesh_to_curve_convert.cc index 22961504015..c2a9b16c8b6 100644 --- a/source/blender/geometry/intern/mesh_to_curve_convert.cc +++ b/source/blender/geometry/intern/mesh_to_curve_convert.cc @@ -1,6 +1,7 @@ /* SPDX-License-Identifier: GPL-2.0-or-later */ #include "BLI_array.hh" +#include "BLI_array_utils.hh" #include "BLI_devirtualize_parameters.hh" #include "BLI_set.hh" #include "BLI_task.hh" @@ -18,19 +19,6 @@ namespace blender::geometry { -template<typename T> -static void copy_with_map(const VArray<T> &src, Span<int> map, MutableSpan<T> dst) -{ - devirtualize_varray(src, [&](const auto &src) { - threading::parallel_for(map.index_range(), 1024, [&](const IndexRange range) { - for (const int i : range) { - const int vert_index = map[i]; - dst[i] = src[vert_index]; - } - }); - }); -} - bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, const Span<int> vert_indices, const Span<int> curve_offsets, @@ -71,7 +59,7 @@ bke::CurvesGeometry create_curve_from_vert_indices(const Mesh &mesh, using T = decltype(dummy); bke::SpanAttributeWriter<T> attribute = curves_attributes.lookup_or_add_for_write_only_span<T>(attribute_id, ATTR_DOMAIN_POINT); - copy_with_map<T>(mesh_attribute.typed<T>(), vert_indices, attribute.span); + array_utils::gather<T>(mesh_attribute.typed<T>(), vert_indices, attribute.span); attribute.finish(); }); } diff --git a/source/blender/geometry/intern/mesh_to_volume.cc b/source/blender/geometry/intern/mesh_to_volume.cc index b1c7be38609..b6025f8f1a9 100644 --- a/source/blender/geometry/intern/mesh_to_volume.cc +++ b/source/blender/geometry/intern/mesh_to_volume.cc @@ -25,30 +25,26 @@ class OpenVDBMeshAdapter { OpenVDBMeshAdapter(const Mesh &mesh, float4x4 transform); size_t polygonCount() const; size_t pointCount() const; - size_t vertexCount(size_t UNUSED(polygon_index)) const; + size_t vertexCount(size_t /*polygon_index*/) const; void getIndexSpacePoint(size_t polygon_index, size_t vertex_index, openvdb::Vec3d &pos) const; }; OpenVDBMeshAdapter::OpenVDBMeshAdapter(const Mesh &mesh, float4x4 transform) - : verts_(mesh.verts()), loops_(mesh.loops()), transform_(transform) + : verts_(mesh.verts()), loops_(mesh.loops()), looptris_(mesh.looptris()), transform_(transform) { - /* This only updates a cache and can be considered to be logically const. */ - const MLoopTri *looptris = BKE_mesh_runtime_looptri_ensure(&mesh); - const int looptris_len = BKE_mesh_runtime_looptri_len(&mesh); - looptris_ = Span(looptris, looptris_len); } size_t OpenVDBMeshAdapter::polygonCount() const { - return static_cast<size_t>(looptris_.size()); + return size_t(looptris_.size()); } size_t OpenVDBMeshAdapter::pointCount() const { - return static_cast<size_t>(verts_.size()); + return size_t(verts_.size()); } -size_t OpenVDBMeshAdapter::vertexCount(size_t UNUSED(polygon_index)) const +size_t OpenVDBMeshAdapter::vertexCount(size_t /*polygon_index*/) const { /* All polygons are triangles. */ return 3; diff --git a/source/blender/geometry/intern/realize_instances.cc b/source/blender/geometry/intern/realize_instances.cc index 29a9f51c0a7..2d9c23df348 100644 --- a/source/blender/geometry/intern/realize_instances.cc +++ b/source/blender/geometry/intern/realize_instances.cc @@ -17,6 +17,7 @@ #include "BKE_curves.hh" #include "BKE_deform.h" #include "BKE_geometry_set_instances.hh" +#include "BKE_instances.hh" #include "BKE_material.h" #include "BKE_mesh.h" #include "BKE_pointcloud.h" @@ -30,6 +31,8 @@ using blender::bke::AttributeMetaData; using blender::bke::custom_data_type_to_cpp_type; using blender::bke::CustomDataAttributes; using blender::bke::GSpanAttributeWriter; +using blender::bke::InstanceReference; +using blender::bke::Instances; using blender::bke::object_get_evaluated_geometry_set; using blender::bke::SpanAttributeWriter; @@ -370,11 +373,11 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, */ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks( GatherTasksInfo &gather_info, - const InstancesComponent &instances_component, + const Instances &instances, const OrderedAttributes &ordered_attributes) { Vector<std::pair<int, GSpan>> attributes_to_override; - const CustomDataAttributes &attributes = instances_component.instance_attributes(); + const CustomDataAttributes &attributes = instances.custom_data_attributes(); attributes.foreach_attribute( [&](const AttributeIDRef &attribute_id, const AttributeMetaData &meta_data) { const int attribute_index = ordered_attributes.ids.index_of_try(attribute_id); @@ -394,7 +397,7 @@ static Vector<std::pair<int, GSpan>> prepare_attribute_fallbacks( } /* Convert the attribute on the instances component to the expected attribute type. */ std::unique_ptr<GArray<>> temporary_array = std::make_unique<GArray<>>( - to_type, instances_component.instances_num()); + to_type, instances.instances_num()); conversions.convert_to_initialized_n(span, temporary_array->as_mutable_span()); span = temporary_array->as_span(); gather_info.r_temporary_arrays.append(std::move(temporary_array)); @@ -430,7 +433,7 @@ static void foreach_geometry_in_reference( int index = 0; FOREACH_COLLECTION_OBJECT_RECURSIVE_BEGIN (&collection, object) { const GeometrySet object_geometry_set = object_get_evaluated_geometry_set(*object); - const float4x4 matrix = base_transform * offset_matrix * object->obmat; + const float4x4 matrix = base_transform * offset_matrix * object->object_to_world; const int sub_id = noise::hash(id, index); fn(object_geometry_set, matrix, sub_id); index++; @@ -450,17 +453,17 @@ static void foreach_geometry_in_reference( } static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info, - const InstancesComponent &instances_component, + const Instances &instances, const float4x4 &base_transform, const InstanceContext &base_instance_context) { - const Span<InstanceReference> references = instances_component.references(); - const Span<int> handles = instances_component.instance_reference_handles(); - const Span<float4x4> transforms = instances_component.instance_transforms(); + const Span<InstanceReference> references = instances.references(); + const Span<int> handles = instances.reference_handles(); + const Span<float4x4> transforms = instances.transforms(); Span<int> stored_instance_ids; if (gather_info.create_id_attribute_on_any_component) { - std::optional<GSpan> ids = instances_component.instance_attributes().get_for_read("id"); + std::optional<GSpan> ids = instances.custom_data_attributes().get_for_read("id"); if (ids.has_value()) { stored_instance_ids = ids->typed<int>(); } @@ -469,11 +472,11 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info, /* Prepare attribute fallbacks. */ InstanceContext instance_context = base_instance_context; Vector<std::pair<int, GSpan>> pointcloud_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.pointclouds.attributes); + gather_info, instances, gather_info.pointclouds.attributes); Vector<std::pair<int, GSpan>> mesh_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.meshes.attributes); + gather_info, instances, gather_info.meshes.attributes); Vector<std::pair<int, GSpan>> curve_attributes_to_override = prepare_attribute_fallbacks( - gather_info, instances_component, gather_info.curves.attributes); + gather_info, instances, gather_info.curves.attributes); for (const int i : transforms.index_range()) { const int handle = handles[i]; @@ -495,10 +498,10 @@ static void gather_realize_tasks_for_instances(GatherTasksInfo &gather_info, uint32_t local_instance_id = 0; if (gather_info.create_id_attribute_on_any_component) { if (stored_instance_ids.is_empty()) { - local_instance_id = (uint32_t)i; + local_instance_id = uint32_t(i); } else { - local_instance_id = (uint32_t)stored_instance_ids[i]; + local_instance_id = uint32_t(stored_instance_ids[i]); } } const uint32_t instance_id = noise::hash(base_instance_context.id, local_instance_id); @@ -584,8 +587,11 @@ static void gather_realize_tasks_recursive(GatherTasksInfo &gather_info, case GEO_COMPONENT_TYPE_INSTANCES: { const InstancesComponent &instances_component = *static_cast<const InstancesComponent *>( component); - gather_realize_tasks_for_instances( - gather_info, instances_component, base_transform, base_instance_context); + const Instances *instances = instances_component.get_for_read(); + if (instances != nullptr && instances->instances_num() > 0) { + gather_realize_tasks_for_instances( + gather_info, *instances, base_transform, base_instance_context); + } break; } case GEO_COMPONENT_TYPE_VOLUME: { @@ -645,8 +651,7 @@ static void gather_pointclouds_to_realize(const GeometrySet &geometry_set, r_pointclouds.add(pointcloud); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read<InstancesComponent>()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_pointclouds_to_realize(instance_geometry_set, r_pointclouds); }); @@ -782,9 +787,7 @@ static void execute_realize_pointcloud_tasks(const RealizeInstancesOptions &opti dst_attribute.finish(); } positions.finish(); - if (point_ids) { - point_ids.finish(); - } + point_ids.finish(); } /** \} */ @@ -811,7 +814,6 @@ static OrderedAttributes gather_generic_mesh_attributes_to_propagate( attributes_to_propagate.remove("position"); attributes_to_propagate.remove("normal"); attributes_to_propagate.remove("shade_smooth"); - attributes_to_propagate.remove("crease"); r_create_id = attributes_to_propagate.pop_try("id").has_value(); r_create_material_index = attributes_to_propagate.pop_try("material_index").has_value(); OrderedAttributes ordered_attributes; @@ -830,8 +832,7 @@ static void gather_meshes_to_realize(const GeometrySet &geometry_set, r_meshes.add(mesh); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read<InstancesComponent>()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_meshes_to_realize(instance_geometry_set, r_meshes); }); @@ -858,6 +859,7 @@ static AllMeshesInfo preprocess_meshes(const GeometrySet &geometry_set, } } } + info.create_material_index_attribute |= info.materials.size() > 1; info.realize_info.reinitialize(info.order.size()); for (const int mesh_index : info.realize_info.index_range()) { MeshRealizeInfo &mesh_info = info.realize_info[mesh_index]; @@ -1107,12 +1109,8 @@ static void execute_realize_mesh_tasks(const RealizeInstancesOptions &options, for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) { dst_attribute.finish(); } - if (vertex_ids) { - vertex_ids.finish(); - } - if (material_indices) { - material_indices.finish(); - } + vertex_ids.finish(); + material_indices.finish(); } /** \} */ @@ -1155,8 +1153,7 @@ static void gather_curves_to_realize(const GeometrySet &geometry_set, r_curves.add(curves); } } - if (const InstancesComponent *instances = - geometry_set.get_component_for_read<InstancesComponent>()) { + if (const Instances *instances = geometry_set.get_instances_for_read()) { instances->foreach_referenced_geometry([&](const GeometrySet &instance_geometry_set) { gather_curves_to_realize(instance_geometry_set, r_curves); }); @@ -1406,19 +1403,11 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, for (GSpanAttributeWriter &dst_attribute : dst_attribute_writers) { dst_attribute.finish(); } - if (point_ids) { - point_ids.finish(); - } - if (radius) { - radius.finish(); - } - if (resolution) { - resolution.finish(); - } - if (all_curves_info.create_handle_postion_attributes) { - handle_left.finish(); - handle_right.finish(); - } + point_ids.finish(); + radius.finish(); + resolution.finish(); + handle_left.finish(); + handle_right.finish(); } /** \} */ @@ -1430,9 +1419,8 @@ static void execute_realize_curve_tasks(const RealizeInstancesOptions &options, static void remove_id_attribute_from_instances(GeometrySet &geometry_set) { geometry_set.modify_geometry_sets([&](GeometrySet &sub_geometry) { - if (sub_geometry.has<InstancesComponent>()) { - InstancesComponent &component = sub_geometry.get_component_for_write<InstancesComponent>(); - component.instance_attributes().remove("id"); + if (Instances *instances = sub_geometry.get_instances_for_write()) { + instances->custom_data_attributes().remove("id"); } }); } diff --git a/source/blender/geometry/intern/resample_curves.cc b/source/blender/geometry/intern/resample_curves.cc index d5560a95a18..3be850ec097 100644 --- a/source/blender/geometry/intern/resample_curves.cc +++ b/source/blender/geometry/intern/resample_curves.cc @@ -116,14 +116,21 @@ struct AttributesForInterpolation : NonCopyable, NonMovable { Vector<GSpan> src_no_interpolation; Vector<GMutableSpan> dst_no_interpolation; + + Span<float3> src_evaluated_tangents; + Span<float3> src_evaluated_normals; + MutableSpan<float3> dst_tangents; + MutableSpan<float3> dst_normals; }; /** * Gather a set of all generic attribute IDs to copy to the result curves. */ -static void gather_point_attributes_to_interpolate(const CurvesGeometry &src_curves, - CurvesGeometry &dst_curves, - AttributesForInterpolation &result) +static void gather_point_attributes_to_interpolate( + const CurvesGeometry &src_curves, + CurvesGeometry &dst_curves, + AttributesForInterpolation &result, + const ResampleCurvesOutputAttributeIDs &output_ids) { VectorSet<bke::AttributeIDRef> ids; VectorSet<bke::AttributeIDRef> ids_no_interpolation; @@ -132,6 +139,9 @@ static void gather_point_attributes_to_interpolate(const CurvesGeometry &src_cur if (meta_data.domain != ATTR_DOMAIN_POINT) { return true; } + if (meta_data.data_type == CD_PROP_STRING) { + return true; + } if (!interpolate_attribute_to_curves(id, dst_curves.curve_type_counts())) { return true; } @@ -159,11 +169,75 @@ static void gather_point_attributes_to_interpolate(const CurvesGeometry &src_cur result.src_no_interpolation, result.dst_no_interpolation, result.dst_attributes); + + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + if (output_ids.tangent_id) { + result.src_evaluated_tangents = src_curves.evaluated_tangents(); + bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span( + output_ids.tangent_id, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + result.dst_tangents = dst_attribute.span.typed<float3>(); + result.dst_attributes.append(std::move(dst_attribute)); + } + if (output_ids.normal_id) { + result.src_evaluated_normals = src_curves.evaluated_normals(); + bke::GSpanAttributeWriter dst_attribute = dst_attributes.lookup_or_add_for_write_only_span( + output_ids.normal_id, ATTR_DOMAIN_POINT, CD_PROP_FLOAT3); + result.dst_normals = dst_attribute.span.typed<float3>(); + result.dst_attributes.append(std::move(dst_attribute)); + } +} + +static void copy_or_defaults_for_unselected_curves(const CurvesGeometry &src_curves, + const Span<IndexRange> unselected_ranges, + const AttributesForInterpolation &attributes, + CurvesGeometry &dst_curves) +{ + bke::curves::copy_point_data(src_curves, + dst_curves, + unselected_ranges, + src_curves.positions(), + dst_curves.positions_for_write()); + + for (const int i : attributes.src.index_range()) { + bke::curves::copy_point_data( + src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); + } + for (const int i : attributes.src_no_interpolation.index_range()) { + bke::curves::copy_point_data(src_curves, + dst_curves, + unselected_ranges, + attributes.src_no_interpolation[i], + attributes.dst_no_interpolation[i]); + } + + if (!attributes.dst_tangents.is_empty()) { + bke::curves::fill_points(dst_curves, unselected_ranges, float3(0), attributes.dst_tangents); + } + if (!attributes.dst_normals.is_empty()) { + bke::curves::fill_points(dst_curves, unselected_ranges, float3(0), attributes.dst_normals); + } +} + +static void normalize_span(MutableSpan<float3> data) +{ + for (const int i : data.index_range()) { + data[i] = math::normalize(data[i]); + } +} + +static void normalize_curve_point_data(const CurvesGeometry &curves, + const IndexMask curve_selection, + MutableSpan<float3> data) +{ + for (const int i_curve : curve_selection) { + normalize_span(data.slice(curves.points_for_curve(i_curve))); + } } static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<int> &count_field) + const fn::Field<int> &count_field, + const ResampleCurvesOutputAttributeIDs &output_ids) { /* Create the new curves without any points and evaluate the final count directly * into the offsets array, in order to be accumulated into offsets later. */ @@ -200,7 +274,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes); + gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids); src_curves.ensure_evaluated_lengths(); @@ -272,14 +346,27 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, }); } + auto interpolate_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) { + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + length_parameterize::interpolate(src.slice(src_points), + sample_indices.as_span().slice(dst_points), + sample_factors.as_span().slice(dst_points), + dst.slice(dst_points)); + } + }; + /* Interpolate the evaluated positions to the resampled curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - length_parameterize::interpolate(evaluated_positions.slice(src_points), - sample_indices.as_span().slice(dst_points), - sample_factors.as_span().slice(dst_points), - dst_positions.slice(dst_points)); + interpolate_evaluated_data(evaluated_positions, dst_positions); + + if (!attributes.dst_tangents.is_empty()) { + interpolate_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents); + normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_tangents); + } + if (!attributes.dst_normals.is_empty()) { + interpolate_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals); + normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_normals); } /* Fill the default value for non-interpolating attributes that still must be copied. */ @@ -291,23 +378,7 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, } }); - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - bke::curves::copy_point_data(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + copy_or_defaults_for_unselected_curves(src_curves, unselected_ranges, attributes, dst_curves); for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) { attribute.finish(); @@ -318,21 +389,25 @@ static CurvesGeometry resample_to_uniform(const CurvesGeometry &src_curves, CurvesGeometry resample_to_count(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<int> &count_field) + const fn::Field<int> &count_field, + const ResampleCurvesOutputAttributeIDs &output_ids) { - return resample_to_uniform(src_curves, selection_field, get_count_input_max_one(count_field)); + return resample_to_uniform( + src_curves, selection_field, get_count_input_max_one(count_field), output_ids); } CurvesGeometry resample_to_length(const CurvesGeometry &src_curves, const fn::Field<bool> &selection_field, - const fn::Field<float> &segment_length_field) + const fn::Field<float> &segment_length_field, + const ResampleCurvesOutputAttributeIDs &output_ids) { return resample_to_uniform( - src_curves, selection_field, get_count_input_from_length(segment_length_field)); + src_curves, selection_field, get_count_input_from_length(segment_length_field), output_ids); } CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, - const fn::Field<bool> &selection_field) + const fn::Field<bool> &selection_field, + const ResampleCurvesOutputAttributeIDs &output_ids) { src_curves.ensure_evaluated_offsets(); @@ -368,11 +443,11 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, dst_curves.resize(dst_offsets.last(), dst_curves.curves_num()); /* Create the correct number of uniform-length samples for every selected curve. */ - Span<float3> evaluated_positions = src_curves.evaluated_positions(); + const Span<float3> evaluated_positions = src_curves.evaluated_positions(); MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); AttributesForInterpolation attributes; - gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes); + gather_point_attributes_to_interpolate(src_curves, dst_curves, attributes, output_ids); threading::parallel_for(selection.index_range(), 512, [&](IndexRange selection_range) { const IndexMask sliced_selection = selection.slice(selection_range); @@ -393,11 +468,24 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, }); } + auto copy_evaluated_data = [&](const Span<float3> src, MutableSpan<float3> dst) { + for (const int i_curve : sliced_selection) { + const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); + const IndexRange dst_points = dst_curves.points_for_curve(i_curve); + dst.slice(dst_points).copy_from(src.slice(src_points)); + } + }; + /* Copy the evaluated positions to the selected curves. */ - for (const int i_curve : sliced_selection) { - const IndexRange src_points = src_curves.evaluated_points_for_curve(i_curve); - const IndexRange dst_points = dst_curves.points_for_curve(i_curve); - dst_positions.slice(dst_points).copy_from(evaluated_positions.slice(src_points)); + copy_evaluated_data(evaluated_positions, dst_positions); + + if (!attributes.dst_tangents.is_empty()) { + copy_evaluated_data(attributes.src_evaluated_tangents, attributes.dst_tangents); + normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_tangents); + } + if (!attributes.dst_normals.is_empty()) { + copy_evaluated_data(attributes.src_evaluated_normals, attributes.dst_normals); + normalize_curve_point_data(dst_curves, sliced_selection, attributes.dst_normals); } /* Fill the default value for non-interpolating attributes that still must be copied. */ @@ -409,23 +497,7 @@ CurvesGeometry resample_to_evaluated(const CurvesGeometry &src_curves, } }); - /* Any attribute data from unselected curve points can be directly copied. */ - for (const int i : attributes.src.index_range()) { - bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, attributes.src[i], attributes.dst[i]); - } - for (const int i : attributes.src_no_interpolation.index_range()) { - bke::curves::copy_point_data(src_curves, - dst_curves, - unselected_ranges, - attributes.src_no_interpolation[i], - attributes.dst_no_interpolation[i]); - } - - /* Copy positions for unselected curves. */ - Span<float3> src_positions = src_curves.positions(); - bke::curves::copy_point_data( - src_curves, dst_curves, unselected_ranges, src_positions, dst_positions); + copy_or_defaults_for_unselected_curves(src_curves, unselected_ranges, attributes, dst_curves); for (bke::GSpanAttributeWriter &attribute : attributes.dst_attributes) { attribute.finish(); diff --git a/source/blender/geometry/intern/set_curve_type.cc b/source/blender/geometry/intern/set_curve_type.cc index 92609a45bdc..e069732ca9b 100644 --- a/source/blender/geometry/intern/set_curve_type.cc +++ b/source/blender/geometry/intern/set_curve_type.cc @@ -186,12 +186,12 @@ static Vector<float3> create_nurbs_to_bezier_handles(const Span<float3> nurbs_po const int segments_num = nurbs_positions_num - 1; const bool ignore_interior_segment = segments_num == 3 && is_periodic == false; if (ignore_interior_segment == false) { - const float mid_offset = (float)(segments_num - 1) / 2.0f; + const float mid_offset = float(segments_num - 1) / 2.0f; for (const int i : IndexRange(1, segments_num - 2)) { /* Divisor can have values: 1, 2 or 3. */ const int divisor = is_periodic ? 3 : - std::min(3, (int)(-std::abs(i - mid_offset) + mid_offset + 1.0f)); + std::min(3, int(-std::abs(i - mid_offset) + mid_offset + 1.0f)); const float3 &p1 = nurbs_positions[i]; const float3 &p2 = nurbs_positions[i + 1]; const float3 displacement = (p2 - p1) / divisor; @@ -257,7 +257,7 @@ static int to_bezier_size(const CurveType src_type, switch (src_type) { case CURVE_TYPE_NURBS: { if (is_nurbs_to_bezier_one_to_one(knots_mode)) { - return cyclic ? src_size : src_size - 2; + return cyclic ? src_size : std::max(1, src_size - 2); } return (src_size + 1) / 3; } @@ -392,6 +392,13 @@ static bke::CurvesGeometry convert_curves_to_bezier(const bke::CurvesGeometry &s const IndexRange src_points = src_curves.points_for_curve(i); const IndexRange dst_points = dst_curves.points_for_curve(i); const Span<float3> src_curve_positions = src_positions.slice(src_points); + if (dst_points.size() == 1) { + const float3 &position = src_positions[src_points.first()]; + dst_positions[dst_points.first()] = position; + dst_handles_l[dst_points.first()] = position; + dst_handles_r[dst_points.first()] = position; + continue; + } KnotsMode knots_mode = KnotsMode(src_knot_modes[i]); Span<float3> nurbs_positions = src_curve_positions; diff --git a/source/blender/geometry/intern/trim_curves.cc b/source/blender/geometry/intern/trim_curves.cc new file mode 100644 index 00000000000..82e9ee78592 --- /dev/null +++ b/source/blender/geometry/intern/trim_curves.cc @@ -0,0 +1,1031 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ + +/** \file + * \ingroup bke + */ + +#include "BLI_array_utils.hh" +#include "BLI_length_parameterize.hh" + +#include "BKE_attribute.hh" +#include "BKE_attribute_math.hh" +#include "BKE_curves.hh" +#include "BKE_curves_utils.hh" +#include "BKE_geometry_set.hh" + +#include "GEO_trim_curves.hh" + +namespace blender::geometry { + +/* -------------------------------------------------------------------- */ +/** \name Lookup Curve Points + * \{ */ + +/** + * Find the point on the curve defined by the distance along the curve. Assumes curve resolution is + * constant for all curve segments and evaluated curve points are uniformly spaced between the + * segment endpoints in relation to the curve parameter. + * + * \param lengths: Accumulated length for the evaluated curve. + * \param sample_length: Distance along the curve to determine the #CurvePoint for. + * \param cyclic: If curve is cyclic. + * \param resolution: Curve resolution (number of evaluated points per segment). + * \param num_curve_points: Total number of control points in the curve. + * \return: Point on the piecewise segment matching the given distance. + */ +static bke::curves::CurvePoint lookup_point_uniform_spacing(const Span<float> lengths, + const float sample_length, + const bool cyclic, + const int resolution, + const int num_curve_points) +{ + BLI_assert(!cyclic || lengths.size() / resolution >= 2); + const int last_index = num_curve_points - 1; + if (sample_length <= 0.0f) { + return {{0, 1}, 0.0f}; + } + if (sample_length >= lengths.last()) { + return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : + bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; + } + int eval_index; + float eval_factor; + length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); + + const int index = eval_index / resolution; + const int next_index = (index == last_index) ? 0 : index + 1; + const float parameter = (eval_factor + eval_index) / resolution - index; + + return bke::curves::CurvePoint{{index, next_index}, parameter}; +} + +/** + * Find the point on the 'evaluated' polygonal curve. + */ +static bke::curves::CurvePoint lookup_point_polygonal(const Span<float> lengths, + const float sample_length, + const bool cyclic, + const int evaluated_size) +{ + const int last_index = evaluated_size - 1; + if (sample_length <= 0.0f) { + return {{0, 1}, 0.0f}; + } + if (sample_length >= lengths.last()) { + return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : + bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; + } + + int eval_index; + float eval_factor; + length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); + + const int next_eval_index = (eval_index == last_index) ? 0 : eval_index + 1; + return bke::curves::CurvePoint{{eval_index, next_eval_index}, eval_factor}; +} + +/** + * Find the point on a Bezier curve using the 'bezier_offsets' cache. + */ +static bke::curves::CurvePoint lookup_point_bezier(const Span<int> bezier_offsets, + const Span<float> lengths, + const float sample_length, + const bool cyclic, + const int num_curve_points) +{ + const int last_index = num_curve_points - 1; + if (sample_length <= 0.0f) { + return {{0, 1}, 0.0f}; + } + if (sample_length >= lengths.last()) { + return cyclic ? bke::curves::CurvePoint{{last_index, 0}, 1.0} : + bke::curves::CurvePoint{{last_index - 1, last_index}, 1.0}; + } + int eval_index; + float eval_factor; + length_parameterize::sample_at_length(lengths, sample_length, eval_index, eval_factor); + + /* Find the segment index from the offset mapping. */ + const int *offset = std::upper_bound(bezier_offsets.begin(), bezier_offsets.end(), eval_index); + const int left = offset - bezier_offsets.begin(); + const int right = left == last_index ? 0 : left + 1; + + const int prev_offset = left == 0 ? 0 : bezier_offsets[int64_t(left) - 1]; + const float offset_in_segment = eval_factor + (eval_index - prev_offset); + const int segment_resolution = bezier_offsets[left] - prev_offset; + const float parameter = std::clamp(offset_in_segment / segment_resolution, 0.0f, 1.0f); + + return {{left, right}, parameter}; +} + +static bke::curves::CurvePoint lookup_point_bezier(const bke::CurvesGeometry &src_curves, + const int64_t curve_index, + const Span<float> accumulated_lengths, + const float sample_length, + const bool cyclic, + const int resolution, + const int num_curve_points) +{ + if (bke::curves::bezier::has_vector_handles( + num_curve_points, + src_curves.evaluated_points_for_curve(curve_index).size(), + cyclic, + resolution)) { + const Span<int> bezier_offsets = src_curves.bezier_evaluated_offsets_for_curve(curve_index); + return lookup_point_bezier( + bezier_offsets, accumulated_lengths, sample_length, cyclic, num_curve_points); + } + else { + return lookup_point_uniform_spacing( + accumulated_lengths, sample_length, cyclic, resolution, num_curve_points); + } +} + +static bke::curves::CurvePoint lookup_curve_point(const bke::CurvesGeometry &src_curves, + const CurveType curve_type, + const int64_t curve_index, + const Span<float> accumulated_lengths, + const float sample_length, + const bool cyclic, + const int resolution, + const int num_curve_points) +{ + if (num_curve_points == 1) { + return {{0, 0}, 0.0f}; + } + + if (curve_type == CURVE_TYPE_CATMULL_ROM) { + return lookup_point_uniform_spacing( + accumulated_lengths, sample_length, cyclic, resolution, num_curve_points); + } + else if (curve_type == CURVE_TYPE_BEZIER) { + return lookup_point_bezier(src_curves, + curve_index, + accumulated_lengths, + sample_length, + cyclic, + resolution, + num_curve_points); + } + else if (curve_type == CURVE_TYPE_POLY) { + return lookup_point_polygonal(accumulated_lengths, sample_length, cyclic, num_curve_points); + } + else { + /* Handle evaluated curve. */ + BLI_assert(resolution > 0); + return lookup_point_polygonal(accumulated_lengths, + sample_length, + cyclic, + src_curves.evaluated_points_for_curve(curve_index).size()); + } +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Utility Functions + * \{ */ + +static void fill_bezier_data(bke::CurvesGeometry &dst_curves, const IndexMask selection) +{ + if (!dst_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { + return; + } + MutableSpan<float3> handle_positions_left = dst_curves.handle_positions_left_for_write(); + MutableSpan<float3> handle_positions_right = dst_curves.handle_positions_right_for_write(); + MutableSpan<int8_t> handle_types_left = dst_curves.handle_types_left_for_write(); + MutableSpan<int8_t> handle_types_right = dst_curves.handle_types_right_for_write(); + + threading::parallel_for(selection.index_range(), 4096, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange points = dst_curves.points_for_curve(curve_i); + handle_types_right.slice(points).fill(int8_t(BEZIER_HANDLE_FREE)); + handle_types_left.slice(points).fill(int8_t(BEZIER_HANDLE_FREE)); + handle_positions_left.slice(points).fill({0.0f, 0.0f, 0.0f}); + handle_positions_right.slice(points).fill({0.0f, 0.0f, 0.0f}); + } + }); +} +static void fill_nurbs_data(bke::CurvesGeometry &dst_curves, const IndexMask selection) +{ + if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { + return; + } + bke::curves::fill_points(dst_curves, selection, 0.0f, dst_curves.nurbs_weights_for_write()); +} + +template<typename T> +static int64_t copy_point_data_between_endpoints(const Span<T> src_data, + MutableSpan<T> dst_data, + const bke::curves::IndexRangeCyclic src_range, + int64_t dst_index) +{ + int64_t increment; + if (src_range.cycles()) { + increment = src_range.size_before_loop(); + dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_range.first(), increment)); + dst_index += increment; + + increment = src_range.size_after_loop(); + dst_data.slice(dst_index, increment) + .copy_from(src_data.slice(src_range.curve_range().first(), increment)); + dst_index += increment; + } + else { + increment = src_range.one_after_last() - src_range.first(); + dst_data.slice(dst_index, increment).copy_from(src_data.slice(src_range.first(), increment)); + dst_index += increment; + } + return dst_index; +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sampling Utilities + * \{ */ + +template<typename T> +static T interpolate_catmull_rom(const Span<T> src_data, + const bke::curves::CurvePoint insertion_point, + const bool src_cyclic) +{ + BLI_assert(insertion_point.index >= 0 && insertion_point.next_index < src_data.size()); + int i0; + if (insertion_point.index == 0) { + i0 = src_cyclic ? src_data.size() - 1 : insertion_point.index; + } + else { + i0 = insertion_point.index - 1; + } + int i3 = insertion_point.next_index + 1; + if (i3 == src_data.size()) { + i3 = src_cyclic ? 0 : insertion_point.next_index; + } + return bke::curves::catmull_rom::interpolate<T>(src_data[i0], + src_data[insertion_point.index], + src_data[insertion_point.next_index], + src_data[i3], + insertion_point.parameter); +} + +static bke::curves::bezier::Insertion knot_insert_bezier( + const Span<float3> positions, + const Span<float3> handles_left, + const Span<float3> handles_right, + const bke::curves::CurvePoint insertion_point) +{ + BLI_assert( + insertion_point.index + 1 == insertion_point.next_index || + (insertion_point.next_index >= 0 && insertion_point.next_index < insertion_point.index)); + return bke::curves::bezier::insert(positions[insertion_point.index], + handles_right[insertion_point.index], + handles_left[insertion_point.next_index], + positions[insertion_point.next_index], + insertion_point.parameter); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Sample Curve Interval (Trim) + * \{ */ + +/** + * Sample source curve data in the interval defined by the points [start_point, end_point]. + * Uses linear interpolation to compute the endpoints. + * + * \tparam include_start_point If False, the 'start_point' point sample will not be copied + * and not accounted for in the destination range. + * \param src_data: Source to sample from. + * \param dst_data: Destination to write samples to. + * \param src_range: Interval within [start_point, end_point] to copy from the source point domain. + * \param dst_range: Interval to copy point data to in the destination buffer. + * \param start_point: Point on the source curve to start sampling from. + * \param end_point: Last point to sample in the source curve. + */ +template<typename T, bool include_start_point = true> +static void sample_interval_linear(const Span<T> src_data, + MutableSpan<T> dst_data, + bke::curves::IndexRangeCyclic src_range, + const IndexRange dst_range, + const bke::curves::CurvePoint start_point, + const bke::curves::CurvePoint end_point) +{ + int64_t dst_index = dst_range.first(); + + if (start_point.is_controlpoint()) { + /* 'start_point' is included in the copy iteration. */ + if constexpr (!include_start_point) { + /* Skip first. */ + src_range = src_range.drop_front(); + } + } + else if constexpr (!include_start_point) { + /* Do nothing (excluded). */ + } + else { + /* General case, sample 'start_point' */ + dst_data[dst_index] = attribute_math::mix2( + start_point.parameter, src_data[start_point.index], src_data[start_point.next_index]); + ++dst_index; + } + + dst_index = copy_point_data_between_endpoints(src_data, dst_data, src_range, dst_index); + if (dst_range.size() == 1) { + BLI_assert(dst_index == dst_range.one_after_last()); + return; + } + + /* Handle last case */ + if (end_point.is_controlpoint()) { + /* 'end_point' is included in the copy iteration. */ + } + else { + dst_data[dst_index] = attribute_math::mix2( + end_point.parameter, src_data[end_point.index], src_data[end_point.next_index]); +#ifdef DEBUG + ++dst_index; +#endif + } + BLI_assert(dst_index == dst_range.one_after_last()); +} + +template<typename T> +static void sample_interval_catmull_rom(const Span<T> src_data, + MutableSpan<T> dst_data, + bke::curves::IndexRangeCyclic src_range, + const IndexRange dst_range, + const bke::curves::CurvePoint start_point, + const bke::curves::CurvePoint end_point, + const bool src_cyclic) +{ + int64_t dst_index = dst_range.first(); + + if (start_point.is_controlpoint()) { + } + else { + /* General case, sample 'start_point' */ + dst_data[dst_index] = interpolate_catmull_rom(src_data, start_point, src_cyclic); + ++dst_index; + } + + dst_index = copy_point_data_between_endpoints(src_data, dst_data, src_range, dst_index); + if (dst_range.size() == 1) { + BLI_assert(dst_index == dst_range.one_after_last()); + return; + } + + /* Handle last case */ + if (end_point.is_controlpoint()) { + /* 'end_point' is included in the copy iteration. */ + } + else { + dst_data[dst_index] = interpolate_catmull_rom(src_data, end_point, src_cyclic); +#ifdef DEBUG + ++dst_index; +#endif + } + BLI_assert(dst_index == dst_range.one_after_last()); +} + +template<bool include_start_point = true> +static void sample_interval_bezier(const Span<float3> src_positions, + const Span<float3> src_handles_l, + const Span<float3> src_handles_r, + const Span<int8_t> src_types_l, + const Span<int8_t> src_types_r, + MutableSpan<float3> dst_positions, + MutableSpan<float3> dst_handles_l, + MutableSpan<float3> dst_handles_r, + MutableSpan<int8_t> dst_types_l, + MutableSpan<int8_t> dst_types_r, + bke::curves::IndexRangeCyclic src_range, + const IndexRange dst_range, + const bke::curves::CurvePoint start_point, + const bke::curves::CurvePoint end_point) +{ + bke::curves::bezier::Insertion start_point_insert; + int64_t dst_index = dst_range.first(); + + bool start_point_trimmed = false; + if (start_point.is_controlpoint()) { + /* The 'start_point' control point is included in the copy iteration. */ + if constexpr (!include_start_point) { + src_range = src_range.drop_front(); + } + } + else if constexpr (!include_start_point) { + /* Do nothing, 'start_point' is excluded. */ + } + else { + /* General case, sample 'start_point'. */ + start_point_insert = knot_insert_bezier( + src_positions, src_handles_l, src_handles_r, start_point); + dst_positions[dst_range.first()] = start_point_insert.position; + dst_handles_l[dst_range.first()] = start_point_insert.left_handle; + dst_handles_r[dst_range.first()] = start_point_insert.right_handle; + dst_types_l[dst_range.first()] = src_types_l[start_point.index]; + dst_types_r[dst_range.first()] = src_types_r[start_point.index]; + + start_point_trimmed = true; + ++dst_index; + } + + /* Copy point data between the 'start_point' and 'end_point'. */ + int64_t increment = src_range.cycles() ? src_range.size_before_loop() : + src_range.one_after_last() - src_range.first(); + + const IndexRange dst_range_to_end(dst_index, increment); + const IndexRange src_range_to_end(src_range.first(), increment); + dst_positions.slice(dst_range_to_end).copy_from(src_positions.slice(src_range_to_end)); + dst_handles_l.slice(dst_range_to_end).copy_from(src_handles_l.slice(src_range_to_end)); + dst_handles_r.slice(dst_range_to_end).copy_from(src_handles_r.slice(src_range_to_end)); + dst_types_l.slice(dst_range_to_end).copy_from(src_types_l.slice(src_range_to_end)); + dst_types_r.slice(dst_range_to_end).copy_from(src_types_r.slice(src_range_to_end)); + dst_index += increment; + + if (dst_range.size() == 1) { + BLI_assert(dst_index == dst_range.one_after_last()); + return; + } + + increment = src_range.size_after_loop(); + if (src_range.cycles() && increment > 0) { + const IndexRange dst_range_looped(dst_index, increment); + const IndexRange src_range_looped(src_range.curve_range().first(), increment); + dst_positions.slice(dst_range_looped).copy_from(src_positions.slice(src_range_looped)); + dst_handles_l.slice(dst_range_looped).copy_from(src_handles_l.slice(src_range_looped)); + dst_handles_r.slice(dst_range_looped).copy_from(src_handles_r.slice(src_range_looped)); + dst_types_l.slice(dst_range_looped).copy_from(src_types_l.slice(src_range_looped)); + dst_types_r.slice(dst_range_looped).copy_from(src_types_r.slice(src_range_looped)); + dst_index += increment; + } + + if (start_point_trimmed) { + dst_handles_l[dst_range.first() + 1] = start_point_insert.handle_next; + /* No need to set handle type (remains the same)! */ + } + + /* Handle 'end_point' */ + bke::curves::bezier::Insertion end_point_insert; + if (end_point.is_controlpoint()) { + /* Do nothing, the 'end_point' control point is included in the copy iteration. */ + } + else { + /* Trimmed in both ends within the same (and only) segment! Ensure both end points is not a + * loop. */ + if (start_point_trimmed && start_point.index == end_point.index && + start_point.parameter <= end_point.parameter) { + + /* Copy following segment control point. */ + dst_positions[dst_index] = src_positions[end_point.next_index]; + dst_handles_r[dst_index] = src_handles_r[end_point.next_index]; + + /* Compute interpolation in the result curve. */ + const float parameter = (end_point.parameter - start_point.parameter) / + (1.0f - start_point.parameter); + end_point_insert = knot_insert_bezier( + dst_positions, + dst_handles_l, + dst_handles_r, + {{int(dst_range.first()), int(dst_range.first() + 1)}, parameter}); + } + else { + /* General case, compute the insertion point. */ + end_point_insert = knot_insert_bezier( + src_positions, src_handles_l, src_handles_r, end_point); + } + + dst_handles_r[dst_index - 1] = end_point_insert.handle_prev; + dst_types_r[dst_index - 1] = src_types_l[end_point.index]; + + dst_handles_l[dst_index] = end_point_insert.left_handle; + dst_handles_r[dst_index] = end_point_insert.right_handle; + dst_positions[dst_index] = end_point_insert.position; + dst_types_l[dst_index] = src_types_l[end_point.next_index]; + dst_types_r[dst_index] = src_types_r[end_point.next_index]; +#ifdef DEBUG + ++dst_index; +#endif // DEBUG + } + BLI_assert(dst_index == dst_range.one_after_last()); +} + +/** \} */ + +/* -------------------------------------------------------------------- */ +/** \name Trim Curves + * \{ */ + +static void trim_attribute_linear(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + const Span<bke::curves::IndexRangeCyclic> src_ranges, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + + sample_interval_linear<T>(attribute.src.template typed<T>().slice(src_points), + attribute.dst.span.typed<T>(), + src_ranges[curve_i], + dst_curves.points_for_curve(curve_i), + start_points[curve_i], + end_points[curve_i]); + } + }); + }); + } +} + +static void trim_polygonal_curves(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + const Span<bke::curves::IndexRangeCyclic> src_ranges, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + sample_interval_linear<float3>(src_positions.slice(src_points), + dst_positions, + src_ranges[curve_i], + dst_points, + start_points[curve_i], + end_points[curve_i]); + } + }); + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); + trim_attribute_linear(src_curves, + dst_curves, + selection, + start_points, + end_points, + src_ranges, + transfer_attributes); +} + +static void trim_catmull_rom_curves(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + const Span<bke::curves::IndexRangeCyclic> src_ranges, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + const VArray<bool> src_cyclic = src_curves.cyclic(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + sample_interval_catmull_rom<float3>(src_positions.slice(src_points), + dst_positions, + src_ranges[curve_i], + dst_points, + start_points[curve_i], + end_points[curve_i], + src_cyclic[curve_i]); + } + }); + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); + + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + sample_interval_catmull_rom<T>(attribute.src.template typed<T>().slice(src_points), + attribute.dst.span.typed<T>(), + src_ranges[curve_i], + dst_points, + start_points[curve_i], + end_points[curve_i], + src_cyclic[curve_i]); + } + }); + }); + } +} + +static void trim_bezier_curves(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + const Span<bke::curves::IndexRangeCyclic> src_ranges, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_positions = src_curves.positions(); + const VArraySpan<int8_t> src_types_l{src_curves.handle_types_left()}; + const VArraySpan<int8_t> src_types_r{src_curves.handle_types_right()}; + const Span<float3> src_handles_l = src_curves.handle_positions_left(); + const Span<float3> src_handles_r = src_curves.handle_positions_right(); + + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + MutableSpan<int8_t> dst_types_l = dst_curves.handle_types_left_for_write(); + MutableSpan<int8_t> dst_types_r = dst_curves.handle_types_right_for_write(); + MutableSpan<float3> dst_handles_l = dst_curves.handle_positions_left_for_write(); + MutableSpan<float3> dst_handles_r = dst_curves.handle_positions_right_for_write(); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange src_points = src_curves.points_for_curve(curve_i); + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + + sample_interval_bezier(src_positions.slice(src_points), + src_handles_l.slice(src_points), + src_handles_r.slice(src_points), + src_types_l.slice(src_points), + src_types_r.slice(src_points), + dst_positions, + dst_handles_l, + dst_handles_r, + dst_types_l, + dst_types_r, + src_ranges[curve_i], + dst_points, + start_points[curve_i], + end_points[curve_i]); + } + }); + fill_nurbs_data(dst_curves, selection); + trim_attribute_linear(src_curves, + dst_curves, + selection, + start_points, + end_points, + src_ranges, + transfer_attributes); +} + +static void trim_evaluated_curves(const bke::CurvesGeometry &src_curves, + bke::CurvesGeometry &dst_curves, + const IndexMask selection, + const Span<bke::curves::CurvePoint> start_points, + const Span<bke::curves::CurvePoint> end_points, + const Span<bke::curves::IndexRangeCyclic> src_ranges, + MutableSpan<bke::AttributeTransferData> transfer_attributes) +{ + const Span<float3> src_eval_positions = src_curves.evaluated_positions(); + MutableSpan<float3> dst_positions = dst_curves.positions_for_write(); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + const IndexRange dst_points = dst_curves.points_for_curve(curve_i); + const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); + + sample_interval_linear<float3>(src_eval_positions.slice(src_evaluated_points), + dst_positions, + src_ranges[curve_i], + dst_points, + start_points[curve_i], + end_points[curve_i]); + } + }); + fill_bezier_data(dst_curves, selection); + fill_nurbs_data(dst_curves, selection); + + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute_math::convert_to_static_type(attribute.meta_data.data_type, [&](auto dummy) { + using T = decltype(dummy); + + threading::parallel_for(selection.index_range(), 512, [&](const IndexRange range) { + for (const int64_t curve_i : selection.slice(range)) { + /* Interpolate onto the evaluated point domain and sample the evaluated domain. */ + const IndexRange src_evaluated_points = src_curves.evaluated_points_for_curve(curve_i); + GArray evaluated_data(CPPType::get<T>(), src_evaluated_points.size()); + GMutableSpan evaluated_span = evaluated_data.as_mutable_span(); + src_curves.interpolate_to_evaluated( + curve_i, attribute.src.slice(src_curves.points_for_curve(curve_i)), evaluated_span); + sample_interval_linear<T>(evaluated_span.typed<T>(), + attribute.dst.span.typed<T>(), + src_ranges[curve_i], + dst_curves.points_for_curve(curve_i), + start_points[curve_i], + end_points[curve_i]); + } + }); + }); + } +} + +/* -------------------------------------------------------------------- */ +/** \name Compute trim parameters + * \{ */ + +static float trim_sample_length(const Span<float> accumulated_lengths, + const float sample_length, + const GeometryNodeCurveSampleMode mode) +{ + float length = mode == GEO_NODE_CURVE_SAMPLE_FACTOR ? + sample_length * accumulated_lengths.last() : + sample_length; + return std::clamp(length, 0.0f, accumulated_lengths.last()); +} + +/** + * Compute the selection for the given curve type. Tracks indices for splitting the selection if + * there are curves reduced to a single point. + */ +static void compute_curve_trim_parameters(const bke::CurvesGeometry &curves, + const IndexMask selection, + const VArray<float> &starts, + const VArray<float> &ends, + const GeometryNodeCurveSampleMode mode, + MutableSpan<int> dst_curve_size, + MutableSpan<int8_t> dst_curve_types, + MutableSpan<bke::curves::CurvePoint> start_points, + MutableSpan<bke::curves::CurvePoint> end_points, + MutableSpan<bke::curves::IndexRangeCyclic> src_ranges) +{ + const VArray<bool> src_cyclic = curves.cyclic(); + const VArray<int> resolution = curves.resolution(); + const VArray<int8_t> curve_types = curves.curve_types(); + + /* Compute. */ + threading::parallel_for(selection.index_range(), 128, [&](const IndexRange selection_range) { + for (const int64_t curve_i : selection.slice(selection_range)) { + CurveType curve_type = CurveType(curve_types[curve_i]); + + int point_count; + if (curve_type == CURVE_TYPE_NURBS) { + dst_curve_types[curve_i] = CURVE_TYPE_POLY; + point_count = curves.evaluated_points_for_curve(curve_i).size(); + } + else { + dst_curve_types[curve_i] = curve_type; + point_count = curves.points_num_for_curve(curve_i); + } + if (point_count == 1) { + /* Single point. */ + dst_curve_size[curve_i] = 1; + src_ranges[curve_i] = bke::curves::IndexRangeCyclic(0, 0, 1, 1); + start_points[curve_i] = {0, 0, 0.0f}; + end_points[curve_i] = {0, 0, 0.0f}; + continue; + } + + const bool cyclic = src_cyclic[curve_i]; + const Span<float> lengths = curves.evaluated_lengths_for_curve(curve_i, cyclic); + BLI_assert(lengths.size() > 0); + + const float start_length = trim_sample_length(lengths, starts[curve_i], mode); + float end_length; + + bool equal_sample_point; + if (cyclic) { + end_length = trim_sample_length(lengths, ends[curve_i], mode); + const float cyclic_start = start_length == lengths.last() ? 0.0f : start_length; + const float cyclic_end = end_length == lengths.last() ? 0.0f : end_length; + equal_sample_point = cyclic_start == cyclic_end; + } + else { + end_length = ends[curve_i] <= starts[curve_i] ? + start_length : + trim_sample_length(lengths, ends[curve_i], mode); + equal_sample_point = start_length == end_length; + } + + start_points[curve_i] = lookup_curve_point(curves, + curve_type, + curve_i, + lengths, + start_length, + cyclic, + resolution[curve_i], + point_count); + if (equal_sample_point) { + end_points[curve_i] = start_points[curve_i]; + if (end_length <= start_length) { + /* Single point. */ + dst_curve_size[curve_i] = 1; + src_ranges[curve_i] = bke::curves::IndexRangeCyclic::get_range_from_size( + start_points[curve_i].index, + start_points[curve_i].is_controlpoint(), /* Only iterate if control point. */ + point_count); + } + else { + /* Split. */ + src_ranges[curve_i] = bke::curves::IndexRangeCyclic::get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], point_count) + .push_loop(); + const int count = 1 + !start_points[curve_i].is_controlpoint() + point_count; + BLI_assert(count > 1); + dst_curve_size[curve_i] = count; + } + } + else { + /* General case. */ + end_points[curve_i] = lookup_curve_point(curves, + curve_type, + curve_i, + lengths, + end_length, + cyclic, + resolution[curve_i], + point_count); + + src_ranges[curve_i] = bke::curves::IndexRangeCyclic::get_range_between_endpoints( + start_points[curve_i], end_points[curve_i], point_count); + const int count = src_ranges[curve_i].size() + !start_points[curve_i].is_controlpoint() + + !end_points[curve_i].is_controlpoint(); + BLI_assert(count > 1); + dst_curve_size[curve_i] = count; + } + BLI_assert(dst_curve_size[curve_i] > 0); + } + }); +} + +/** \} */ + +bke::CurvesGeometry trim_curves(const bke::CurvesGeometry &src_curves, + const IndexMask selection, + const VArray<float> &starts, + const VArray<float> &ends, + const GeometryNodeCurveSampleMode mode) +{ + BLI_assert(selection.size() > 0); + BLI_assert(selection.last() <= src_curves.curves_num()); + BLI_assert(starts.size() == src_curves.curves_num()); + BLI_assert(starts.size() == ends.size()); + src_curves.ensure_evaluated_lengths(); + + Vector<int64_t> inverse_selection_indices; + const IndexMask inverse_selection = selection.invert(src_curves.curves_range(), + inverse_selection_indices); + + /* Create destination curves. */ + bke::CurvesGeometry dst_curves(0, src_curves.curves_num()); + MutableSpan<int> dst_curve_offsets = dst_curves.offsets_for_write(); + MutableSpan<int8_t> dst_curve_types = dst_curves.curve_types_for_write(); + Array<bke::curves::CurvePoint, 12> start_points(src_curves.curves_num()); + Array<bke::curves::CurvePoint, 12> end_points(src_curves.curves_num()); + Array<bke::curves::IndexRangeCyclic, 12> src_ranges(src_curves.curves_num()); + + if (src_curves.has_curve_with_type({CURVE_TYPE_BEZIER, CURVE_TYPE_NURBS})) { + src_curves.ensure_evaluated_offsets(); + if (src_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { + src_curves.evaluated_positions(); + } + } + + /* Compute destination curves. */ + compute_curve_trim_parameters(src_curves, + selection, + starts, + ends, + mode, + dst_curve_offsets, + dst_curve_types, + start_points, + end_points, + src_ranges); + + /* Transfer copied curves parameters. */ + const VArray<int8_t> src_curve_types = src_curves.curve_types(); + threading::parallel_for( + inverse_selection.index_range(), 4096, [&](const IndexRange selection_range) { + for (const int64_t curve_i : inverse_selection.slice(selection_range)) { + dst_curve_offsets[curve_i] = src_curves.points_num_for_curve(curve_i); + dst_curve_types[curve_i] = src_curve_types[curve_i]; + } + }); + /* Finalize and update the geometry container. */ + bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); + dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); + dst_curves.update_curve_types(); + + /* Populate curve domain. */ + const bke::AttributeAccessor src_attributes = src_curves.attributes(); + bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); + Set<std::string> transfer_curve_skip = {"cyclic", "curve_type", "nurbs_order", "knots_mode"}; + if (dst_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { + /* If a NURBS curve is copied keep */ + transfer_curve_skip.remove("nurbs_order"); + transfer_curve_skip.remove("knots_mode"); + } + bke::copy_attribute_domain( + src_attributes, dst_attributes, selection, ATTR_DOMAIN_CURVE, transfer_curve_skip); + + /* Fetch custom point domain attributes for transfer (copy). */ + Vector<bke::AttributeTransferData> transfer_attributes = bke::retrieve_attributes_for_transfer( + src_attributes, + dst_attributes, + ATTR_DOMAIN_MASK_POINT, + {"position", + "handle_left", + "handle_right", + "handle_type_left", + "handle_type_right", + "nurbs_weight"}); + + auto trim_catmull = [&](const IndexMask selection) { + trim_catmull_rom_curves(src_curves, + dst_curves, + selection, + start_points, + end_points, + src_ranges, + transfer_attributes); + }; + auto trim_poly = [&](const IndexMask selection) { + trim_polygonal_curves(src_curves, + dst_curves, + selection, + start_points, + end_points, + src_ranges, + transfer_attributes); + }; + auto trim_bezier = [&](const IndexMask selection) { + trim_bezier_curves(src_curves, + dst_curves, + selection, + start_points, + end_points, + src_ranges, + transfer_attributes); + }; + auto trim_evaluated = [&](const IndexMask selection) { + /* Ensure evaluated positions are available. */ + src_curves.ensure_evaluated_offsets(); + src_curves.evaluated_positions(); + trim_evaluated_curves(src_curves, + dst_curves, + selection, + start_points, + end_points, + src_ranges, + transfer_attributes); + }; + + /* Populate point domain. */ + bke::curves::foreach_curve_by_type(src_curves.curve_types(), + src_curves.curve_type_counts(), + selection, + trim_catmull, + trim_poly, + trim_bezier, + trim_evaluated); + + /* Cleanup/close context */ + for (bke::AttributeTransferData &attribute : transfer_attributes) { + attribute.dst.finish(); + } + + /* Copy unselected */ + if (!inverse_selection.is_empty()) { + transfer_curve_skip.remove("cyclic"); + bke::copy_attribute_domain( + src_attributes, dst_attributes, inverse_selection, ATTR_DOMAIN_CURVE, transfer_curve_skip); + /* Trim curves are no longer cyclic. If all curves are trimmed, this will be set implicitly. */ + dst_curves.cyclic_for_write().fill_indices(selection, false); + + Set<std::string> copy_point_skip; + if (!dst_curves.has_curve_with_type(CURVE_TYPE_NURBS) && + src_curves.has_curve_with_type(CURVE_TYPE_NURBS)) { + copy_point_skip.add("nurbs_weight"); + } + + /* Copy point domain. */ + for (auto &attribute : bke::retrieve_attributes_for_transfer( + src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT, copy_point_skip)) { + bke::curves::copy_point_data( + src_curves, dst_curves, inverse_selection, attribute.src, attribute.dst.span); + attribute.dst.finish(); + } + } + + dst_curves.tag_topology_changed(); + return dst_curves; +} + +/** \} */ + +} // namespace blender::geometry diff --git a/source/blender/geometry/intern/uv_parametrizer.cc b/source/blender/geometry/intern/uv_parametrizer.cc index f074febe23a..070703f5228 100644 --- a/source/blender/geometry/intern/uv_parametrizer.cc +++ b/source/blender/geometry/intern/uv_parametrizer.cc @@ -22,11 +22,11 @@ /* Utils */ #define param_assert(condition) \ - if (!(condition)) { /*printf("Assertion %s:%d\n", __FILE__, __LINE__); abort();*/ \ + if (!(condition)) { /* `printf("Assertion %s:%d\n", __FILE__, __LINE__); abort();` */ \ } \ (void)0 #define param_warning(message) \ - {/*printf("Warning %s:%d: %s\n", __FILE__, __LINE__, message);*/}(void)0 + {/* `printf("Warning %s:%d: %s\n", __FILE__, __LINE__, message);` */}(void)0 /* Special Purpose Hash */ @@ -195,7 +195,7 @@ static int PHashSizes[] = { 1048583, 2097169, 4194319, 8388617, 16777259, 33554467, 67108879, 134217757, 268435459, }; -#define PHASH_hash(ph, item) (((uintptr_t)(item)) % ((uint)(ph)->cursize)) +#define PHASH_hash(ph, item) (uintptr_t(item) % uint((ph)->cursize)) #define PHASH_edge(v1, v2) (((v1) < (v2)) ? ((v1)*39) ^ ((v2)*31) : ((v1)*31) ^ ((v2)*39)) static PHash *phash_new(PHashLink **list, int sizehint) @@ -319,7 +319,7 @@ static void fix_large_angle(const float v_fix[3], float *r_a1, float *r_a2) { - const float max_angle = (float)M_PI * (179.0f / 180.0f); + const float max_angle = float(M_PI) * (179.0f / 180.0f); const float fix_amount = *r_fix - max_angle; if (fix_amount < 0.0f) { return; /* angle is reasonable, i.e. less than 179 degrees. */ @@ -1903,7 +1903,7 @@ static bool p_collapse_allowed_geometric(PEdge *edge, PEdge *pair) if (p_vert_interior(oldv)) { /* HLSCM criterion: angular defect smaller than threshold. */ - if (fabsf(angulardefect) > (float)(M_PI * 30.0 / 180.0)) { + if (fabsf(angulardefect) > float(M_PI * 30.0 / 180.0)) { return false; } } @@ -2465,7 +2465,7 @@ static float p_abf_compute_gradient(PAbfSystem *sys, PChart *chart) norm += galpha1 * galpha1 + galpha2 * galpha2 + galpha3 * galpha3; - gtriangle = sys->alpha[e1->u.id] + sys->alpha[e2->u.id] + sys->alpha[e3->u.id] - (float)M_PI; + gtriangle = sys->alpha[e1->u.id] + sys->alpha[e2->u.id] + sys->alpha[e3->u.id] - float(M_PI); sys->bTriangle[f->u.id] = -gtriangle; norm += gtriangle * gtriangle; } @@ -2696,8 +2696,8 @@ static bool p_abf_matrix_invert(PAbfSystem *sys, PChart *chart) /* clamp */ PEdge *e = f->edge; do { - if (sys->alpha[e->u.id] > (float)M_PI) { - sys->alpha[e->u.id] = (float)M_PI; + if (sys->alpha[e->u.id] > float(M_PI)) { + sys->alpha[e->u.id] = float(M_PI); } else if (sys->alpha[e->u.id] < 0.0f) { sys->alpha[e->u.id] = 0.0f; @@ -2706,8 +2706,8 @@ static bool p_abf_matrix_invert(PAbfSystem *sys, PChart *chart) } for (int i = 0; i < ninterior; i++) { - sys->lambdaPlanar[i] += (float)EIG_linear_solver_variable_get(context, 0, i); - sys->lambdaLength[i] += (float)EIG_linear_solver_variable_get(context, 0, ninterior + i); + sys->lambdaPlanar[i] += float(EIG_linear_solver_variable_get(context, 0, i)); + sys->lambdaLength[i] += float(EIG_linear_solver_variable_get(context, 0, ninterior + i)); } } @@ -2780,7 +2780,7 @@ static bool p_chart_abf_solve(PChart *chart) e = e->next->next->pair; } while (e && (e != v->edge)); - scale = (anglesum == 0.0f) ? 0.0f : 2.0f * (float)M_PI / anglesum; + scale = (anglesum == 0.0f) ? 0.0f : 2.0f * float(M_PI) / anglesum; e = v->edge; do { @@ -3061,7 +3061,7 @@ static void p_chart_lscm_begin(PChart *chart, bool live, bool abf) } } - if ((live && (!select || !deselect))) { + if (live && (!select || !deselect)) { chart->u.lscm.context = nullptr; } else { @@ -3409,7 +3409,7 @@ static void p_chart_stretch_minimize(PChart *chart, RNG *rng) trusted_radius /= 2 * nedges; - random_angle = BLI_rng_get_float(rng) * 2.0f * (float)M_PI; + random_angle = BLI_rng_get_float(rng) * 2.0f * float(M_PI); dir[0] = trusted_radius * cosf(random_angle); dir[1] = trusted_radius * sinf(random_angle); @@ -3588,7 +3588,7 @@ static float p_chart_minimum_area_angle(PChart *chart) p2 = points[i]; p3 = (i == npoints - 1) ? points[0] : points[i + 1]; - angles[i] = (float)M_PI - p_vec2_angle(p1->uv, p2->uv, p3->uv); + angles[i] = float(M_PI) - p_vec2_angle(p1->uv, p2->uv, p3->uv); if (points[i]->uv[1] < miny) { miny = points[i]->uv[1]; @@ -3628,7 +3628,7 @@ static float p_chart_minimum_area_angle(PChart *chart) minarea = 1e10; minangle = 0.0; - while (rotated <= (float)M_PI_2) { /* INVESTIGATE: how far to rotate? */ + while (rotated <= float(M_PI_2)) { /* INVESTIGATE: how far to rotate? */ /* rotate with the smallest angle */ i_min = 0; mina = 1e10; @@ -3675,8 +3675,8 @@ static float p_chart_minimum_area_angle(PChart *chart) } /* try keeping rotation as small as possible */ - if (minangle > (float)M_PI_4) { - minangle -= (float)M_PI_2; + if (minangle > float(M_PI_4)) { + minangle -= float(M_PI_2); } MEM_freeN(angles); @@ -3869,9 +3869,9 @@ static void p_add_ngon(ParamHandle *handle, Heap *heap = handle->polyfill_heap; uint nfilltri = nverts - 2; uint(*tris)[3] = static_cast<uint(*)[3]>( - BLI_memarena_alloc(arena, sizeof(*tris) * (size_t)nfilltri)); + BLI_memarena_alloc(arena, sizeof(*tris) * size_t(nfilltri))); float(*projverts)[2] = static_cast<float(*)[2]>( - BLI_memarena_alloc(arena, sizeof(*projverts) * (size_t)nverts)); + BLI_memarena_alloc(arena, sizeof(*projverts) * size_t(nverts))); /* Calc normal, flipped: to get a positive 2d cross product. */ float normal[3]; @@ -4209,7 +4209,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, box->index = i; /* Warning this index skips chart->has_pins boxes. */ if (margin > 0.0f) { - area += (double)sqrtf(box->w * box->h); + area += double(sqrtf(box->w * box->h)); } } @@ -4218,7 +4218,7 @@ void GEO_uv_parametrizer_pack(ParamHandle *handle, * ...Without using the area running pack multiple times also gives a bad feedback loop. * multiply by 0.1 so the margin value from the UI can be from * 0.0 to 1.0 but not give a massive margin */ - margin = (margin * (float)area) * 0.1f; + margin = (margin * float(area)) * 0.1f; unpacked = 0; for (i = 0; i < handle->ncharts; i++) { chart = handle->charts[i]; @@ -4342,7 +4342,7 @@ void GEO_uv_parametrizer_average(ParamHandle *phandle, /* Compute correction transform. */ float t[2][2]; t[0][0] = scale_factor_u; - t[1][0] = clamp_f((float)(scale_cross / weight_sum), -0.5f, 0.5f); + t[1][0] = clamp_f(float(scale_cross / weight_sum), -0.5f, 0.5f); t[0][1] = 0; t[1][1] = 1.0f / scale_factor_u; |