36#include "curve_fit_nd.h"
48 points_to_delete.
slice(range).
fill(
false);
49 int64_t total_points_to_remove = 0;
56 if (sub_range.
size() < 3) {
61 float max_dist = -1.0f;
63 for (
const int64_t index : inside_range) {
64 const float dist = dist_function(sub_range.
first(), sub_range.
last(), index);
65 if (dist > max_dist) {
67 max_index = index - sub_range.
first();
71 if (max_dist > epsilon) {
74 stack.
push(sub_range.
slice(0, max_index + 1));
75 stack.
push(sub_range.
slice(max_index, sub_range.
size() - max_index));
79 total_points_to_remove += inside_range.
size();
80 points_to_delete.
slice(inside_range).
fill(
true);
83 return total_points_to_remove;
87 const float error_threshold,
90 if (points.is_empty()) {
93 double total_length = 0.0;
94 for (
const int point_i : points.index_range().drop_front(1)) {
95 total_length +=
math::distance(points[point_i - 1], points[point_i]);
98 if (total_length < 1
e-8) {
103 corner_mask.
to_indices(indices.as_mutable_span());
104 uint *indicies_ptr = corner_mask.
is_empty() ? nullptr :
reinterpret_cast<uint *
>(indices.data());
106 float *r_cubic_array;
107 uint r_cubic_array_len;
108 int error = curve_fit_cubic_to_points_fl(*points.data(),
112 CURVE_FIT_CALC_HIGH_QUALIY,
126 if (r_cubic_array ==
nullptr) {
131 r_cubic_array_len * 3);
135 return curve_positions;
139 const float radius_min,
140 const float radius_max,
141 const int samples_max,
142 const float angle_threshold,
145 if (points.is_empty()) {
148 if (points.size() == 1) {
153 const int error = curve_fit_corners_detect_fl(*points.data(),
167 if (r_corners ==
nullptr) {
171 BLI_assert(samples_max < std::numeric_limits<int>::max());
182 const float merge_distance,
186 KDTree_1d *
tree = BLI_kdtree_1d_new(selection.size());
189 BLI_kdtree_1d_insert(
tree,
pos, &distances[i - points.first()]);
191 BLI_kdtree_1d_balance(
tree);
193 Array<int> selection_merge_indices(selection.size(), -1);
194 const int duplicate_count = BLI_kdtree_1d_calc_duplicates_fast(
195 tree, merge_distance,
false, selection_merge_indices.
data());
196 BLI_kdtree_1d_free(
tree);
200 selection.foreach_index([&](
const int src_index,
const int pos) {
201 const int merge_index = selection_merge_indices[
pos];
202 if (merge_index != -1) {
203 const int src_merge_index = selection[merge_index] - points.
first();
204 r_merge_indices[src_index - points.first()] = src_merge_index;
208 return duplicate_count;
213 const float merge_distance,
217 const int src_point_size = src_curves.
points_num();
218 if (src_point_size == 0) {
228 std::atomic<int> total_duplicate_count = 0;
231 for (const int curve_i : range) {
232 const IndexRange points = points_by_curve[curve_i];
233 merge_indices_per_curve[curve_i].reinitialize(points.size());
235 Array<float> distances_along_curve(points.size());
236 distances_along_curve.first() = 0.0f;
237 const Span<float> lengths = src_curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]);
238 distances_along_curve.as_mutable_span().drop_front(1).copy_from(lengths);
240 MutableSpan<int> merge_indices = merge_indices_per_curve[curve_i].as_mutable_span();
241 array_utils::fill_index_range<int>(merge_indices);
243 const int duplicate_count = curve_merge_by_distance(points,
244 distances_along_curve,
245 selection.slice_content(points),
249 dst_offsets[curve_i] = points.size() - duplicate_count;
250 total_duplicate_count += duplicate_count;
254 const int dst_point_size = src_point_size - total_duplicate_count;
255 dst_curves.resize(dst_point_size, src_curves.curves_num());
258 int merged_points = 0;
259 Array<int> src_to_dst_indices(src_point_size);
260 for (
const int curve_i : src_curves.curves_range()) {
261 const IndexRange points = points_by_curve[curve_i];
262 const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
263 for (
const int i : points.index_range()) {
264 const int point_i = points.start() + i;
265 src_to_dst_indices[point_i] = point_i - merged_points;
266 if (merge_indices[i] != i) {
272 Array<int> point_merge_counts(dst_point_size, 0);
273 for (
const int curve_i : src_curves.curves_range()) {
274 const IndexRange points = points_by_curve[curve_i];
275 const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
276 for (
const int i : points.index_range()) {
277 const int merge_index = merge_indices[i];
278 const int point_src = points.start() + merge_index;
279 const int dst_index = src_to_dst_indices[point_src];
280 point_merge_counts[dst_index]++;
284 Array<int> map_offsets_data(dst_point_size + 1);
285 map_offsets_data.as_mutable_span().drop_back(1).copy_from(point_merge_counts);
288 point_merge_counts.fill(0);
290 Array<int> merge_map_indices(src_point_size);
291 for (
const int curve_i : src_curves.curves_range()) {
292 const IndexRange points = points_by_curve[curve_i];
293 const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
294 for (
const int i : points.index_range()) {
295 const int point_i = points.start() + i;
296 const int merge_index = merge_indices[i];
297 const int dst_index = src_to_dst_indices[points.start() + merge_index];
298 merge_map_indices[map_offsets[dst_index].first() + point_merge_counts[dst_index]] = point_i;
299 point_merge_counts[dst_index]++;
303 bke::AttributeAccessor src_attributes = src_curves.attributes();
304 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
305 src_attributes.foreach_attribute([&](
const bke::AttributeIter &iter) {
306 if (attribute_filter.allow_skip(iter.name)) {
313 bke::GAttributeReader src_attribute = iter.get();
315 using T = decltype(dummy);
316 if constexpr (!std::is_void_v<bke::attribute_math::DefaultMixer<T>>) {
317 bke::SpanAttributeWriter<T> dst_attribute =
318 dst_attributes.lookup_or_add_for_write_only_span<T>(iter.name, bke::AttrDomain::Point);
319 VArraySpan<T> src = src_attribute.varray.typed<T>();
321 threading::parallel_for(dst_curves.points_range(), 1024, [&](IndexRange range) {
322 for (const int dst_point_i : range) {
325 bke::attribute_math::DefaultMixer<T> mixer{dst_attribute.span.slice(dst_point_i, 1)};
327 Span<int> src_merge_indices = merge_map_indices.as_span().slice(
328 map_offsets[dst_point_i]);
329 for (const int src_point_i : src_merge_indices) {
330 mixer.mix_in(0, src[src_point_i]);
337 dst_attribute.finish();
349 const float merge_distance,
355 const float merge_distance_squared = merge_distance * merge_distance;
363 for (const int src_i : range) {
364 const IndexRange points = src_points_by_curve[src_i];
365 const float3 start_pos = src_positions[points.first()];
366 const float3 end_pos = src_positions[points.last()];
367 const float3 start_world = math::transform_point(layer_to_world, start_pos);
368 const float3 end_world = math::transform_point(layer_to_world, end_pos);
370 ED_view3d_project_float_global(
371 ®ion, start_world, screen_start_points[src_i], V3D_PROJ_TEST_NOP);
372 ED_view3d_project_float_global(
373 ®ion, end_world, screen_end_points[src_i], V3D_PROJ_TEST_NOP);
377 for (
const int src_i : src_curves.curves_range()) {
378 BLI_kdtree_2d_insert(
tree, src_i * 2, screen_start_points[src_i]);
379 BLI_kdtree_2d_insert(
tree, src_i * 2 + 1, screen_end_points[src_i]);
381 BLI_kdtree_2d_balance(
tree);
383 Array<int> connect_to_curve(src_curves.curves_num(), -1);
384 Array<bool> flip_direction(src_curves.curves_num(),
false);
385 selection.foreach_index(
GrainSize(512), [&](
const int src_i) {
386 const float2 &start_co = screen_start_points[src_i];
387 const float2 &end_co = screen_end_points[src_i];
389 const int start_index = src_i * 2;
390 const int end_index = src_i * 2 + 1;
392 KDTreeNearest_2d nearest_start, nearest_end;
393 const bool is_start_ok = (BLI_kdtree_2d_find_nearest_cb_cpp(
397 [&](
const int other,
const float * ,
const float dist_sq) {
398 if (start_index == other || dist_sq > merge_distance_squared) {
403 const bool is_end_ok = (BLI_kdtree_2d_find_nearest_cb_cpp(
407 [&](
const int other,
const float * ,
const float dist_sq) {
408 if (end_index == other || dist_sq > merge_distance_squared) {
415 const int curve_index = nearest_start.index / 2;
416 const bool is_end_point = bool(nearest_start.index % 2);
417 if (connect_to_curve[curve_index] < 0) {
418 connect_to_curve[curve_index] = src_i;
419 flip_direction[curve_index] = !is_end_point;
423 const int curve_index = nearest_end.index / 2;
424 const bool is_end_point = bool(nearest_end.index % 2);
425 if (connect_to_curve[src_i] < 0) {
426 connect_to_curve[src_i] = curve_index;
427 flip_direction[curve_index] = is_end_point;
431 BLI_kdtree_2d_free(
tree);
433 return geometry::curves_merge_endpoints(
434 src_curves, connect_to_curve, flip_direction, attribute_filter);
441 const int corner_subdivisions,
442 const int src_point_index,
446 const float3 vec_from = from - center_pt;
447 const float3 vec_to = to - center_pt;
449 r_perimeter.
append(center_pt);
450 r_src_indices.
append(src_point_index);
454 const float cos_angle =
math::dot(vec_from.
xy(), vec_to.
xy());
455 const float sin_angle = vec_from.x * vec_to.y - vec_from.y * vec_to.x;
462 const int num_full = (1 << (corner_subdivisions + 1)) + 1;
464 if (num_points < 2) {
465 r_perimeter.
append(center_pt + vec_from);
466 r_src_indices.
append(src_point_index);
469 const float delta_angle = angle /
float(num_points - 1);
470 const float delta_cos =
math::cos(delta_angle);
471 const float delta_sin =
math::sin(delta_angle);
474 for ([[maybe_unused]]
const int i :
IndexRange(num_points)) {
475 r_perimeter.
append(center_pt + vec);
476 r_src_indices.
append(src_point_index);
478 const float x = delta_cos * vec.x - delta_sin * vec.y;
479 const float y = delta_sin * vec.x + delta_cos * vec.y;
488 const int corner_subdivisions,
490 const int src_point_index,
498 point + normal * radius,
506 r_perimeter.
append(point - normal * radius);
507 r_src_indices.
append(src_point_index);
508 r_perimeter.
append(point + normal * radius);
509 r_src_indices.
append(src_point_index);
524 const int corner_subdivisions,
525 const int src_point_index,
533 const float3 normal = {tangent.y, -tangent.x, 0.0f};
534 const float3 normal_prev = {tangent_prev.y, -tangent_prev.x, 0.0f};
536 const float sin_angle = tangent_prev.x * tangent.y - tangent_prev.y * tangent.x;
539 const bool is_outside_corner = (sin_angle >= 0.0f);
540 if (is_outside_corner) {
542 pt_b + normal * radius,
551 const float3 miter = {avg_tangent.y, -avg_tangent.x, 0.0f};
552 const float miter_invscale =
math::dot(normal, miter);
555 const float3 miter_point = (radius < length * miter_invscale &&
556 radius < length_prev * miter_invscale) ?
557 pt_b + miter * radius / miter_invscale :
558 pt_b + miter * radius;
560 r_perimeter.
append(miter_point);
561 r_src_indices.
append(src_point_index);
568 const int corner_subdivisions,
573 const float outline_offset,
579 const int point_num = points.
size();
584 auto add_corner = [&](
const int a,
const int b,
const int c) {
585 const int point = points[
b];
586 const float3 pt_a = positions[a];
587 const float3 pt_b = positions[
b];
588 const float3 pt_c = positions[c];
589 const float radius = std::max(all_radii[point] + outline_offset, 0.0f);
591 pt_a, pt_b, pt_c, radius, corner_subdivisions, point, r_perimeter, r_point_indices);
593 auto add_cap = [&](
const int center_i,
const int next_i,
const eGPDstroke_Caps cap_type) {
594 const int point = points[center_i];
595 const float3 ¢er = positions[center_i];
597 const float radius = std::max(all_radii[point] + outline_offset, 0.0f);
599 center, dir, radius, corner_subdivisions, cap_type, point, r_perimeter, r_point_indices);
605 const int perimeter_start = r_perimeter.
size();
608 add_cap(0, 1, start_cap_type);
611 for (
const int i : points.index_range().drop_front(1).drop_back(1)) {
612 add_corner(i - 1, i, i + 1);
615 add_corner(point_num - 2, point_num - 1, 0);
621 add_cap(0, point_num - 1, end_cap_type);
625 add_cap(point_num - 1, point_num - 2, end_cap_type);
630 add_corner(0, point_num - 1, point_num - 2);
632 for (
const int i : points.index_range().drop_front(1).drop_back(1)) {
633 add_corner(point_num - i, point_num - i - 1, point_num - i - 2);
636 const int perimeter_count = r_perimeter.
size() - perimeter_start;
637 if (perimeter_count > 0) {
638 r_point_counts.
append(perimeter_count);
646 const int left_perimeter_start = r_perimeter.
size();
647 add_corner(point_num - 1, 0, 1);
648 for (
const int i : points.index_range().drop_front(1).drop_back(1)) {
649 add_corner(i - 1, i, i + 1);
651 add_corner(point_num - 2, point_num - 1, 0);
652 const int left_perimeter_count = r_perimeter.
size() - left_perimeter_start;
653 if (left_perimeter_count > 0) {
654 r_point_counts.
append(left_perimeter_count);
658 const int right_perimeter_start = r_perimeter.
size();
659 add_corner(0, point_num - 1, point_num - 2);
660 for (
const int i : points.index_range().drop_front(1).drop_back(1)) {
661 add_corner(point_num - i, point_num - i - 1, point_num - i - 2);
663 add_corner(1, 0, point_num - 1);
664 const int right_perimeter_count = r_perimeter.
size() - right_perimeter_start;
665 if (right_perimeter_count > 0) {
666 r_point_counts.
append(right_perimeter_count);
685 const int corner_subdivisions,
686 const float outline_radius,
687 const float outline_offset,
688 const int material_index)
695 "cyclic", bke::AttrDomain::Curve,
false);
701 "material_index", bke::AttrDomain::Curve, 0);
708 for (const int i : range) {
709 transformed_positions[i] = math::transform_point(transform, src_positions[i]);
712 threading::parallel_for(transformed_radii.index_range(), 4096, [&](
const IndexRange range) {
713 for (const int i : range) {
714 transformed_radii[i] = src_radii[i] * scale;
721 PerimeterData &data = thread_data.
local();
723 const bool is_cyclic_curve = src_cyclic[curve_i];
727 const bool use_caps =
true ;
729 const int prev_point_num = data.positions.size();
730 const int prev_curve_num = data.point_counts.size();
731 const IndexRange points = src_curves.points_by_curve()[curve_i];
733 generate_stroke_perimeter(transformed_positions,
747 for (
float3 &
pos : data.positions.as_mutable_span().drop_front(prev_point_num)) {
751 data.curve_indices.append_n_times(curve_i, data.point_counts.size() - prev_curve_num);
754 int dst_curve_num = 0;
755 int dst_point_num = 0;
756 for (
const PerimeterData &data : thread_data) {
757 BLI_assert(data.point_counts.size() == data.curve_indices.size());
758 BLI_assert(data.positions.size() == data.point_indices.size());
759 dst_curve_num += data.point_counts.size();
760 dst_point_num += data.positions.size();
763 bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num);
764 if (dst_point_num == 0 || dst_curve_num == 0) {
768 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
769 bke::SpanAttributeWriter<bool> dst_cyclic = dst_attributes.lookup_or_add_for_write_span<
bool>(
770 "cyclic", bke::AttrDomain::Curve);
771 bke::SpanAttributeWriter<int> dst_material = dst_attributes.lookup_or_add_for_write_span<
int>(
772 "material_index", bke::AttrDomain::Curve);
773 bke::SpanAttributeWriter<float> dst_radius = dst_attributes.lookup_or_add_for_write_span<
float>(
774 "radius", bke::AttrDomain::Point);
775 const MutableSpan<int> dst_offsets = dst_curves.offsets_for_write();
776 const MutableSpan<float3> dst_positions = dst_curves.positions_for_write();
778 Array<int> dst_curve_map(dst_curve_num);
779 Array<int> dst_point_map(dst_point_num);
783 for (
const PerimeterData &data : thread_data) {
784 curves = curves.after(data.point_counts.size());
785 points = points.after(data.positions.size());
788 dst_curve_map.as_mutable_span().slice(curves).copy_from(data.curve_indices);
790 dst_offsets.slice(curves).copy_from(data.point_counts);
791 dst_cyclic.span.slice(curves).fill(
true);
792 if (material_index >= 0) {
793 dst_material.span.slice(curves).fill(material_index);
796 for (
const int i : curves.index_range()) {
797 dst_material.span[curves[i]] = src_material_index[data.curve_indices[i]];
802 dst_positions.slice(points).copy_from(data.positions);
803 dst_point_map.as_mutable_span().slice(points).copy_from(data.point_indices);
804 dst_radius.span.slice(points).fill(outline_radius);
806 offset_indices::accumulate_counts_to_offsets(dst_curves.offsets_for_write());
808 bke::gather_attributes(src_attributes,
809 bke::AttrDomain::Point,
810 bke::AttrDomain::Point,
811 bke::attribute_filter_from_skip_ref({
"position",
"radius"}),
814 bke::gather_attributes(src_attributes,
815 bke::AttrDomain::Curve,
816 bke::AttrDomain::Curve,
817 bke::attribute_filter_from_skip_ref({
"cyclic",
"material_index"}),
822 dst_material.finish();
824 dst_curves.update_curve_types();
859 float intersection_distance[2];
863 bool is_intersected[2];
880 segment.point_range[Side::Start] =
point;
881 segment.point_range[Side::End] =
point;
883 this->segments.
append(std::move(segment));
885 return &this->segments.
last();
894 while (!this->segments.
is_empty()) {
898 for (
Segment &
b : merged_segments) {
899 if (a.curve !=
b.curve) {
903 if ((a.point_range[Side::Start] <=
b.point_range[Side::End] &&
904 a.point_range[Side::End] >=
b.point_range[Side::Start]) ||
905 (a.point_range[Side::End] ==
b.point_range[Side::Start] - 1) ||
906 (
b.point_range[Side::End] == a.point_range[Side::Start] - 1))
909 const bool take_start_a = a.point_range[Side::Start] <
b.point_range[Side::Start];
910 const bool take_end_a = a.point_range[Side::End] >
b.point_range[Side::End];
911 b.point_range[Side::Start] = take_start_a ? a.point_range[Side::Start] :
912 b.point_range[Side::Start];
913 b.point_range[Side::End] = take_end_a ? a.point_range[Side::End] :
914 b.point_range[Side::End];
915 b.is_intersected[Side::Start] = take_start_a ? a.is_intersected[Side::Start] :
916 b.is_intersected[Side::Start];
917 b.is_intersected[Side::End] = take_end_a ? a.is_intersected[Side::End] :
918 b.is_intersected[Side::End];
919 b.intersection_distance[Side::Start] = take_start_a ?
920 a.intersection_distance[Side::Start] :
921 b.intersection_distance[Side::Start];
922 b.intersection_distance[Side::End] = take_end_a ? a.intersection_distance[Side::End] :
923 b.intersection_distance[Side::End];
929 merged_segments.
append(std::move(a));
933 this->segments = merged_segments;
948 const float a1 = co_b[1] - co_a[1];
949 const float b1 = co_a[0] - co_b[0];
950 const float c1 = a1 * co_a[0] + b1 * co_a[1];
952 const float a2 = co_d[1] - co_c[1];
953 const float b2 = co_c[0] - co_d[0];
954 const float c2 = a2 * co_c[0] + b2 * co_c[1];
956 const float det =
float(a1 * b2 - a2 * b1);
961 float2 isect((b2 * c1 - b1 * c2) / det, (a1 * c2 - a2 * c1) / det);
965 float distance = (length_ab == 0.0f ?
986 if (points_by_curve[src_curve].
size() < 2) {
991 const IndexRange src_curve_points = points_by_curve[src_curve].drop_back(
993 for (
const int point_a : src_curve_points) {
994 const int point_b = (point_a == points_by_curve[src_curve].last()) ? src_curve_points.
first() :
998 const float2 co_a = screen_space_positions[point_a];
999 const float2 co_b = screen_space_positions[point_b];
1006 float intersection_distance_min =
FLT_MAX;
1007 float intersection_distance_max = -
FLT_MAX;
1012 if (points_by_curve[curve].
size() < 2) {
1017 if (!
BLI_rcti_isect(&bbox_ab, &screen_space_curve_bounds[curve],
nullptr)) {
1023 for (
const int point_c : points) {
1024 const int point_d = (point_c == points_by_curve[
curve].last()) ? points.first() :
1028 if (curve == src_curve &&
1029 (point_a == point_c || point_a == point_d || point_b == point_c || point_b == point_d))
1035 const float2 co_c = screen_space_positions[point_c];
1036 const float2 co_d = screen_space_positions[point_d];
1049 const float2 padded_c = co_c - padding_cd;
1050 const float2 padded_d = co_d + padding_cd;
1054 if (
ELEM(isect.kind, isect.LINE_LINE_CROSS, isect.LINE_LINE_EXACT)) {
1056 r_is_intersected_after_point[point_a] =
true;
1061 co_a, co_b, co_c, co_d);
1062 intersection_distance_min =
math::min(normalized_distance, intersection_distance_min);
1063 intersection_distance_max =
math::max(normalized_distance, intersection_distance_max);
1068 if (r_is_intersected_after_point[point_a]) {
1069 r_intersection_distance[point_a][Distance::Min] = intersection_distance_min;
1070 r_intersection_distance[point_a][Distance::Max] = intersection_distance_max;
1080 const int direction,
1087 const int point_first = points_by_curve[segment.curve].first();
1088 const int point_last = points_by_curve[segment.curve].last();
1090 const Side segment_side = (direction == 1) ? Side::End : Side::Start;
1091 int point_a = segment.point_range[segment_side];
1093 bool intersected =
false;
1094 segment.is_intersected[segment_side] =
false;
1097 while ((direction == 1 && point_a < point_last) || (direction == -1 && point_a > point_first)) {
1098 const int point_b = point_a + direction;
1099 const bool at_end_of_curve = (direction == -1 && point_b == point_first) ||
1100 (direction == 1 && point_b == point_last);
1103 segment.point_range[segment_side] = point_a;
1104 point_is_in_segment[point_a] =
true;
1109 const int intersection_point = direction == 1 ? point_a : point_b;
1110 intersected = is_intersected_after_point[intersection_point];
1113 if (at_end_of_curve &&
1114 ((direction == -1 &&
1116 (direction == 1 && intersection_distance[intersection_point][Distance::Min] >
1119 intersected =
false;
1128 segment.is_intersected[segment_side] =
true;
1129 segment.intersection_distance[segment_side] =
1130 (direction == 1) ? intersection_distance[intersection_point][Distance::Min] :
1131 intersection_distance[intersection_point][Distance::Max];
1136 point_a += direction;
1141 if (direction == -1) {
1142 segment.point_range[Side::Start] = point_first;
1143 point_is_in_segment[point_first] =
true;
1146 segment.point_range[Side::End] = point_last;
1147 point_is_in_segment[point_last] =
true;
1161 const int8_t directions[2] = {-1, 1};
1162 for (
const int8_t direction : directions) {
1166 is_intersected_after_point,
1167 intersection_distance,
1168 point_is_in_segment);
1177 const bool keep_caps)
1183 Array<bool> is_intersected_after_point(src_points_num,
false);
1188 screen_space_positions,
1189 screen_space_curve_bounds,
1190 is_intersected_after_point,
1191 intersection_distance);
1197 Array<bool> point_is_in_segment(src_points_num,
false);
1200 Segments &thread_segments = trim_segments_by_thread.
local();
1201 for (
const int selected_point : selected_points_in_curves[
pos]) {
1203 if (point_is_in_segment[selected_point]) {
1213 *segment, src, is_intersected_after_point, intersection_distance, point_is_in_segment);
1218 (!segment->is_intersected[Side::Start] || !segment->is_intersected[Side::End]) &&
1219 !(!segment->is_intersected[Side::Start] && !segment->is_intersected[Side::End]))
1221 const int cyclic_outer_point = !segment->is_intersected[Side::Start] ?
1222 src_points_by_curve[curve_i].last() :
1223 src_points_by_curve[curve_i].first();
1224 segment = thread_segments.
create_segment(curve_i, cyclic_outer_point);
1228 *segment, src, is_intersected_after_point, intersection_distance, point_is_in_segment);
1233 for (
Segments &thread_segments : trim_segments_by_thread) {
1234 trim_segments.
segments.extend(thread_segments.segments);
1239 if (trim_segments.
segments.is_empty()) {
1251 const IndexRange src_points = src_points_by_curve[src_curve];
1252 for (
const int src_point : src_points) {
1254 const int src_next_point = (src_point == src_points.
last()) ? src_points.
first() :
1258 if (!point_is_in_segment[src_point]) {
1259 dst_points.
append({src_point, src_next_point, 0.0f,
true,
false});
1284 if (trim_segment.is_intersected[Side::Start] &&
1287 const int src_point = trim_segment.point_range[Side::Start] - 1;
1289 dst_points.
append({src_point,
1291 trim_segment.intersection_distance[Side::Start],
1296 if (trim_segment.is_intersected[Side::End]) {
1297 const int src_point = trim_segment.point_range[Side::End];
1300 dst_points.
append({src_point,
1302 trim_segment.intersection_distance[Side::End],
1311 if (dst_point.is_src_point) {
1312 dst_point.is_cut =
true;
1331 const int frame_number)
1337 int max_bvh_lines = 0;
1338 for (
const int i_drawing : drawings.
index_range()) {
1339 if (drawings[i_drawing].frame_number == frame_number) {
1340 max_bvh_lines += drawings[i_drawing].drawing.strokes().evaluated_points_num();
1345 data.start_positions.reinitialize(max_bvh_lines);
1346 data.end_positions.reinitialize(max_bvh_lines);
1348 data.drawing_offsets.reinitialize(drawings.
size() + 1);
1349 for (
const int i_drawing : drawings.
index_range()) {
1351 data.drawing_offsets[i_drawing] = (drawings[i_drawing].frame_number == frame_number ?
1355 OffsetIndices bvh_elements_by_drawing = offset_indices::accumulate_counts_to_offsets(
1356 data.drawing_offsets);
1359 for (
const int i_drawing : drawings.
index_range()) {
1361 if (drawings[i_drawing].frame_number != frame_number) {
1366 const float4x4 layer_to_world = layer.to_world_space(
object);
1369 const OffsetIndices evaluated_points_by_curve = curves.evaluated_points_by_curve();
1371 const Span<float3> evaluated_positions = curves.evaluated_positions();
1372 const IndexMask curves_mask = curves.curves_range();
1375 const IndexRange bvh_index_range = bvh_elements_by_drawing[i_drawing];
1383 const IndexRange evaluated_points = evaluated_points_by_curve[i_curve];
1386 for (
const int i_point : evaluated_points) {
1388 vc.
region, evaluated_positions[i_point], projection);
1389 start_positions[i_point] = co;
1392 const int i_prev_point = (i_point > 0 ? i_point - 1 : evaluated_points.
last());
1393 end_positions[i_prev_point] = co;
1396 for (
const int i_point : evaluated_points.
drop_back(1)) {
1397 const float2 &start = start_positions[i_point];
1398 const float2 &end = end_positions[i_point];
1400 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
1405 const float2 &start = start_positions.
last();
1408 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
1423 data.tree =
nullptr;
1425 data.drawing_offsets.reinitialize(0);
1426 data.start_positions.reinitialize(0);
1427 data.end_positions.reinitialize(0);
1440 const OffsetIndices points_by_curve = curves.points_by_curve();
1443 struct RaycastArgs {
1455 const RaycastArgs &args = *
static_cast<const RaycastArgs *
>(userdata);
1456 if (
ELEM(index, args.ignore_index1, args.ignore_index2, args.ignore_index3)) {
1461 const float2 ray_end = ray_start +
float2(ray->direction) * ray->radius;
1462 const float2 &line_start = args.tree_data.start_positions[index];
1463 const float2 &line_end = args.tree_data.end_positions[index];
1465 if (result.kind <= 0) {
1468 const float dist = result.lambda *
math::distance(ray_start, ray_end);
1469 if (dist >= hit->dist) {
1476 hit->no[0] = result.lambda;
1480 auto do_raycast = [&](
const int index_back,
1482 const int index_forward,
1483 float &r_lambda) ->
bool {
1484 if (index_forward < 0) {
1488 const float2 start = screen_space_positions[index];
1489 const float2 end = screen_space_positions[index_forward];
1493 RaycastArgs args = {tree_data,
1494 index_back >= 0 ?
int(tree_data_range[index_back]) : -1,
1495 int(tree_data_range[index]),
1496 index_forward >= 0 ?
int(tree_data_range[index_forward]) : -1};
1503 if (hit.index >= 0) {
1504 r_lambda = hit.no[0];
1511 if (r_first_intersect_factors) {
1512 r_first_intersect_factors->fill(-1.0f);
1514 if (r_last_intersect_factors) {
1515 r_last_intersect_factors->fill(-1.0f);
1520 const IndexRange points = points_by_curve[i_curve];
1522 for (
const int i_point : points) {
1523 const int i_prev_point = (i_point == points.first() ? (
is_cyclic ? points.last() : -1) :
1525 const int i_next_point = (i_point == points.last() ? (
is_cyclic ? points.first() : -1) :
1529 if (do_raycast(i_prev_point, i_point, i_next_point, lambda)) {
1530 r_hits[i_point] =
true;
1531 if (r_first_intersect_factors) {
1532 (*r_first_intersect_factors)[i_point] = lambda;
1536 if (do_raycast(i_next_point, i_point, i_prev_point, lambda)) {
1538 if (r_last_intersect_factors) {
1539 (*r_last_intersect_factors)[i_point] = 1.0f - lambda;
1552 const OffsetIndices points_by_curve = curves.points_by_curve();
1560 screen_space_positions,
1568 const IndexMask hit_mask = IndexMask::from_bools(hits, memory);
1575 result.segment_offsets.fill(0);
1577 const IndexRange points = points_by_curve[curve_i];
1583 result.segment_offsets[curve_i] = (
is_cyclic ? 0 : 1) + curve_hit_mask.
size();
1585 const OffsetIndices segments_by_curve = offset_indices::accumulate_counts_to_offsets(
1586 result.segment_offsets);
1588 const int num_segments = segments_by_curve.
total_size();
1589 result.segment_start_points.reinitialize(num_segments);
1590 result.segment_start_fractions.reinitialize(num_segments);
1593 const IndexRange points = points_by_curve[curve_i];
1596 const IndexRange segments = segments_by_curve[curve_i];
1597 const int hit_segments_start = (
is_cyclic ? 0 : 1);
1605 result.segment_start_points[segments[0]] = points.first();
1606 result.segment_start_fractions[segments[0]] = 0.0f;
1609 curve_hit_mask.
foreach_index([&](
const int point_i,
const int hit_i) {
1610 result.segment_start_points[segments[hit_segments_start + hit_i]] = point_i;
1611 result.segment_start_fractions[segments[hit_segments_start + hit_i]] =
1612 first_hit_factors[point_i];
Low-level operations for curves.
Low-level operations for curves.
Low-level operations for grease pencil.
#define BLI_assert_unreachable()
BVHTree * BLI_bvhtree_new(int maxsize, float epsilon, char tree_type, char axis)
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
void(* BVHTree_RayCastCallback)(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit)
void BLI_bvhtree_insert(BVHTree *tree, int index, const float co[3], int numpoints)
int BLI_bvhtree_ray_cast(const BVHTree *tree, const float co[3], const float dir[3], float radius, BVHTreeRayHit *hit, BVHTree_RayCastCallback callback, void *userdata)
A KD-tree for nearest neighbor search.
void BLI_kdtree_nd_ free(KDTree *tree)
void BLI_rcti_init_minmax(struct rcti *rect)
void BLI_rcti_pad(struct rcti *rect, int pad_x, int pad_y)
bool BLI_rcti_isect(const struct rcti *src1, const struct rcti *src2, struct rcti *dest)
void BLI_rcti_do_minmax_v(struct rcti *rect, const int xy[2])
blender::float2 ED_view3d_project_float_v2_m4(const ARegion *region, const float co[3], const blender::float4x4 &mat)
blender::float4x4 ED_view3d_ob_project_mat_get_from_obmat(const RegionView3D *rv3d, const blender::float4x4 &obmat)
in reality light always falls off quadratically Particle Retrieve the data of the particle that spawned the object for example to give variation to multiple instances of an object Point Retrieve information about points in a point cloud Retrieve the edges of an object as it appears to Cycles topology will always appear triangulated Convert a blackbody temperature to an RGB value Normal Generate a perturbed normal from an RGB normal map image Typically used for faking highly detailed surfaces Generate an OSL shader from a file or text data block Image Sample an image file as a texture Gabor Generate Gabor noise Gradient Generate interpolated color and intensity values based on the input vector Magic Generate a psychedelic color texture Voronoi Generate Worley noise based on the distance to random points Typically used to generate textures such as or biological cells Brick Generate a procedural texture producing bricks Texture Retrieve multiple types of texture coordinates nTypically used as inputs for texture nodes Vector Convert a point
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
SIMD_FORCE_INLINE btScalar length() const
Return the length of the vector.
IndexRange index_range() const
void reinitialize(const int64_t new_size)
constexpr int64_t first() const
constexpr IndexRange drop_back(int64_t n) const
constexpr int64_t last(const int64_t n=0) const
constexpr int64_t size() const
constexpr bool is_empty() const
constexpr IndexRange slice(int64_t start, int64_t size) const
constexpr IndexRange drop_front(int64_t n) const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
constexpr void fill(const T &value) const
constexpr T & first() const
constexpr T & last(const int64_t n=0) const
constexpr Span slice(int64_t start, int64_t size) const
constexpr int64_t size() const
constexpr IndexRange index_range() const
void push(const T &value)
void append(const T &value)
const T & last(const int64_t n=0) const
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, eCustomDataType data_type, const void *default_value=nullptr) const
OffsetIndices< int > points_by_curve() const
IndexRange curves_range() const
int evaluated_points_num() const
Span< float3 > positions() const
AttributeAccessor attributes() const
MutableSpan< int > offsets_for_write()
void ensure_evaluated_lengths() const
VArray< bool > cyclic() const
const bke::CurvesGeometry & strokes() const
VArray< float > radii() const
void to_indices(MutableSpan< T > r_indices) const
IndexMask slice_content(IndexRange range) const
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
void foreach_index(Fn &&fn) const
local_group_size(16, 16) .push_constant(Type b
DEGForeachIDComponentCallback callback
static bool is_cyclic(const Nurb *nu)
draw_view in_light_buf[] float
draw_view push_constant(Type::INT, "radiance_src") .push_constant(Type capture_info_buf storage_buf(1, Qualifier::READ, "ObjectBounds", "bounds_buf[]") .push_constant(Type draw_view int
static void error(const char *str)
void fill_index_range(MutableSpan< T > span, const T start=0)
void convert_to_static_type(const CPPType &cpp_type, const Func &func)
bke::CurvesGeometry copy_only_curve_domain(const bke::CurvesGeometry &src_curves)
static constexpr float DISTANCE_FACTOR_THRESHOLD
bke::CurvesGeometry trim_curve_segments(const bke::CurvesGeometry &src, const Span< float2 > screen_space_positions, const Span< rcti > screen_space_curve_bounds, const IndexMask &curve_selection, const Vector< Vector< int > > &selected_points_in_curves, const bool keep_caps)
static void expand_trim_segment(Segment &segment, const bke::CurvesGeometry &src, const Span< bool > is_intersected_after_point, const Span< float2 > intersection_distance, MutableSpan< bool > point_is_in_segment)
static float get_intersection_distance_of_segments(const float2 &co_a, const float2 &co_b, const float2 &co_c, const float2 &co_d)
static void expand_trim_segment_direction(Segment &segment, const int direction, const bke::CurvesGeometry &src, const Span< bool > is_intersected_after_point, const Span< float2 > intersection_distance, MutableSpan< bool > point_is_in_segment)
static void get_intersections_of_curve_with_curves(const int src_curve, const bke::CurvesGeometry &src, const Span< float2 > screen_space_positions, const Span< rcti > screen_space_curve_bounds, MutableSpan< bool > r_is_intersected_after_point, MutableSpan< float2 > r_intersection_distance)
static constexpr int BBOX_PADDING
void find_curve_intersections(const bke::CurvesGeometry &curves, const IndexMask &curve_mask, const Span< float2 > screen_space_positions, const Curves2DBVHTree &tree_data, const IndexRange tree_data_range, MutableSpan< bool > r_hits, std::optional< MutableSpan< float > > r_first_intersect_factors, std::optional< MutableSpan< float > > r_last_intersect_factors)
blender::bke::CurvesGeometry curves_merge_by_distance(const bke::CurvesGeometry &src_curves, const float merge_distance, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
int64_t ramer_douglas_peucker_simplify(const IndexRange range, const float epsilon, const FunctionRef< float(int64_t, int64_t, int64_t)> dist_function, MutableSpan< bool > points_to_delete)
static void generate_corner(const float3 &pt_a, const float3 &pt_b, const float3 &pt_c, const float radius, const int corner_subdivisions, const int src_point_index, Vector< float3 > &r_perimeter, Vector< int > &r_src_indices)
static void generate_arc_from_point_to_point(const float3 &from, const float3 &to, const float3 ¢er_pt, const int corner_subdivisions, const int src_point_index, Vector< float3 > &r_perimeter, Vector< int > &r_src_indices)
void free_curves_2d_bvh_data(Curves2DBVHTree &data)
CurveSegmentsData find_curve_segments(const bke::CurvesGeometry &curves, const IndexMask &curve_mask, const Span< float2 > screen_space_positions, const Curves2DBVHTree &tree_data, const IndexRange tree_data_range)
IndexMask polyline_detect_corners(Span< float2 > points, const float radius_min, const float radius_max, const int samples_max, const float angle_threshold, IndexMaskMemory &memory)
static void generate_stroke_perimeter(const Span< float3 > all_positions, const Span< float > all_radii, const IndexRange points, const int corner_subdivisions, const bool is_cyclic, const bool use_caps, const eGPDstroke_Caps start_cap_type, const eGPDstroke_Caps end_cap_type, const float outline_offset, Vector< float3 > &r_perimeter, Vector< int > &r_point_counts, Vector< int > &r_point_indices)
Array< PointTransferData > compute_topology_change(const bke::CurvesGeometry &src, bke::CurvesGeometry &dst, const Span< Vector< PointTransferData > > src_to_dst_points, const bool keep_caps)
static void generate_cap(const float3 &point, const float3 &tangent, const float radius, const int corner_subdivisions, const eGPDstroke_Caps cap_type, const int src_point_index, Vector< float3 > &r_perimeter, Vector< int > &r_src_indices)
bke::CurvesGeometry curves_merge_endpoints_by_distance(const ARegion ®ion, const bke::CurvesGeometry &src_curves, const float4x4 &layer_to_world, const float merge_distance, const IndexMask &selection, const bke::AttributeFilter &attribute_filter)
Array< float2 > polyline_fit_curve(Span< float2 > points, const float error_threshold, const IndexMask &corner_mask)
Curves2DBVHTree build_curves_2d_bvh_from_visible(const ViewContext &vc, const Object &object, const GreasePencil &grease_pencil, Span< MutableDrawingInfo > drawings, const int frame_number)
int curve_merge_by_distance(const IndexRange points, const Span< float > distances, const IndexMask &selection, const float merge_distance, MutableSpan< int > r_merge_indices)
bke::CurvesGeometry create_curves_outline(const bke::greasepencil::Drawing &drawing, const IndexMask &strokes, const float4x4 &transform, const int corner_subdivisions, const float outline_radius, const float outline_offset, const int material_index)
T cos(const AngleRadianBase< T > &a)
T clamp(const T &a, const T &min, const T &max)
isect_result< VecBase< T, Size > > isect_seg_seg(const VecBase< T, Size > &v1, const VecBase< T, Size > &v2, const VecBase< T, Size > &v3, const VecBase< T, Size > &v4)
T distance(const T &a, const T &b)
T length(const VecBase< T, Size > &a)
QuaternionBase< T > normalize_and_get_length(const QuaternionBase< T > &q, T &out_length)
T dot(const QuaternionBase< T > &a, const QuaternionBase< T > &b)
T average(const VecBase< T, Size > &a)
T min(const T &a, const T &b)
CartesianBasis invert(const CartesianBasis &basis)
T atan2(const T &y, const T &x)
MatBase< T, NumCol, NumRow > normalize(const MatBase< T, NumCol, NumRow > &a)
VecBase< T, 3 > to_scale(const MatBase< T, NumCol, NumRow > &mat)
T sin(const AngleRadianBase< T > &a)
T max(const T &a, const T &b)
VecBase< T, 3 > transform_point(const CartesianBasis &basis, const VecBase< T, 3 > &v)
OffsetIndices< int > accumulate_counts_to_offsets(MutableSpan< int > counts_to_offsets, int start_offset=0)
void parallel_for(const IndexRange range, const int64_t grain_size, const Function &function, const TaskSizeHints &size_hints=detail::TaskSizeHints_Static(1))
float distance(float a, float b)
static constexpr int type_length
VecBase< T, 2 > xy() const
Array< int > segment_offsets
bke::greasepencil::Drawing & drawing
Vector< int > point_counts
Vector< int > point_indices
Vector< float3 > positions
Vector< int > curve_indices
Segment * create_segment(const int curve, const int point)
Vector< Segment > segments
void merge_adjacent_segments()