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) {
107 uint cubic_array_len;
108 int error = curve_fit_cubic_to_points_fl(*points.
data(),
112 CURVE_FIT_CALC_HIGH_QUALIY,
126 if (cubic_array ==
nullptr) {
130 Span<float2> cubic_array_span(
reinterpret_cast<float2 *
>(cubic_array), cubic_array_len * 3);
134 return curve_positions;
138 const float radius_min,
139 const float radius_max,
140 const int samples_max,
141 const float angle_threshold,
147 if (points.
size() == 1) {
152 const int error = curve_fit_corners_detect_fl(*points.
data(),
166 if (corners ==
nullptr) {
170 BLI_assert(samples_max < std::numeric_limits<int>::max());
181 const float merge_distance,
185 KDTree_1d *
tree = BLI_kdtree_1d_new(selection.
size());
188 BLI_kdtree_1d_insert(
tree,
pos, &distances[
i - points.
first()]);
190 BLI_kdtree_1d_balance(
tree);
193 const int duplicate_count = BLI_kdtree_1d_calc_duplicates_fast(
194 tree, merge_distance,
false, selection_merge_indices.
data());
195 BLI_kdtree_1d_free(
tree);
200 const int merge_index = selection_merge_indices[
pos];
201 if (merge_index != -1) {
202 const int src_merge_index = selection[merge_index] - points.
first();
203 r_merge_indices[src_index - points.
first()] = src_merge_index;
207 return duplicate_count;
211 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() + int(cyclic[curve_i]));
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();
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();
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();
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 BLI_assert(dst_attribute);
320 VArraySpan<T> src = src_attribute.varray.typed<T>();
322 threading::parallel_for(dst_curves.points_range(), 1024, [&](IndexRange range) {
323 for (const int dst_point_i : range) {
326 bke::attribute_math::DefaultMixer<T> mixer{dst_attribute.span.slice(dst_point_i, 1)};
328 Span<int> src_merge_indices = merge_map_indices.as_span().slice(
329 map_offsets[dst_point_i]);
330 for (const int src_point_i : src_merge_indices) {
331 mixer.mix_in(0, src[src_point_i]);
338 dst_attribute.finish();
343 if (dst_curves.nurbs_has_custom_knots()) {
344 bke::curves::nurbs::update_custom_knot_modes(
354 const float merge_distance,
360 const float merge_distance_squared = merge_distance * merge_distance;
370 for (const int src_i : range) {
371 const IndexRange points = src_points_by_curve[src_i];
372 const float3 start_pos = src_positions[points.first()];
373 const float3 end_pos = src_positions[points.last()];
374 const float3 start_world = math::transform_point(layer_to_world, start_pos);
375 const float3 end_world = math::transform_point(layer_to_world, end_pos);
377 ED_view3d_project_float_global(
378 ®ion, start_world, screen_start_points[src_i], V3D_PROJ_TEST_NOP);
379 ED_view3d_project_float_global(
380 ®ion, end_world, screen_end_points[src_i], V3D_PROJ_TEST_NOP);
384 for (
const int src_i : src_curves.curves_range()) {
385 if (cyclic[src_i] ==
true) {
388 BLI_kdtree_2d_insert(
tree, src_i * 2, screen_start_points[src_i]);
389 BLI_kdtree_2d_insert(
tree, src_i * 2 + 1, screen_end_points[src_i]);
391 BLI_kdtree_2d_balance(
tree);
393 Array<int> connect_to_curve(src_curves.curves_num(), -1);
394 Array<bool> flip_direction(src_curves.curves_num(),
false);
395 selection.foreach_index(
GrainSize(512), [&](
const int src_i) {
396 const float2 &start_co = screen_start_points[src_i];
397 const float2 &end_co = screen_end_points[src_i];
399 const int start_index = src_i * 2;
400 const int end_index = src_i * 2 + 1;
402 KDTreeNearest_2d nearest_start, nearest_end;
403 const bool is_start_ok = (BLI_kdtree_2d_find_nearest_cb_cpp(
407 [&](
const int other,
const float * ,
const float dist_sq) {
408 if (start_index == other || dist_sq > merge_distance_squared) {
413 const bool is_end_ok = (BLI_kdtree_2d_find_nearest_cb_cpp(
417 [&](
const int other,
const float * ,
const float dist_sq) {
418 if (end_index == other || dist_sq > merge_distance_squared) {
425 const int curve_index = nearest_start.index / 2;
426 const bool is_end_point = bool(nearest_start.index % 2);
427 if (connect_to_curve[curve_index] < 0) {
428 connect_to_curve[curve_index] = src_i;
429 flip_direction[curve_index] = !is_end_point;
433 const int curve_index = nearest_end.index / 2;
434 const bool is_end_point = bool(nearest_end.index % 2);
435 if (connect_to_curve[src_i] < 0) {
436 connect_to_curve[src_i] = curve_index;
437 flip_direction[curve_index] = is_end_point;
441 BLI_kdtree_2d_free(
tree);
443 return geometry::curves_merge_endpoints(
444 src_curves, connect_to_curve, flip_direction, attribute_filter);
450 const int corner_subdivisions,
451 const int src_point_index,
457 const int num_points = 1 << (corner_subdivisions + 2);
458 const float delta_angle = 2 *
M_PI /
float(num_points);
459 const float delta_cos =
math::cos(delta_angle);
460 const float delta_sin =
math::sin(delta_angle);
463 for ([[maybe_unused]]
const int i :
IndexRange(num_points)) {
464 r_perimeter.
append(pt + vec);
465 r_src_indices.
append(src_point_index);
467 const float x = delta_cos * vec.x - delta_sin * vec.y;
468 const float y = delta_sin * vec.x + delta_cos * vec.y;
477 const int corner_subdivisions,
478 const int src_point_index,
482 const float3 vec_from = from - center_pt;
483 const float3 vec_to = to - center_pt;
485 r_perimeter.
append(center_pt);
486 r_src_indices.
append(src_point_index);
490 const float cos_angle =
math::dot(vec_from.
xy(), vec_to.
xy());
491 const float sin_angle = vec_from.x * vec_to.y - vec_from.y * vec_to.x;
498 const int num_full = (1 << (corner_subdivisions + 1)) + 1;
500 if (num_points < 2) {
501 r_perimeter.
append(center_pt + vec_from);
502 r_src_indices.
append(src_point_index);
505 const float delta_angle =
angle /
float(num_points - 1);
506 const float delta_cos =
math::cos(delta_angle);
507 const float delta_sin =
math::sin(delta_angle);
510 for ([[maybe_unused]]
const int i :
IndexRange(num_points)) {
511 r_perimeter.
append(center_pt + vec);
512 r_src_indices.
append(src_point_index);
514 const float x = delta_cos * vec.x - delta_sin * vec.y;
515 const float y = delta_sin * vec.x + delta_cos * vec.y;
524 const int corner_subdivisions,
526 const int src_point_index,
534 point + normal * radius,
542 r_perimeter.
append(point - normal * radius);
543 r_src_indices.
append(src_point_index);
544 r_perimeter.
append(point + normal * radius);
545 r_src_indices.
append(src_point_index);
560 const float miter_limit_angle,
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);
579 pt_b + normal * radius,
589 const float3 miter = {avg_tangent.y, -avg_tangent.x, 0.0f};
590 const float miter_invscale =
math::dot(normal, miter);
592 if (is_outside_corner) {
593 const bool is_bevel = -
math::dot(tangent, tangent_prev) >
math::cos(miter_limit_angle);
595 r_perimeter.
append(pt_b + normal_prev * radius);
596 r_perimeter.
append(pt_b + normal * radius);
601 const float3 miter_point = pt_b + miter * radius / miter_invscale;
603 r_perimeter.
append(miter_point);
604 r_src_indices.
append(src_point_index);
610 const float3 miter_point = (radius <
length * miter_invscale &&
611 radius < length_prev * miter_invscale) ?
612 pt_b + miter * radius / miter_invscale :
613 pt_b + miter * radius;
615 r_perimeter.
append(miter_point);
616 r_src_indices.
append(src_point_index);
622 const int corner_subdivisions,
628 const float outline_offset,
634 const int point_num = points.
size();
635 if (point_num == 0) {
638 if (point_num == 1) {
640 const int perimeter_start = r_perimeter.
size();
641 const int point = points.
first();
642 const float radius = std::max(all_radii[point] + outline_offset, 0.0f);
644 positions.
first(), radius, corner_subdivisions, point, r_perimeter, r_point_indices);
645 const int perimeter_count = r_perimeter.
size() - perimeter_start;
646 if (perimeter_count > 0) {
647 r_point_counts.
append(perimeter_count);
652 auto add_corner = [&](
const int a,
const int b,
const int c) {
653 const int point = points[
b];
654 const float3 pt_a = positions[a];
655 const float3 pt_b = positions[
b];
656 const float3 pt_c = positions[c];
657 const float radius = std::max(all_radii[point] + outline_offset, 0.0f);
658 const float miter_angle = miter_angles[point];
669 auto add_cap = [&](
const int center_i,
const int next_i,
const eGPDstroke_Caps cap_type) {
670 const int point = points[center_i];
671 const float3 ¢er = positions[center_i];
673 const float radius = std::max(all_radii[point] + outline_offset, 0.0f);
675 center, dir, radius, corner_subdivisions, cap_type, point, r_perimeter, r_point_indices);
681 const int perimeter_start = r_perimeter.
size();
684 add_cap(0, 1, start_cap_type);
688 add_corner(
i - 1,
i,
i + 1);
691 add_corner(point_num - 2, point_num - 1, 0);
697 add_cap(0, point_num - 1, end_cap_type);
701 add_cap(point_num - 1, point_num - 2, end_cap_type);
706 add_corner(0, point_num - 1, point_num - 2);
709 add_corner(point_num -
i, point_num -
i - 1, point_num -
i - 2);
712 const int perimeter_count = r_perimeter.
size() - perimeter_start;
713 if (perimeter_count > 0) {
714 r_point_counts.
append(perimeter_count);
722 const int left_perimeter_start = r_perimeter.
size();
723 add_corner(point_num - 1, 0, 1);
725 add_corner(
i - 1,
i,
i + 1);
727 add_corner(point_num - 2, point_num - 1, 0);
728 const int left_perimeter_count = r_perimeter.
size() - left_perimeter_start;
729 if (left_perimeter_count > 0) {
730 r_point_counts.
append(left_perimeter_count);
734 const int right_perimeter_start = r_perimeter.
size();
735 add_corner(0, point_num - 1, point_num - 2);
737 add_corner(point_num -
i, point_num -
i - 1, point_num -
i - 2);
739 add_corner(1, 0, point_num - 1);
740 const int right_perimeter_count = r_perimeter.
size() - right_perimeter_start;
741 if (right_perimeter_count > 0) {
742 r_point_counts.
append(right_perimeter_count);
761 const int corner_subdivisions,
762 const float outline_radius,
763 const float outline_offset,
764 const int material_index)
788 for (const int i : range) {
789 transformed_radii[i] = src_radii[i] * scale;
796 PerimeterData &
data = thread_data.
local();
798 const bool is_cyclic_curve = src_cyclic[curve_i];
802 const bool use_caps =
true ;
804 const int prev_point_num =
data.positions.size();
805 const int prev_curve_num =
data.point_counts.size();
806 const IndexRange points = src_curves.points_by_curve()[curve_i];
808 generate_stroke_perimeter(transformed_positions,
824 data.positions.as_mutable_span().drop_front(prev_point_num));
826 data.curve_indices.append_n_times(curve_i,
data.point_counts.size() - prev_curve_num);
829 int dst_curve_num = 0;
830 int dst_point_num = 0;
831 for (
const PerimeterData &
data : thread_data) {
834 dst_curve_num +=
data.point_counts.size();
835 dst_point_num +=
data.positions.size();
838 bke::CurvesGeometry dst_curves(dst_point_num, dst_curve_num);
839 if (dst_point_num == 0 || dst_curve_num == 0) {
843 bke::MutableAttributeAccessor dst_attributes = dst_curves.attributes_for_write();
844 bke::SpanAttributeWriter<bool> dst_cyclic = dst_attributes.lookup_or_add_for_write_span<
bool>(
845 "cyclic", bke::AttrDomain::Curve);
846 bke::SpanAttributeWriter<int> dst_material = dst_attributes.lookup_or_add_for_write_span<
int>(
847 "material_index", bke::AttrDomain::Curve);
848 bke::SpanAttributeWriter<float> dst_radius = dst_attributes.lookup_or_add_for_write_span<
float>(
849 "radius", bke::AttrDomain::Point);
858 for (
const PerimeterData &
data : thread_data) {
859 curves = curves.
after(
data.point_counts.size());
860 points = points.
after(
data.positions.size());
863 dst_curve_map.as_mutable_span().
slice(curves).copy_from(
data.curve_indices);
865 dst_offsets.
slice(curves).copy_from(
data.point_counts);
866 dst_cyclic.span.
slice(curves).fill(
true);
867 if (material_index >= 0) {
868 dst_material.span.slice(curves).fill(material_index);
872 dst_material.span[curves[
i]] = src_material_index[
data.curve_indices[
i]];
878 dst_point_map.as_mutable_span().slice(points).copy_from(
data.point_indices);
879 dst_radius.span.slice(points).fill(outline_radius);
881 offset_indices::accumulate_counts_to_offsets(dst_curves.offsets_for_write());
883 bke::gather_attributes(src_attributes,
884 bke::AttrDomain::Point,
885 bke::AttrDomain::Point,
886 bke::attribute_filter_from_skip_ref({
"position",
"radius"}),
889 bke::gather_attributes(src_attributes,
890 bke::AttrDomain::Curve,
891 bke::AttrDomain::Curve,
892 bke::attribute_filter_from_skip_ref({
"cyclic",
"material_index"}),
897 dst_material.finish();
899 dst_curves.update_curve_types();
954 segment.curve = curve;
958 this->segments.
append(std::move(segment));
960 return &this->segments.
last();
969 while (!this->segments.
is_empty()) {
973 for (
Segment &
b : merged_segments) {
1004 merged_segments.append(std::move(a));
1008 this->segments = merged_segments;
1023 const float a1 = co_b[1] - co_a[1];
1024 const float b1 = co_a[0] - co_b[0];
1025 const float c1 = a1 * co_a[0] + b1 * co_a[1];
1027 const float a2 = co_d[1] - co_c[1];
1028 const float b2 = co_c[0] - co_d[0];
1029 const float c2 = a2 * co_c[0] + b2 * co_c[1];
1031 const float det = (a1 * b2 - a2 * b1);
1036 float2 isect((b2 * c1 - b1 * c2) / det, (a1 * c2 - a2 * c1) / det);
1040 float distance = (length_ab == 0.0f ?
1061 if (points_by_curve[src_curve].
size() < 2) {
1066 const IndexRange src_curve_points = points_by_curve[src_curve].drop_back(
1068 for (
const int point_a : src_curve_points) {
1069 const int point_b = (point_a == points_by_curve[src_curve].last()) ? src_curve_points.
first() :
1073 const float2 co_a = screen_space_positions[point_a];
1074 const float2 co_b = screen_space_positions[point_b];
1081 float intersection_distance_min =
FLT_MAX;
1082 float intersection_distance_max = -
FLT_MAX;
1087 if (points_by_curve[curve].
size() < 2) {
1092 if (!
BLI_rcti_isect(&bbox_ab, &screen_space_curve_bounds[curve],
nullptr)) {
1098 for (
const int point_c : points) {
1099 const int point_d = (point_c == points_by_curve[curve].last()) ? points.
first() :
1103 if (curve == src_curve &&
1104 (point_a == point_c || point_a == point_d || point_b == point_c || point_b == point_d))
1110 const float2 co_c = screen_space_positions[point_c];
1111 const float2 co_d = screen_space_positions[point_d];
1124 const float2 padded_c = co_c - padding_cd;
1125 const float2 padded_d = co_d + padding_cd;
1129 if (
ELEM(isect.kind, isect.LINE_LINE_CROSS, isect.LINE_LINE_EXACT)) {
1131 r_is_intersected_after_point[point_a] =
true;
1136 co_a, co_b, co_c, co_d);
1137 intersection_distance_min =
math::min(normalized_distance, intersection_distance_min);
1138 intersection_distance_max =
math::max(normalized_distance, intersection_distance_max);
1143 if (r_is_intersected_after_point[point_a]) {
1144 r_intersection_distance[point_a][
Distance::Min] = intersection_distance_min;
1145 r_intersection_distance[point_a][
Distance::Max] = intersection_distance_max;
1155 const int direction,
1162 const int point_first = points_by_curve[segment.curve].first();
1163 const int point_last = points_by_curve[segment.curve].last();
1166 int point_a = segment.point_range[segment_side];
1168 bool intersected =
false;
1169 segment.is_intersected[segment_side] =
false;
1172 while ((direction == 1 && point_a < point_last) || (direction == -1 && point_a > point_first)) {
1173 const int point_b = point_a + direction;
1174 const bool at_end_of_curve = (direction == -1 && point_b == point_first) ||
1175 (direction == 1 && point_b == point_last);
1178 segment.point_range[segment_side] = point_a;
1179 point_is_in_segment[point_a] =
true;
1184 const int intersection_point = direction == 1 ? point_a : point_b;
1185 intersected = is_intersected_after_point[intersection_point];
1188 if (at_end_of_curve &&
1189 ((direction == -1 &&
1191 (direction == 1 && intersection_distance[intersection_point][
Distance::Min] >
1194 intersected =
false;
1203 segment.is_intersected[segment_side] =
true;
1204 segment.intersection_distance[segment_side] =
1205 (direction == 1) ? intersection_distance[intersection_point][
Distance::Min] :
1211 point_a += direction;
1216 if (direction == -1) {
1218 point_is_in_segment[point_first] =
true;
1221 segment.point_range[
Side::End] = point_last;
1222 point_is_in_segment[point_last] =
true;
1236 const int8_t directions[2] = {-1, 1};
1237 for (
const int8_t direction : directions) {
1241 is_intersected_after_point,
1242 intersection_distance,
1243 point_is_in_segment);
1252 const bool keep_caps)
1258 Array<bool> is_intersected_after_point(src_points_num,
false);
1263 screen_space_positions,
1264 screen_space_curve_bounds,
1265 is_intersected_after_point,
1266 intersection_distance);
1272 Array<bool> point_is_in_segment(src_points_num,
false);
1275 Segments &thread_segments = trim_segments_by_thread.
local();
1276 for (
const int selected_point : selected_points_in_curves[
pos]) {
1278 if (point_is_in_segment[selected_point]) {
1288 *segment, src, is_intersected_after_point, intersection_distance, point_is_in_segment);
1296 const int cyclic_outer_point = !segment->is_intersected[
Side::Start] ?
1297 src_points_by_curve[curve_i].last() :
1298 src_points_by_curve[curve_i].first();
1299 segment = thread_segments.
create_segment(curve_i, cyclic_outer_point);
1303 *segment, src, is_intersected_after_point, intersection_distance, point_is_in_segment);
1308 for (
Segments &thread_segments : trim_segments_by_thread) {
1309 trim_segments.
segments.extend(thread_segments.segments);
1314 if (trim_segments.
segments.is_empty()) {
1326 const IndexRange src_points = src_points_by_curve[src_curve];
1327 for (
const int src_point : src_points) {
1329 const int src_next_point = (src_point == src_points.
last()) ? src_points.
first() :
1333 if (!point_is_in_segment[src_point]) {
1334 dst_points.
append({src_point, src_next_point, 0.0f,
true,
false});
1364 dst_points.
append({src_point,
1375 dst_points.
append({src_point,
1386 if (dst_point.is_src_point) {
1387 dst_point.is_cut =
true;
1406 const int frame_number)
1412 int max_bvh_lines = 0;
1413 for (
const int i_drawing : drawings.
index_range()) {
1414 if (drawings[i_drawing].frame_number == frame_number) {
1415 max_bvh_lines += drawings[i_drawing].drawing.strokes().evaluated_points_num();
1420 data.start_positions.reinitialize(max_bvh_lines);
1421 data.end_positions.reinitialize(max_bvh_lines);
1423 data.drawing_offsets.reinitialize(drawings.
size() + 1);
1424 for (
const int i_drawing : drawings.
index_range()) {
1426 data.drawing_offsets[i_drawing] = (drawings[i_drawing].frame_number == frame_number ?
1431 data.drawing_offsets);
1434 for (
const int i_drawing : drawings.
index_range()) {
1436 if (drawings[i_drawing].frame_number != frame_number) {
1450 const IndexRange bvh_index_range = bvh_elements_by_drawing[i_drawing];
1458 const IndexRange evaluated_points = evaluated_points_by_curve[i_curve];
1461 for (
const int i_point : evaluated_points) {
1463 vc.
region, evaluated_positions[i_point], projection);
1464 start_positions[i_point] = co;
1467 const int i_prev_point = (i_point > 0 ? i_point - 1 : evaluated_points.
last());
1468 end_positions[i_prev_point] = co;
1471 for (
const int i_point : evaluated_points.
drop_back(1)) {
1472 const float2 &start = start_positions[i_point];
1473 const float2 &end = end_positions[i_point];
1475 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
1480 const float2 &start = start_positions.
last();
1483 const float bb[6] = {start.x, start.y, 0.0f, end.x, end.y, 0.0f};
1498 data.tree =
nullptr;
1500 data.drawing_offsets.reinitialize(0);
1501 data.start_positions.reinitialize(0);
1502 data.end_positions.reinitialize(0);
1518 struct RaycastArgs {
1530 const RaycastArgs &args = *
static_cast<const RaycastArgs *
>(userdata);
1531 if (
ELEM(index, args.ignore_index1, args.ignore_index2, args.ignore_index3)) {
1536 const float2 ray_end = ray_start +
float2(ray->direction) * ray->radius;
1537 const float2 &line_start = args.tree_data.start_positions[index];
1538 const float2 &line_end = args.tree_data.end_positions[index];
1544 if (dist >= hit->dist) {
1551 hit->no[0] =
result.lambda;
1555 auto do_raycast = [&](
const int index_back,
1557 const int index_forward,
1558 float &r_lambda) ->
bool {
1559 if (index_forward < 0) {
1563 const float2 start = screen_space_positions[index];
1564 const float2 end = screen_space_positions[index_forward];
1568 RaycastArgs args = {tree_data,
1569 index_back >= 0 ? int(tree_data_range[index_back]) : -1,
1570 int(tree_data_range[index]),
1571 index_forward >= 0 ? int(tree_data_range[index_forward]) : -1};
1578 if (hit.
index >= 0) {
1579 r_lambda = hit.
no[0];
1586 if (r_first_intersect_factors) {
1587 r_first_intersect_factors->fill(-1.0f);
1589 if (r_last_intersect_factors) {
1590 r_last_intersect_factors->fill(-1.0f);
1595 const IndexRange points = points_by_curve[i_curve];
1597 for (
const int i_point : points) {
1598 const int i_prev_point = (i_point == points.
first() ? (
is_cyclic ? points.
last() : -1) :
1600 const int i_next_point = (i_point == points.
last() ? (
is_cyclic ? points.
first() : -1) :
1604 if (do_raycast(i_prev_point, i_point, i_next_point, lambda)) {
1605 r_hits[i_point] =
true;
1606 if (r_first_intersect_factors) {
1607 (*r_first_intersect_factors)[i_point] = lambda;
1611 if (do_raycast(i_next_point, i_point, i_prev_point, lambda)) {
1613 if (r_last_intersect_factors) {
1614 (*r_last_intersect_factors)[i_point] = 1.0f - lambda;
1635 screen_space_positions,
1648 result.segment_offsets.reinitialize(
curves.curves_num() + 1);
1650 result.segment_offsets.fill(0);
1652 const IndexRange points = points_by_curve[curve_i];
1663 const int num_segments = segments_by_curve.
total_size();
1664 result.segment_start_points.reinitialize(num_segments);
1665 result.segment_start_fractions.reinitialize(num_segments);
1668 const IndexRange points = points_by_curve[curve_i];
1671 const IndexRange segments = segments_by_curve[curve_i];
1672 const int hit_segments_start = (
is_cyclic ? 0 : 1);
1680 result.segment_start_points[segments[0]] = points.
first();
1681 result.segment_start_fractions[segments[0]] = 0.0f;
1684 curve_hit_mask.
foreach_index([&](
const int point_i,
const int hit_i) {
1685 result.segment_start_points[segments[hit_segments_start + hit_i]] = point_i;
1686 result.segment_start_fractions[segments[hit_segments_start + hit_i]] =
1687 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])
#define GP_STROKE_MITER_ANGLE_ROUND
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
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
void append_n_times(const T &value, const int64_t n)
GAttributeReader lookup_or_default(StringRef attribute_id, AttrDomain domain, AttrType 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)
float length(VecOp< float, D >) RET
float distance(VecOp< float, D >, VecOp< float, D >) RET
VecBase< float, 2 > float2
VecBase< float, 3 > float3
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_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)
static void generate_corner(const float3 &pt_a, const float3 &pt_b, const float3 &pt_c, const float radius, const float miter_limit_angle, const int corner_subdivisions, const int src_point_index, Vector< float3 > &r_perimeter, Vector< int > &r_src_indices)
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)
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_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 VArray< float > miter_angles, const float outline_offset, Vector< float3 > &r_perimeter, Vector< int > &r_point_counts, Vector< int > &r_point_indices)
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)
void transform_points(const float4x4 &transform, MutableSpan< float3 > points, bool use_threading=true)
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()