Changeset View
Changeset View
Standalone View
Standalone View
source/blender/geometry/intern/fillet_curves.cc
| Show First 20 Lines • Show All 60 Lines • ▼ Show 20 Lines | ||||||||||||||||||||||||
| { | { | |||||||||||||||||||||||
| attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { | attribute_math::convert_to_static_type(dst.type(), [&](auto dummy) { | |||||||||||||||||||||||
| using T = decltype(dummy); | using T = decltype(dummy); | |||||||||||||||||||||||
| duplicate_fillet_point_data( | duplicate_fillet_point_data( | |||||||||||||||||||||||
| src_curves, dst_curves, selection, point_offsets, src.typed<T>(), dst.typed<T>()); | src_curves, dst_curves, selection, point_offsets, src.typed<T>(), dst.typed<T>()); | |||||||||||||||||||||||
| }); | }); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| /** | ||||||||||||||||||||||||
| * For each curve, identify which point ranges overlap with the curve's point range. | ||||||||||||||||||||||||
| * Example: | ||||||||||||||||||||||||
| * Given a curve geometry with two curves, with point ranges | ||||||||||||||||||||||||
| * [(start: 0, size: 5), (start: 6, size: 5)], | ||||||||||||||||||||||||
HooglyBooglyUnsubmitted Done Inline Actions
HooglyBoogly: | ||||||||||||||||||||||||
| * Given point ranges | ||||||||||||||||||||||||
| * [(start: 0, size: 3), (start: 7, size: 1), (start: 9, size: 1)], | ||||||||||||||||||||||||
| * Returns [(start: 0, size: 1), (start:1, size: 2)]. | ||||||||||||||||||||||||
| */ | ||||||||||||||||||||||||
| static Vector<IndexRange> calculate_overlapping_ranges(const bke::CurvesGeometry &curves, | ||||||||||||||||||||||||
| const Span<IndexRange> ranges) | ||||||||||||||||||||||||
| { | ||||||||||||||||||||||||
| Vector<IndexRange> overlapping_ranges(curves.curves_num(), IndexRange(0, 0)); | ||||||||||||||||||||||||
| if (ranges.is_empty()) { | ||||||||||||||||||||||||
| return overlapping_ranges; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| int next_curve_index = 0; | ||||||||||||||||||||||||
| for (const int curve_i : curves.curves_range()) { | ||||||||||||||||||||||||
| // For each curve, loop through all point selection ranges and keep track of which | ||||||||||||||||||||||||
| // ones overlap with the curve's own point range. | ||||||||||||||||||||||||
| const IndexRange curve_points = curves.points_for_curve(curve_i); | ||||||||||||||||||||||||
| int starting_range_index = -1; | ||||||||||||||||||||||||
ModerUnsubmitted Not Done Inline ActionsI'm not sure about this, so it's just a question. Is it possible to skip max() below if you just do 0 here? Moder: I'm not sure about this, so it's just a question. Is it possible to skip max() below if you… | ||||||||||||||||||||||||
| int size = 0; | ||||||||||||||||||||||||
| while (curve_points.overlaps(ranges[next_curve_index])) { | ||||||||||||||||||||||||
| if (starting_range_index < 0) { | ||||||||||||||||||||||||
| starting_range_index = next_curve_index; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| size++; | ||||||||||||||||||||||||
| if (curve_points.contains(ranges[next_curve_index].last())) { | ||||||||||||||||||||||||
| if (next_curve_index >= ranges.size() - 1) { | ||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| next_curve_index++; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| else { | ||||||||||||||||||||||||
| break; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| overlapping_ranges[curve_i] = {std::max(starting_range_index, 0), size}; | ||||||||||||||||||||||||
ModerUnsubmitted Not Done Inline Actions
Moder: | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| return overlapping_ranges; | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| static void calculate_result_offsets(const bke::CurvesGeometry &src_curves, | static void calculate_result_offsets(const bke::CurvesGeometry &src_curves, | |||||||||||||||||||||||
| const IndexMask selection, | const IndexMask point_selection, | |||||||||||||||||||||||
Done Inline Actions/home/hans/Blender-Git/blender/source/blender/geometry/intern/fillet_curves.cc:108:59: warning: unused parameter ‘radii’ [-Wunused-parameter]
108 | const VArray<float> &radii,
| ~~~~~~~~~~~~~~~~~~~~~^~~~~HooglyBoogly: ```
/home/hans/Blender-Git/blender/source/blender/geometry/intern/fillet_curves.cc:108:59… | ||||||||||||||||||||||||
| const Span<IndexRange> unselected_ranges, | ||||||||||||||||||||||||
| const VArray<float> &radii, | ||||||||||||||||||||||||
| const VArray<int> &counts, | const VArray<int> &counts, | |||||||||||||||||||||||
| const Span<bool> cyclic, | const Span<bool> cyclic, | |||||||||||||||||||||||
| MutableSpan<int> dst_curve_offsets, | MutableSpan<int> dst_curve_offsets, | |||||||||||||||||||||||
| MutableSpan<int> dst_point_offsets) | MutableSpan<int> dst_point_offsets) | |||||||||||||||||||||||
| { | { | |||||||||||||||||||||||
| /* Fill the offsets array with the curve point counts, then accumulate them to form offsets. */ | const Vector<IndexRange> unselected_points = point_selection.extract_ranges_invert( | |||||||||||||||||||||||
| bke::curves::fill_curve_counts(src_curves, unselected_ranges, dst_curve_offsets); | src_curves.points_range()); | |||||||||||||||||||||||
Done Inline ActionsInverting selection is counter intuitive, but is the most space efficient way I can think of. Open to suggestions though :) yemount: Inverting selection is counter intuitive, but is the most space efficient way I can think of. | ||||||||||||||||||||||||
| threading::parallel_for(selection.index_range(), 512, [&](IndexRange range) { | /* For each curve, store the range of overlapping unselected point ranges. */ | |||||||||||||||||||||||
| for (const int curve_i : selection.slice(range)) { | const Vector<IndexRange> unselected_points_per_curve = calculate_overlapping_ranges( | |||||||||||||||||||||||
| src_curves, unselected_points); | ||||||||||||||||||||||||
| threading::parallel_for(src_curves.curves_range(), 512, [&](IndexRange range) { | ||||||||||||||||||||||||
| for (const int curve_i : range) { | ||||||||||||||||||||||||
Done Inline Actionssrc_curves.curves_range().slice(range)) is equivalent to just range The same thing appears a couple times in the patch HooglyBoogly: `src_curves.curves_range().slice(range))` is equivalent to just `range`
The same thing appears… | ||||||||||||||||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | const IndexRange src_points = src_curves.points_for_curve(curve_i); | |||||||||||||||||||||||
| const IndexRange offsets_range = curve_dst_offsets(src_points, curve_i); | const IndexRange offsets_range = curve_dst_offsets(src_points, curve_i); | |||||||||||||||||||||||
| MutableSpan<int> point_offsets = dst_point_offsets.slice(offsets_range); | MutableSpan<int> point_offsets = dst_point_offsets.slice(offsets_range); | |||||||||||||||||||||||
| MutableSpan<int> point_counts = point_offsets.drop_back(1); | MutableSpan<int> point_counts = point_offsets.drop_back(1); | |||||||||||||||||||||||
| counts.materialize_compressed(src_points, point_counts); | counts.materialize_compressed(src_points, point_counts); | |||||||||||||||||||||||
| for (int &count : point_counts) { | for (int &count : point_counts) { | |||||||||||||||||||||||
| /* Make sure the number of cuts is greater than zero and add one for the existing point. */ | /* Make sure the number of cuts is greater than zero and add one for the existing point. */ | |||||||||||||||||||||||
| count = std::max(count, 0) + 1; | count = std::max(count, 0) + 1; | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| if (!cyclic[curve_i]) { | if (!cyclic[curve_i]) { | |||||||||||||||||||||||
| /* Endpoints on non-cyclic curves cannot be filleted. */ | /* Endpoints on non-cyclic curves cannot be filleted. */ | |||||||||||||||||||||||
| point_counts.first() = 1; | point_counts.first() = 1; | |||||||||||||||||||||||
| point_counts.last() = 1; | point_counts.last() = 1; | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| /* Implicitly "deselect" points with zero radius. */ | ||||||||||||||||||||||||
| devirtualize_varray(radii, [&](const auto radii) { | /* Deselect points. */ | |||||||||||||||||||||||
| for (const int i : IndexRange(src_points.size())) { | for (const int unselected_range_i : unselected_points_per_curve[curve_i]) { | |||||||||||||||||||||||
| if (radii[src_points[i]] == 0.0f) { | IndexRange unselected_ranges = unselected_points[unselected_range_i]; | |||||||||||||||||||||||
| point_counts[i] = 1; | const int min_i = std::max(unselected_ranges.start(), src_points.start()); | |||||||||||||||||||||||
| const int max_i = std::min(unselected_ranges.one_after_last(), | ||||||||||||||||||||||||
Done Inline Actions
Better type safety and less use of macros is always nice HooglyBoogly: Better type safety and less use of macros is always nice | ||||||||||||||||||||||||
| src_points.one_after_last()); | ||||||||||||||||||||||||
Done Inline ActionsThis should be equivalent to src_points.one_after_last() HooglyBoogly: This should be equivalent to `src_points.one_after_last()` | ||||||||||||||||||||||||
| for (int i = min_i; i < max_i; i++) { | ||||||||||||||||||||||||
| int local_point_i = i - src_points.start(); | ||||||||||||||||||||||||
| point_counts[local_point_i] = 1; | ||||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
ModerUnsubmitted Not Done Inline Actions
Moder: | ||||||||||||||||||||||||
ModerUnsubmitted Not Done Inline Actionsconst IndexRange intersection = unselected_ranges.intersect(src_points); for (const int index : intersection.index_range().shift(src_points.start() - intersection.start())) { point_counts[index] = 1; } Moder: ```lang=C++
const IndexRange intersection = unselected_ranges.intersect(src_points);
for… | ||||||||||||||||||||||||
| }); | ||||||||||||||||||||||||
| bke::curves::accumulate_counts_to_offsets(point_offsets); | bke::curves::accumulate_counts_to_offsets(point_offsets); | |||||||||||||||||||||||
| dst_curve_offsets[curve_i] = point_offsets.last(); | dst_curve_offsets[curve_i] = point_offsets.last(); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| }); | }); | |||||||||||||||||||||||
| bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); | bke::curves::accumulate_counts_to_offsets(dst_curve_offsets); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| ▲ Show 20 Lines • Show All 277 Lines • ▼ Show 20 Lines | for (const int i_src : range) { | |||||||||||||||||||||||
| dst_handles_l[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i], | dst_handles_l[i] = bke::curves::bezier::calculate_vector_handle(dst_positions[i], | |||||||||||||||||||||||
| dst_positions[i + 1]); | dst_positions[i + 1]); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| }); | }); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, | static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, | |||||||||||||||||||||||
| const IndexMask curve_selection, | const IndexMask point_selection, | |||||||||||||||||||||||
| const VArray<float> &radius_input, | const VArray<float> &radius_input, | |||||||||||||||||||||||
| const VArray<int> &counts, | const VArray<int> &counts, | |||||||||||||||||||||||
| const bool limit_radius, | const bool limit_radius, | |||||||||||||||||||||||
| const bool use_bezier_mode) | const bool use_bezier_mode) | |||||||||||||||||||||||
| { | { | |||||||||||||||||||||||
| const Vector<IndexRange> unselected_ranges = curve_selection.extract_ranges_invert( | ||||||||||||||||||||||||
| src_curves.curves_range()); | ||||||||||||||||||||||||
| const Span<float3> positions = src_curves.positions(); | const Span<float3> positions = src_curves.positions(); | |||||||||||||||||||||||
| const VArraySpan<bool> cyclic{src_curves.cyclic()}; | const VArraySpan<bool> cyclic{src_curves.cyclic()}; | |||||||||||||||||||||||
| const bke::AttributeAccessor src_attributes = src_curves.attributes(); | const bke::AttributeAccessor src_attributes = src_curves.attributes(); | |||||||||||||||||||||||
| bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); | bke::CurvesGeometry dst_curves = bke::curves::copy_only_curve_domain(src_curves); | |||||||||||||||||||||||
| /* Stores the offset of every result point for every original point. | /* Stores the offset of every result point for every original point. | |||||||||||||||||||||||
| * The extra length is used in order to store an extra zero for every curve. */ | * The extra length is used in order to store an extra zero for every curve. */ | |||||||||||||||||||||||
| Array<int> dst_point_offsets(src_curves.points_num() + src_curves.curves_num()); | Array<int> dst_point_offsets(src_curves.points_num() + src_curves.curves_num()); | |||||||||||||||||||||||
| calculate_result_offsets(src_curves, | calculate_result_offsets(src_curves, | |||||||||||||||||||||||
| curve_selection, | point_selection, | |||||||||||||||||||||||
| unselected_ranges, | ||||||||||||||||||||||||
| radius_input, | ||||||||||||||||||||||||
| counts, | counts, | |||||||||||||||||||||||
| cyclic, | cyclic, | |||||||||||||||||||||||
| dst_curves.offsets_for_write(), | dst_curves.offsets_for_write(), | |||||||||||||||||||||||
| dst_point_offsets); | dst_point_offsets); | |||||||||||||||||||||||
| const Span<int> point_offsets = dst_point_offsets.as_span(); | const Span<int> point_offsets = dst_point_offsets.as_span(); | |||||||||||||||||||||||
| dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); | dst_curves.resize(dst_curves.offsets().last(), dst_curves.curves_num()); | |||||||||||||||||||||||
| bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); | bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write(); | |||||||||||||||||||||||
| Show All 14 Lines | if (src_curves.has_curve_with_type(CURVE_TYPE_BEZIER)) { | |||||||||||||||||||||||
| src_handles_r = src_curves.handle_positions_right(); | src_handles_r = src_curves.handle_positions_right(); | |||||||||||||||||||||||
| dst_types_l = dst_curves.handle_types_left_for_write(); | dst_types_l = dst_curves.handle_types_left_for_write(); | |||||||||||||||||||||||
| dst_types_r = dst_curves.handle_types_right_for_write(); | dst_types_r = dst_curves.handle_types_right_for_write(); | |||||||||||||||||||||||
| dst_handles_l = dst_curves.handle_positions_left_for_write(); | dst_handles_l = dst_curves.handle_positions_left_for_write(); | |||||||||||||||||||||||
| dst_handles_r = dst_curves.handle_positions_right_for_write(); | dst_handles_r = dst_curves.handle_positions_right_for_write(); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| threading::parallel_for(curve_selection.index_range(), 512, [&](IndexRange range) { | threading::parallel_for(src_curves.curves_range(), 512, [&](IndexRange range) { | |||||||||||||||||||||||
| Array<float3> directions; | Array<float3> directions; | |||||||||||||||||||||||
| Array<float> angles; | Array<float> angles; | |||||||||||||||||||||||
| Array<float> radii; | Array<float> radii; | |||||||||||||||||||||||
| Array<float> input_radii_buffer; | Array<float> input_radii_buffer; | |||||||||||||||||||||||
| for (const int curve_i : curve_selection.slice(range)) { | for (const int curve_i : range) { | |||||||||||||||||||||||
| const IndexRange src_points = src_curves.points_for_curve(curve_i); | const IndexRange src_points = src_curves.points_for_curve(curve_i); | |||||||||||||||||||||||
| const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i)); | const Span<int> offsets = point_offsets.slice(curve_dst_offsets(src_points, curve_i)); | |||||||||||||||||||||||
| const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | const IndexRange dst_points = dst_curves.points_for_curve(curve_i); | |||||||||||||||||||||||
| const Span<float3> src_positions = positions.slice(src_points); | const Span<float3> src_positions = positions.slice(src_points); | |||||||||||||||||||||||
| directions.reinitialize(src_points.size()); | directions.reinitialize(src_points.size()); | |||||||||||||||||||||||
| calculate_directions(src_positions, directions); | calculate_directions(src_positions, directions); | |||||||||||||||||||||||
| ▲ Show 20 Lines • Show All 50 Lines • ▼ Show 20 Lines | static bke::CurvesGeometry fillet_curves(const bke::CurvesGeometry &src_curves, | |||||||||||||||||||||||
| }); | }); | |||||||||||||||||||||||
| for (auto &attribute : bke::retrieve_attributes_for_transfer( | for (auto &attribute : bke::retrieve_attributes_for_transfer( | |||||||||||||||||||||||
| src_attributes, | src_attributes, | |||||||||||||||||||||||
| dst_attributes, | dst_attributes, | |||||||||||||||||||||||
| ATTR_DOMAIN_MASK_POINT, | ATTR_DOMAIN_MASK_POINT, | |||||||||||||||||||||||
| {"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) { | {"position", "handle_type_left", "handle_type_right", "handle_right", "handle_left"})) { | |||||||||||||||||||||||
| duplicate_fillet_point_data( | duplicate_fillet_point_data( | |||||||||||||||||||||||
| src_curves, dst_curves, curve_selection, point_offsets, attribute.src, attribute.dst.span); | src_curves, dst_curves, src_curves.curves_range(), point_offsets, attribute.src, attribute.dst.span); | |||||||||||||||||||||||
| attribute.dst.finish(); | attribute.dst.finish(); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| if (!unselected_ranges.is_empty()) { | ||||||||||||||||||||||||
| for (auto &attribute : bke::retrieve_attributes_for_transfer( | ||||||||||||||||||||||||
| src_attributes, dst_attributes, ATTR_DOMAIN_MASK_POINT)) { | ||||||||||||||||||||||||
| bke::curves::copy_point_data( | ||||||||||||||||||||||||
| src_curves, dst_curves, unselected_ranges, attribute.src, attribute.dst.span); | ||||||||||||||||||||||||
| attribute.dst.finish(); | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| } | ||||||||||||||||||||||||
| return dst_curves; | return dst_curves; | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, | bke::CurvesGeometry fillet_curves_poly(const bke::CurvesGeometry &src_curves, | |||||||||||||||||||||||
| const IndexMask curve_selection, | const IndexMask point_selection, | |||||||||||||||||||||||
| const VArray<float> &radius, | const VArray<float> &radius, | |||||||||||||||||||||||
| const VArray<int> &count, | const VArray<int> &count, | |||||||||||||||||||||||
| const bool limit_radius) | const bool limit_radius) | |||||||||||||||||||||||
| { | { | |||||||||||||||||||||||
| return fillet_curves(src_curves, curve_selection, radius, count, limit_radius, false); | return fillet_curves(src_curves, point_selection, radius, count, limit_radius, false); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, | bke::CurvesGeometry fillet_curves_bezier(const bke::CurvesGeometry &src_curves, | |||||||||||||||||||||||
| const IndexMask curve_selection, | const IndexMask point_selection, | |||||||||||||||||||||||
| const VArray<float> &radius, | const VArray<float> &radius, | |||||||||||||||||||||||
| const bool limit_radius) | const bool limit_radius) | |||||||||||||||||||||||
| { | { | |||||||||||||||||||||||
| return fillet_curves(src_curves, | return fillet_curves(src_curves, | |||||||||||||||||||||||
| curve_selection, | point_selection, | |||||||||||||||||||||||
| radius, | radius, | |||||||||||||||||||||||
| VArray<int>::ForSingle(1, src_curves.points_num()), | VArray<int>::ForSingle(1, src_curves.points_num()), | |||||||||||||||||||||||
| limit_radius, | limit_radius, | |||||||||||||||||||||||
| true); | true); | |||||||||||||||||||||||
| } | } | |||||||||||||||||||||||
| } // namespace blender::geometry | } // namespace blender::geometry | |||||||||||||||||||||||