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,
93 double total_length = 0.0;
95 total_length +=
math::distance(points[point_i - 1], points[point_i]);
98 if (total_length < 1
e-8) {
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,
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);
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);
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;
212 const float merge_distance,
218 const int src_point_size = src_curves.
points_num();
219 if (src_point_size == 0) {
229 std::atomic<int> total_duplicate_count = 0;
232 for (const int curve_i : range) {
233 const IndexRange points = points_by_curve[curve_i];
234 merge_indices_per_curve[curve_i].reinitialize(points.size());
236 Array<float> distances_along_curve(points.size() + int(cyclic[curve_i]));
237 distances_along_curve.first() = 0.0f;
238 const Span<float> lengths = src_curves.evaluated_lengths_for_curve(curve_i, cyclic[curve_i]);
239 distances_along_curve.as_mutable_span().drop_front(1).copy_from(lengths);
241 MutableSpan<int> merge_indices = merge_indices_per_curve[curve_i].as_mutable_span();
242 array_utils::fill_index_range<int>(merge_indices);
244 const int duplicate_count = curve_merge_by_distance(points,
245 distances_along_curve,
246 selection.slice_content(points),
250 dst_offsets[curve_i] = points.size() - duplicate_count;
251 total_duplicate_count += duplicate_count;
255 const int dst_point_size = src_point_size - total_duplicate_count;
256 dst_curves.resize(dst_point_size, src_curves.curves_num());
259 int merged_points = 0;
260 Array<int> src_to_dst_indices(src_point_size);
261 for (
const int curve_i : src_curves.curves_range()) {
262 const IndexRange points = points_by_curve[curve_i];
263 const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
265 const int point_i = points.
start() +
i;
266 src_to_dst_indices[point_i] = point_i - merged_points;
267 if (merge_indices[
i] !=
i) {
273 Array<int> point_merge_counts(dst_point_size, 0);
274 for (
const int curve_i : src_curves.curves_range()) {
275 const IndexRange points = points_by_curve[curve_i];
276 const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
278 const int merge_index = merge_indices[
i];
279 const int point_src = points.
start() + merge_index;
280 const int dst_index = src_to_dst_indices[point_src];
281 point_merge_counts[dst_index]++;
285 Array<int> map_offsets_data(dst_point_size + 1);
286 map_offsets_data.as_mutable_span().drop_back(1).copy_from(point_merge_counts);
289 point_merge_counts.fill(0);
291 Array<int> merge_map_indices(src_point_size);
292 for (
const int curve_i : src_curves.curves_range()) {
293 const IndexRange points = points_by_curve[curve_i];
294 const Span<int> merge_indices = merge_indices_per_curve[curve_i].as_span();
296 const int point_i = points.
start() +
i;
297 const int merge_index = merge_indices[
i];
298 const int dst_index = src_to_dst_indices[points.
start() + merge_index];
299 merge_map_indices[map_offsets[dst_index].first() + point_merge_counts[dst_index]] = point_i;
300 point_merge_counts[dst_index]++;
304 bke::AttributeAccessor src_attributes = src_curves.attributes();
305 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
306 src_attributes.foreach_attribute([&](
const bke::AttributeIter &iter) {
307 if (attribute_filter.allow_skip(iter.name)) {
314 bke::GAttributeReader src_attribute = iter.get();
316 using T = decltype(dummy);
317 if constexpr (!std::is_void_v<bke::attribute_math::DefaultMixer<T>>) {
318 bke::SpanAttributeWriter<T> dst_attribute =
319 dst_attributes.lookup_or_add_for_write_only_span<T>(iter.name, bke::AttrDomain::Point);
320 BLI_assert(dst_attribute);
321 VArraySpan<T> src = src_attribute.varray.typed<T>();
323 threading::parallel_for(dst_curves.points_range(), 1024, [&](IndexRange range) {
324 for (const int dst_point_i : range) {
327 bke::attribute_math::DefaultMixer<T> mixer{dst_attribute.span.slice(dst_point_i, 1)};
329 Span<int> src_merge_indices = merge_map_indices.as_span().slice(
330 map_offsets[dst_point_i]);
331 for (const int src_point_i : src_merge_indices) {
332 mixer.mix_in(0, src[src_point_i]);
339 dst_attribute.finish();
344 if (dst_curves.nurbs_has_custom_knots()) {
345 bke::curves::nurbs::update_custom_knot_modes(
355 const float merge_distance,
361 const float merge_distance_squared = merge_distance * merge_distance;
371 for (const int src_i : range) {
372 const IndexRange points = src_points_by_curve[src_i];
373 const float3 start_pos = src_positions[points.first()];
374 const float3 end_pos = src_positions[points.last()];
375 const float3 start_world = math::transform_point(layer_to_world, start_pos);
376 const float3 end_world = math::transform_point(layer_to_world, end_pos);
378 ED_view3d_project_float_global(
379 ®ion, start_world, screen_start_points[src_i], V3D_PROJ_TEST_NOP);
380 ED_view3d_project_float_global(
381 ®ion, end_world, screen_end_points[src_i], V3D_PROJ_TEST_NOP);
385 for (
const int src_i : src_curves.curves_range()) {
386 if (cyclic[src_i] ==
true) {
389 BLI_kdtree_2d_insert(
tree, src_i * 2, screen_start_points[src_i]);
390 BLI_kdtree_2d_insert(
tree, src_i * 2 + 1, screen_end_points[src_i]);
392 BLI_kdtree_2d_balance(
tree);
394 Array<int> connect_to_curve(src_curves.curves_num(), -1);
395 Array<bool> flip_direction(src_curves.curves_num(),
false);
396 selection.foreach_index(
GrainSize(512), [&](
const int src_i) {
397 const float2 &start_co = screen_start_points[src_i];
398 const float2 &end_co = screen_end_points[src_i];
400 const int start_index = src_i * 2;
401 const int end_index = src_i * 2 + 1;
403 KDTreeNearest_2d nearest_start, nearest_end;
404 const bool is_start_ok = (BLI_kdtree_2d_find_nearest_cb_cpp(
408 [&](
const int other,
const float * ,
const float dist_sq) {
409 if (start_index == other || dist_sq > merge_distance_squared) {
414 const bool is_end_ok = (BLI_kdtree_2d_find_nearest_cb_cpp(
418 [&](
const int other,
const float * ,
const float dist_sq) {
419 if (end_index == other || dist_sq > merge_distance_squared) {
426 const int curve_index = nearest_start.index / 2;
427 const bool is_end_point = bool(nearest_start.index % 2);
428 if (connect_to_curve[curve_index] < 0) {
429 connect_to_curve[curve_index] = src_i;
430 flip_direction[curve_index] = !is_end_point;
434 const int curve_index = nearest_end.index / 2;
435 const bool is_end_point = bool(nearest_end.index % 2);
436 if (connect_to_curve[src_i] < 0) {
437 connect_to_curve[src_i] = curve_index;
438 flip_direction[curve_index] = is_end_point;
442 BLI_kdtree_2d_free(
tree);
444 return geometry::curves_merge_endpoints(
445 src_curves, connect_to_curve, flip_direction, attribute_filter);
451 const int corner_subdivisions,
452 const int src_point_index,
458 const int num_points = 1 << (corner_subdivisions + 2);
459 const float delta_angle = 2 *
M_PI / float(num_points);
460 const float delta_cos =
math::cos(delta_angle);
461 const float delta_sin =
math::sin(delta_angle);
464 for ([[maybe_unused]]
const int i :
IndexRange(num_points)) {
465 r_perimeter.
append(pt + vec);
466 r_src_indices.
append(src_point_index);
468 const float x = delta_cos * vec.x - delta_sin * vec.y;
469 const float y = delta_sin * vec.x + delta_cos * vec.y;
478 const int corner_subdivisions,
479 const int src_point_index,
483 const float3 vec_from = from - center_pt;
484 const float3 vec_to = to - center_pt;
486 r_perimeter.
append(center_pt);
487 r_src_indices.
append(src_point_index);
491 const float cos_angle =
math::dot(vec_from.
xy(), vec_to.
xy());
492 const float sin_angle = vec_from.x * vec_to.y - vec_from.y * vec_to.x;
499 const int num_full = (1 << (corner_subdivisions + 1)) + 1;
501 if (num_points < 2) {
502 r_perimeter.
append(center_pt + vec_from);
503 r_src_indices.
append(src_point_index);
506 const float delta_angle =
angle / float(num_points - 1);
507 const float delta_cos =
math::cos(delta_angle);
508 const float delta_sin =
math::sin(delta_angle);
511 for ([[maybe_unused]]
const int i :
IndexRange(num_points)) {
512 r_perimeter.
append(center_pt + vec);
513 r_src_indices.
append(src_point_index);
515 const float x = delta_cos * vec.x - delta_sin * vec.y;
516 const float y = delta_sin * vec.x + delta_cos * vec.y;
525 const int corner_subdivisions,
527 const int src_point_index,
535 point + normal * radius,
543 r_perimeter.
append(point - normal * radius);
544 r_src_indices.
append(src_point_index);
545 r_perimeter.
append(point + normal * radius);
546 r_src_indices.
append(src_point_index);
561 const int corner_subdivisions,
562 const int src_point_index,
570 const float3 normal = {tangent.y, -tangent.x, 0.0f};
571 const float3 normal_prev = {tangent_prev.y, -tangent_prev.x, 0.0f};
573 const float sin_angle = tangent_prev.x * tangent.y - tangent_prev.y * tangent.x;
576 const bool is_outside_corner = (sin_angle >= 0.0f);
577 if (is_outside_corner) {
579 pt_b + normal * radius,
588 const float3 miter = {avg_tangent.y, -avg_tangent.x, 0.0f};
589 const float miter_invscale =
math::dot(normal, miter);
592 const float3 miter_point = (radius <
length * miter_invscale &&
593 radius < length_prev * miter_invscale) ?
594 pt_b + miter * radius / miter_invscale :
595 pt_b + miter * radius;
597 r_perimeter.
append(miter_point);
598 r_src_indices.
append(src_point_index);
605 const int corner_subdivisions,
610 const float outline_offset,
616 const int point_num = points.
size();
617 if (point_num == 0) {
620 if (point_num == 1) {
622 const int perimeter_start = r_perimeter.
size();
623 const int point = points.
first();
624 const float radius = std::max(all_radii[point] + outline_offset, 0.0f);
626 positions.
first(), radius, corner_subdivisions, point, r_perimeter, r_point_indices);
627 const int perimeter_count = r_perimeter.
size() - perimeter_start;
628 if (perimeter_count > 0) {
629 r_point_counts.
append(perimeter_count);
634 auto add_corner = [&](
const int a,
const int b,
const int c) {
635 const int point = points[
b];
636 const float3 pt_a = positions[a];
637 const float3 pt_b = positions[
b];
638 const float3 pt_c = positions[c];
639 const float radius = std::max(all_radii[point] + outline_offset, 0.0f);
641 pt_a, pt_b, pt_c, radius, corner_subdivisions, point, r_perimeter, r_point_indices);
643 auto add_cap = [&](
const int center_i,
const int next_i,
const eGPDstroke_Caps cap_type) {
644 const int point = points[center_i];
645 const float3 ¢er = positions[center_i];
647 const float radius = std::max(all_radii[point] + outline_offset, 0.0f);
649 center, dir, radius, corner_subdivisions, cap_type, point, r_perimeter, r_point_indices);
655 const int perimeter_start = r_perimeter.
size();
658 add_cap(0, 1, start_cap_type);
662 add_corner(
i - 1,
i,
i + 1);
665 add_corner(point_num - 2, point_num - 1, 0);
671 add_cap(0, point_num - 1, end_cap_type);
675 add_cap(point_num - 1, point_num - 2, end_cap_type);
680 add_corner(0, point_num - 1, point_num - 2);
683 add_corner(point_num -
i, point_num -
i - 1, point_num -
i - 2);
686 const int perimeter_count = r_perimeter.
size() - perimeter_start;
687 if (perimeter_count > 0) {
688 r_point_counts.
append(perimeter_count);
696 const int left_perimeter_start = r_perimeter.
size();
697 add_corner(point_num - 1, 0, 1);
699 add_corner(
i - 1,
i,
i + 1);
701 add_corner(point_num - 2, point_num - 1, 0);
702 const int left_perimeter_count = r_perimeter.
size() - left_perimeter_start;
703 if (left_perimeter_count > 0) {
704 r_point_counts.
append(left_perimeter_count);
708 const int right_perimeter_start = r_perimeter.
size();
709 add_corner(0, point_num - 1, point_num - 2);
711 add_corner(point_num -
i, point_num -
i - 1, point_num -
i - 2);
713 add_corner(1, 0, point_num - 1);
714 const int right_perimeter_count = r_perimeter.
size() - right_perimeter_start;
715 if (right_perimeter_count > 0) {
716 r_point_counts.
append(right_perimeter_count);
735 const int corner_subdivisions,
736 const float outline_radius,
737 const float outline_offset,
738 const int material_index)
758 for (const int i : range) {
759 transformed_positions[i] = math::transform_point(transform, src_positions[i]);
763 for (const int i : range) {
764 transformed_radii[i] = src_radii[i] * scale;
771 PerimeterData &
data = thread_data.
local();
773 const bool is_cyclic_curve = src_cyclic[curve_i];
777 const bool use_caps =
true ;
779 const int prev_point_num =
data.positions.size();
780 const int prev_curve_num =
data.point_counts.size();
781 const IndexRange points = src_curves.points_by_curve()[curve_i];
783 generate_stroke_perimeter(transformed_positions,
797 for (
float3 &
pos :
data.positions.as_mutable_span().drop_front(prev_point_num)) {
801 data.curve_indices.append_n_times(curve_i,
data.point_counts.size() - prev_curve_num);
804 int dst_curve_num = 0;
805 int dst_point_num = 0;
806 for (
const PerimeterData &
data : thread_data) {
809 dst_curve_num +=
data.point_counts.size();
810 dst_point_num +=
data.positions.size();
813 bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num);
814 if (dst_point_num == 0 || dst_curve_num == 0) {
818 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
819 bke::SpanAttributeWriter<bool> dst_cyclic = dst_attributes.lookup_or_add_for_write_span<
bool>(
820 "cyclic", bke::AttrDomain::Curve);
821 bke::SpanAttributeWriter<int> dst_material = dst_attributes.lookup_or_add_for_write_span<
int>(
822 "material_index", bke::AttrDomain::Curve);
823 bke::SpanAttributeWriter<float> dst_radius = dst_attributes.lookup_or_add_for_write_span<
float>(
824 "radius", bke::AttrDomain::Point);
833 for (
const PerimeterData &
data : thread_data) {
834 curves = curves.
after(
data.point_counts.size());
835 points = points.
after(
data.positions.size());
838 dst_curve_map.as_mutable_span().
slice(curves).copy_from(
data.curve_indices);
840 dst_offsets.
slice(curves).copy_from(
data.point_counts);
841 dst_cyclic.span.
slice(curves).fill(
true);
842 if (material_index >= 0) {
843 dst_material.span.slice(curves).fill(material_index);
847 dst_material.span[curves[
i]] = src_material_index[
data.curve_indices[
i]];
853 dst_point_map.as_mutable_span().slice(points).copy_from(
data.point_indices);
854 dst_radius.span.slice(points).fill(outline_radius);
856 offset_indices::accumulate_counts_to_offsets(dst_curves.offsets_for_write());
858 bke::gather_attributes(src_attributes,
859 bke::AttrDomain::Point,
860 bke::AttrDomain::Point,
861 bke::attribute_filter_from_skip_ref({
"position",
"radius"}),
864 bke::gather_attributes(src_attributes,
865 bke::AttrDomain::Curve,
866 bke::AttrDomain::Curve,
867 bke::attribute_filter_from_skip_ref({
"cyclic",
"material_index"}),
872 dst_material.finish();
874 dst_curves.update_curve_types();
929 segment.curve = curve;
933 this->segments.
append(std::move(segment));
935 return &this->segments.
last();
944 while (!this->segments.
is_empty()) {
948 for (
Segment &
b : merged_segments) {
979 merged_segments.append(std::move(a));
983 this->segments = merged_segments;
998 const float a1 = co_b[1] - co_a[1];
999 const float b1 = co_a[0] - co_b[0];
1000 const float c1 = a1 * co_a[0] + b1 * co_a[1];
1002 const float a2 = co_d[1] - co_c[1];
1003 const float b2 = co_c[0] - co_d[0];
1004 const float c2 = a2 * co_c[0] + b2 * co_c[1];
1006 const float det = (a1 * b2 - a2 * b1);
1011 float2 isect((b2 * c1 - b1 * c2) / det, (a1 * c2 - a2 * c1) / det);
1015 float distance = (length_ab == 0.0f ?
1036 if (points_by_curve[src_curve].
size() < 2) {
1041 const IndexRange src_curve_points = points_by_curve[src_curve].drop_back(
1043 for (
const int point_a : src_curve_points) {
1044 const int point_b = (point_a == points_by_curve[src_curve].last()) ? src_curve_points.
first() :
1048 const float2 co_a = screen_space_positions[point_a];
1049 const float2 co_b = screen_space_positions[point_b];
1056 float intersection_distance_min =
FLT_MAX;
1057 float intersection_distance_max = -
FLT_MAX;
1062 if (points_by_curve[curve].
size() < 2) {
1067 if (!
BLI_rcti_isect(&bbox_ab, &screen_space_curve_bounds[curve],
nullptr)) {
1073 for (
const int point_c : points) {
1074 const int point_d = (point_c == points_by_curve[curve].last()) ? points.
first() :
1078 if (curve == src_curve &&
1079 (point_a == point_c || point_a == point_d || point_b == point_c || point_b == point_d))
1085 const float2 co_c = screen_space_positions[point_c];
1086 const float2 co_d = screen_space_positions[point_d];
1099 const float2 padded_c = co_c - padding_cd;
1100 const float2 padded_d = co_d + padding_cd;
1104 if (
ELEM(isect.kind, isect.LINE_LINE_CROSS, isect.LINE_LINE_EXACT)) {
1106 r_is_intersected_after_point[point_a] =
true;
1111 co_a, co_b, co_c, co_d);
1112 intersection_distance_min =
math::min(normalized_distance, intersection_distance_min);
1113 intersection_distance_max =
math::max(normalized_distance, intersection_distance_max);
1118 if (r_is_intersected_after_point[point_a]) {
1119 r_intersection_distance[point_a][
Distance::Min] = intersection_distance_min;
1120 r_intersection_distance[point_a][
Distance::Max] = intersection_distance_max;
1130 const int direction,
1137 const int point_first = points_by_curve[segment.curve].first();
1138 const int point_last = points_by_curve[segment.curve].last();
1141 int point_a = segment.point_range[segment_side];
1143 bool intersected =
false;
1144 segment.is_intersected[segment_side] =
false;
1147 while ((direction == 1 && point_a < point_last) || (direction == -1 && point_a > point_first)) {
1148 const int point_b = point_a + direction;
1149 const bool at_end_of_curve = (direction == -1 && point_b == point_first) ||
1150 (direction == 1 && point_b == point_last);
1153 segment.point_range[segment_side] = point_a;
1154 point_is_in_segment[point_a] =
true;
1159 const int intersection_point = direction == 1 ? point_a : point_b;
1160 intersected = is_intersected_after_point[intersection_point];
1163 if (at_end_of_curve &&
1164 ((direction == -1 &&
1166 (direction == 1 && intersection_distance[intersection_point][
Distance::Min] >
1169 intersected =
false;
1178 segment.is_intersected[segment_side] =
true;
1179 segment.intersection_distance[segment_side] =
1180 (direction == 1) ? intersection_distance[intersection_point][
Distance::Min] :
1186 point_a += direction;
1191 if (direction == -1) {
1193 point_is_in_segment[point_first] =
true;
1196 segment.point_range[
Side::End] = point_last;
1197 point_is_in_segment[point_last] =
true;
1211 const int8_t directions[2] = {-1, 1};
1212 for (
const int8_t direction : directions) {
1216 is_intersected_after_point,
1217 intersection_distance,
1218 point_is_in_segment);
1227 const bool keep_caps)
1233 Array<bool> is_intersected_after_point(src_points_num,
false);
1238 screen_space_positions,
1239 screen_space_curve_bounds,
1240 is_intersected_after_point,
1241 intersection_distance);
1247 Array<bool> point_is_in_segment(src_points_num,
false);
1250 Segments &thread_segments = trim_segments_by_thread.
local();
1251 for (
const int selected_point : selected_points_in_curves[
pos]) {
1253 if (point_is_in_segment[selected_point]) {
1263 *segment, src, is_intersected_after_point, intersection_distance, point_is_in_segment);
1271 const int cyclic_outer_point = !segment->is_intersected[
Side::Start] ?
1272 src_points_by_curve[curve_i].last() :
1273 src_points_by_curve[curve_i].first();
1274 segment = thread_segments.
create_segment(curve_i, cyclic_outer_point);
1278 *segment, src, is_intersected_after_point, intersection_distance, point_is_in_segment);
1283 for (
Segments &thread_segments : trim_segments_by_thread) {
1284 trim_segments.
segments.extend(thread_segments.segments);
1289 if (trim_segments.
segments.is_empty()) {
1301 const IndexRange src_points = src_points_by_curve[src_curve];
1302 for (
const int src_point : src_points) {
1304 const int src_next_point = (src_point == src_points.
last()) ? src_points.
first() :
1308 if (!point_is_in_segment[src_point]) {
1309 dst_points.
append({src_point, src_next_point, 0.0f,
true,
false});
1339 dst_points.
append({src_point,
1350 dst_points.
append({src_point,
1361 if (dst_point.is_src_point) {
1362 dst_point.is_cut =
true;
1381 const int frame_number)
1387 int max_bvh_lines = 0;
1388 for (
const int i_drawing : drawings.
index_range()) {
1389 if (drawings[i_drawing].frame_number == frame_number) {
1390 max_bvh_lines += drawings[i_drawing].drawing.strokes().evaluated_points_num();
1395 data.start_positions.reinitialize(max_bvh_lines);
1396 data.end_positions.reinitialize(max_bvh_lines);
1398 data.drawing_offsets.reinitialize(drawings.
size() + 1);
1399 for (
const int i_drawing : drawings.
index_range()) {
1401 data.drawing_offsets[i_drawing] = (drawings[i_drawing].frame_number == frame_number ?
1406 data.drawing_offsets);
1409 for (
const int i_drawing : drawings.
index_range()) {
1411 if (drawings[i_drawing].frame_number != frame_number) {
1425 const IndexRange bvh_index_range = bvh_elements_by_drawing[i_drawing];
1433 const IndexRange evaluated_points = evaluated_points_by_curve[i_curve];
1436 for (
const int i_point : evaluated_points) {
1438 vc.
region, evaluated_positions[i_point], projection);
1439 start_positions[i_point] = co;
1442 const int i_prev_point = (i_point > 0 ? i_point - 1 : evaluated_points.
last());
1443 end_positions[i_prev_point] = co;
1446 for (
const int i_point : evaluated_points.
drop_back(1)) {
1447 const float2 &start = start_positions[i_point];
1448 const float2 &end = end_positions[i_point];
1450 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
1455 const float2 &start = start_positions.
last();
1458 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
1473 data.tree =
nullptr;
1475 data.drawing_offsets.reinitialize(0);
1476 data.start_positions.reinitialize(0);
1477 data.end_positions.reinitialize(0);
1493 struct RaycastArgs {
1505 const RaycastArgs &args = *
static_cast<const RaycastArgs *
>(userdata);
1506 if (
ELEM(index, args.ignore_index1, args.ignore_index2, args.ignore_index3)) {
1511 const float2 ray_end = ray_start +
float2(ray->direction) * ray->radius;
1512 const float2 &line_start = args.tree_data.start_positions[index];
1513 const float2 &line_end = args.tree_data.end_positions[index];
1519 if (dist >= hit->dist) {
1526 hit->no[0] =
result.lambda;
1530 auto do_raycast = [&](
const int index_back,
1532 const int index_forward,
1533 float &r_lambda) ->
bool {
1534 if (index_forward < 0) {
1538 const float2 start = screen_space_positions[index];
1539 const float2 end = screen_space_positions[index_forward];
1543 RaycastArgs args = {tree_data,
1544 index_back >= 0 ? int(tree_data_range[index_back]) : -1,
1545 int(tree_data_range[index]),
1546 index_forward >= 0 ? int(tree_data_range[index_forward]) : -1};
1553 if (hit.
index >= 0) {
1554 r_lambda = hit.
no[0];
1561 if (r_first_intersect_factors) {
1562 r_first_intersect_factors->fill(-1.0f);
1564 if (r_last_intersect_factors) {
1565 r_last_intersect_factors->fill(-1.0f);
1570 const IndexRange points = points_by_curve[i_curve];
1572 for (
const int i_point : points) {
1573 const int i_prev_point = (i_point == points.
first() ? (
is_cyclic ? points.
last() : -1) :
1575 const int i_next_point = (i_point == points.
last() ? (
is_cyclic ? points.
first() : -1) :
1579 if (do_raycast(i_prev_point, i_point, i_next_point, lambda)) {
1580 r_hits[i_point] =
true;
1581 if (r_first_intersect_factors) {
1582 (*r_first_intersect_factors)[i_point] = lambda;
1586 if (do_raycast(i_next_point, i_point, i_prev_point, lambda)) {
1588 if (r_last_intersect_factors) {
1589 (*r_last_intersect_factors)[i_point] = 1.0f - lambda;
1610 screen_space_positions,
1623 result.segment_offsets.reinitialize(
curves.curves_num() + 1);
1625 result.segment_offsets.fill(0);
1627 const IndexRange points = points_by_curve[curve_i];
1638 const int num_segments = segments_by_curve.
total_size();
1639 result.segment_start_points.reinitialize(num_segments);
1640 result.segment_start_fractions.reinitialize(num_segments);
1643 const IndexRange points = points_by_curve[curve_i];
1646 const IndexRange segments = segments_by_curve[curve_i];
1647 const int hit_segments_start = (
is_cyclic ? 0 : 1);
1655 result.segment_start_points[segments[0]] = points.
first();
1656 result.segment_start_fractions[segments[0]] = 0.0f;
1659 curve_hit_mask.
foreach_index([&](
const int point_i,
const int hit_i) {
1660 result.segment_start_points[segments[hit_segments_start + hit_i]] = point_i;
1661 result.segment_start_fractions[segments[hit_segments_start + hit_i]] =
1662 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(*)(void *userdata, int index, const BVHTreeRay *ray, BVHTreeRayHit *hit) BVHTree_RayCastCallback
void BLI_bvhtree_balance(BVHTree *tree)
void BLI_bvhtree_free(BVHTree *tree)
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)
static double angle(const Eigen::Vector3d &v1, const Eigen::Vector3d &v2)
BMesh const char void * data
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
SIMD_FORCE_INLINE btVector3 transform(const btVector3 &point) const
static DBVT_INLINE btScalar size(const btDbvtVolume &a)
static IndexMask from_bools(Span< bool > bools, IndexMaskMemory &memory)
constexpr IndexRange after(int64_t n) const
constexpr int64_t start() const
constexpr IndexRange index_range() const
constexpr MutableSpan slice(const int64_t start, const int64_t size) const
IndexRange index_range() const
static IndexMask from_indices(Span< T > indices, IndexMaskMemory &memory)
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 int64_t start() const
constexpr IndexRange slice(int64_t start, int64_t size) const
constexpr IndexRange index_range() 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 void copy_from(Span< T > values) const
constexpr T & last(const int64_t n=0) const
constexpr Span slice(int64_t start, int64_t size) const
constexpr const T * data() const
constexpr const T & first() const
constexpr int64_t size() const
constexpr IndexRange index_range() const
constexpr bool is_empty() 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
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
float4x4 to_world_space(const Object &object) const
void to_indices(MutableSpan< T > r_indices) const
IndexMask slice_content(IndexRange range) const
void foreach_index_optimized(Fn &&fn) const
void foreach_index(Fn &&fn) const
static bool is_cyclic(const Nurb *nu)
VecBase< float, 2 > float2
VecBase< float, 3 > float3
float length(VecOp< float, D >) RET
float distance(VecOp< float, D >, VecOp< float, D >) RET
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_circle_from_point(const float3 &pt, const float radius, const int corner_subdivisions, const int src_point_index, Vector< float3 > &r_perimeter, Vector< int > &r_src_indices)
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))
MatBase< float, 4, 4 > float4x4
VecBase< float, 2 > float2
VecBase< float, 3 > float3
static constexpr int type_length
VecBase< T, 2 > xy() const
bke::greasepencil::Drawing & drawing
Vector< int > point_counts
Vector< int > point_indices
Vector< float3 > positions
Vector< int > curve_indices
float intersection_distance[2]
Segment * create_segment(const int curve, const int point)
Vector< Segment > segments
void merge_adjacent_segments()