/* SPDX-License-Identifier: GPL-2.0-or-later * Copyright 2012 Blender Foundation. All rights reserved. */ /** \file * \ingroup bke * * Functions for evaluating the mask beziers into points for the outline and feather. */ #include #include #include "MEM_guardedalloc.h" #include "BLI_math.h" #include "BLI_utildefines.h" #include "DNA_mask_types.h" #include "DNA_object_types.h" #include "BKE_curve.h" #include "BKE_mask.h" #include "DEG_depsgraph.h" #include "DEG_depsgraph_query.h" unsigned int BKE_mask_spline_resolution(MaskSpline *spline, int width, int height) { float max_segment = 0.01f; unsigned int i, resol = 1; if (width != 0 && height != 0) { max_segment = 1.0f / (float)max_ii(width, height); } for (i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; BezTriple *bezt_curr, *bezt_next; float a, b, c, len; unsigned int cur_resol; bezt_curr = &point->bezt; bezt_next = BKE_mask_spline_point_next_bezt(spline, spline->points, point); if (bezt_next == NULL) { break; } a = len_v3v3(bezt_curr->vec[1], bezt_curr->vec[2]); b = len_v3v3(bezt_curr->vec[2], bezt_next->vec[0]); c = len_v3v3(bezt_next->vec[0], bezt_next->vec[1]); len = a + b + c; cur_resol = len / max_segment; resol = MAX2(resol, cur_resol); if (resol >= MASK_RESOL_MAX) { break; } } return CLAMPIS(resol, 1, MASK_RESOL_MAX); } unsigned int BKE_mask_spline_feather_resolution(MaskSpline *spline, int width, int height) { const float max_segment = 0.005; unsigned int resol = BKE_mask_spline_resolution(spline, width, height); float max_jump = 0.0f; /* avoid checking the featrher if we already hit the maximum value */ if (resol >= MASK_RESOL_MAX) { return MASK_RESOL_MAX; } for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; float prev_u = 0.0f; float prev_w = point->bezt.weight; for (int j = 0; j < point->tot_uw; j++) { const float w_diff = (point->uw[j].w - prev_w); const float u_diff = (point->uw[j].u - prev_u); /* avoid divide by zero and very high values, * though these get clamped eventually */ if (u_diff > FLT_EPSILON) { float jump = fabsf(w_diff / u_diff); max_jump = max_ff(max_jump, jump); } prev_u = point->uw[j].u; prev_w = point->uw[j].w; } } resol += max_jump / max_segment; return CLAMPIS(resol, 1, MASK_RESOL_MAX); } int BKE_mask_spline_differentiate_calc_total(const MaskSpline *spline, const unsigned int resol) { if (spline->flag & MASK_SPLINE_CYCLIC) { return spline->tot_point * resol; } return ((spline->tot_point - 1) * resol) + 1; } float (*BKE_mask_spline_differentiate_with_resolution(MaskSpline *spline, const unsigned int resol, unsigned int *r_tot_diff_point))[2] { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); MaskSplinePoint *point_curr, *point_prev; float(*diff_points)[2], (*fp)[2]; const int tot = BKE_mask_spline_differentiate_calc_total(spline, resol); int a; if (spline->tot_point <= 1) { /* nothing to differentiate */ *r_tot_diff_point = 0; return NULL; } /* len+1 because of 'forward_diff_bezier' function */ *r_tot_diff_point = tot; diff_points = fp = MEM_mallocN((tot + 1) * sizeof(*diff_points), "mask spline vets"); a = spline->tot_point - 1; if (spline->flag & MASK_SPLINE_CYCLIC) { a++; } point_prev = points_array; point_curr = point_prev + 1; while (a--) { BezTriple *bezt_prev; BezTriple *bezt_curr; int j; if (a == 0 && (spline->flag & MASK_SPLINE_CYCLIC)) { point_curr = points_array; } bezt_prev = &point_prev->bezt; bezt_curr = &point_curr->bezt; for (j = 0; j < 2; j++) { BKE_curve_forward_diff_bezier(bezt_prev->vec[1][j], bezt_prev->vec[2][j], bezt_curr->vec[0][j], bezt_curr->vec[1][j], &(*fp)[j], resol, sizeof(float[2])); } fp += resol; if (a == 0 && (spline->flag & MASK_SPLINE_CYCLIC) == 0) { copy_v2_v2(*fp, bezt_curr->vec[1]); } point_prev = point_curr; point_curr++; } return diff_points; } float (*BKE_mask_spline_differentiate( MaskSpline *spline, int width, int height, unsigned int *r_tot_diff_point))[2] { uint resol = BKE_mask_spline_resolution(spline, width, height); return BKE_mask_spline_differentiate_with_resolution(spline, resol, r_tot_diff_point); } /* ** feather points self-intersection collapse routine ** */ typedef struct FeatherEdgesBucket { int tot_segment; int (*segments)[2]; int alloc_segment; } FeatherEdgesBucket; static void feather_bucket_add_edge(FeatherEdgesBucket *bucket, int start, int end) { const int alloc_delta = 256; if (bucket->tot_segment >= bucket->alloc_segment) { if (!bucket->segments) { bucket->segments = MEM_callocN(alloc_delta * sizeof(*bucket->segments), "feather bucket segments"); } else { bucket->segments = MEM_reallocN( bucket->segments, (alloc_delta + bucket->tot_segment) * sizeof(*bucket->segments)); } bucket->alloc_segment += alloc_delta; } bucket->segments[bucket->tot_segment][0] = start; bucket->segments[bucket->tot_segment][1] = end; bucket->tot_segment++; } static void feather_bucket_check_intersect(float (*feather_points)[2], int tot_feather_point, FeatherEdgesBucket *bucket, int cur_a, int cur_b) { const float *v1 = (float *)feather_points[cur_a]; const float *v2 = (float *)feather_points[cur_b]; for (int i = 0; i < bucket->tot_segment; i++) { int check_a = bucket->segments[i][0]; int check_b = bucket->segments[i][1]; const float *v3 = (float *)feather_points[check_a]; const float *v4 = (float *)feather_points[check_b]; if (check_a >= cur_a - 1 || cur_b == check_a) { continue; } if (isect_seg_seg_v2_simple(v1, v2, v3, v4)) { int k; float p[2]; float min_a[2], max_a[2]; float min_b[2], max_b[2]; isect_seg_seg_v2_point(v1, v2, v3, v4, p); INIT_MINMAX2(min_a, max_a); INIT_MINMAX2(min_b, max_b); /* collapse loop with smaller AABB */ for (k = 0; k < tot_feather_point; k++) { if (k >= check_b && k <= cur_a) { minmax_v2v2_v2(min_a, max_a, feather_points[k]); } else { minmax_v2v2_v2(min_b, max_b, feather_points[k]); } } if (max_a[0] - min_a[0] < max_b[0] - min_b[0] || max_a[1] - min_a[1] < max_b[1] - min_b[1]) { for (k = check_b; k <= cur_a; k++) { copy_v2_v2(feather_points[k], p); } } else { for (k = 0; k <= check_a; k++) { copy_v2_v2(feather_points[k], p); } if (cur_b != 0) { for (k = cur_b; k < tot_feather_point; k++) { copy_v2_v2(feather_points[k], p); } } } } } } static int feather_bucket_index_from_coord(const float co[2], const float min[2], const float bucket_scale[2], const int buckets_per_side) { int x = (int)((co[0] - min[0]) * bucket_scale[0]); int y = (int)((co[1] - min[1]) * bucket_scale[1]); if (x == buckets_per_side) { x--; } if (y == buckets_per_side) { y--; } return y * buckets_per_side + x; } static void feather_bucket_get_diagonal(FeatherEdgesBucket *buckets, int start_bucket_index, int end_bucket_index, int buckets_per_side, FeatherEdgesBucket **r_diagonal_bucket_a, FeatherEdgesBucket **r_diagonal_bucket_b) { int start_bucket_x = start_bucket_index % buckets_per_side; int start_bucket_y = start_bucket_index / buckets_per_side; int end_bucket_x = end_bucket_index % buckets_per_side; int end_bucket_y = end_bucket_index / buckets_per_side; int diagonal_bucket_a_index = start_bucket_y * buckets_per_side + end_bucket_x; int diagonal_bucket_b_index = end_bucket_y * buckets_per_side + start_bucket_x; *r_diagonal_bucket_a = &buckets[diagonal_bucket_a_index]; *r_diagonal_bucket_b = &buckets[diagonal_bucket_b_index]; } void BKE_mask_spline_feather_collapse_inner_loops(MaskSpline *spline, float (*feather_points)[2], const unsigned int tot_feather_point) { #define BUCKET_INDEX(co) feather_bucket_index_from_coord(co, min, bucket_scale, buckets_per_side) int buckets_per_side, tot_bucket; float bucket_size, bucket_scale[2]; FeatherEdgesBucket *buckets; float min[2], max[2]; float max_delta_x = -1.0f, max_delta_y = -1.0f, max_delta; if (tot_feather_point < 4) { /* self-intersection works only for quads at least, * in other cases polygon can't be self-intersecting anyway */ return; } /* find min/max corners of mask to build buckets in that space */ INIT_MINMAX2(min, max); for (uint i = 0; i < tot_feather_point; i++) { unsigned int next = i + 1; float delta; minmax_v2v2_v2(min, max, feather_points[i]); if (next == tot_feather_point) { if (spline->flag & MASK_SPLINE_CYCLIC) { next = 0; } else { break; } } delta = fabsf(feather_points[i][0] - feather_points[next][0]); if (delta > max_delta_x) { max_delta_x = delta; } delta = fabsf(feather_points[i][1] - feather_points[next][1]); if (delta > max_delta_y) { max_delta_y = delta; } } /* prevent divisionsby zero by ensuring bounding box is not collapsed */ if (max[0] - min[0] < FLT_EPSILON) { max[0] += 0.01f; min[0] -= 0.01f; } if (max[1] - min[1] < FLT_EPSILON) { max[1] += 0.01f; min[1] -= 0.01f; } /* use dynamically calculated buckets per side, so we likely wouldn't * run into a situation when segment doesn't fit two buckets which is * pain collecting candidates for intersection */ max_delta_x /= max[0] - min[0]; max_delta_y /= max[1] - min[1]; max_delta = MAX2(max_delta_x, max_delta_y); buckets_per_side = min_ii(512, 0.9f / max_delta); if (buckets_per_side == 0) { /* happens when some segment fills the whole bounding box across some of dimension */ buckets_per_side = 1; } tot_bucket = buckets_per_side * buckets_per_side; bucket_size = 1.0f / buckets_per_side; /* pre-compute multipliers, to save mathematical operations in loops */ bucket_scale[0] = 1.0f / ((max[0] - min[0]) * bucket_size); bucket_scale[1] = 1.0f / ((max[1] - min[1]) * bucket_size); /* fill in buckets' edges */ buckets = MEM_callocN(sizeof(FeatherEdgesBucket) * tot_bucket, "feather buckets"); for (int i = 0; i < tot_feather_point; i++) { int start = i, end = i + 1; int start_bucket_index, end_bucket_index; if (end == tot_feather_point) { if (spline->flag & MASK_SPLINE_CYCLIC) { end = 0; } else { break; } } start_bucket_index = BUCKET_INDEX(feather_points[start]); end_bucket_index = BUCKET_INDEX(feather_points[end]); feather_bucket_add_edge(&buckets[start_bucket_index], start, end); if (start_bucket_index != end_bucket_index) { FeatherEdgesBucket *end_bucket = &buckets[end_bucket_index]; FeatherEdgesBucket *diagonal_bucket_a, *diagonal_bucket_b; feather_bucket_get_diagonal(buckets, start_bucket_index, end_bucket_index, buckets_per_side, &diagonal_bucket_a, &diagonal_bucket_b); feather_bucket_add_edge(end_bucket, start, end); feather_bucket_add_edge(diagonal_bucket_a, start, end); feather_bucket_add_edge(diagonal_bucket_a, start, end); } } /* check all edges for intersection with edges from their buckets */ for (int i = 0; i < tot_feather_point; i++) { int cur_a = i, cur_b = i + 1; int start_bucket_index, end_bucket_index; FeatherEdgesBucket *start_bucket; if (cur_b == tot_feather_point) { cur_b = 0; } start_bucket_index = BUCKET_INDEX(feather_points[cur_a]); end_bucket_index = BUCKET_INDEX(feather_points[cur_b]); start_bucket = &buckets[start_bucket_index]; feather_bucket_check_intersect(feather_points, tot_feather_point, start_bucket, cur_a, cur_b); if (start_bucket_index != end_bucket_index) { FeatherEdgesBucket *end_bucket = &buckets[end_bucket_index]; FeatherEdgesBucket *diagonal_bucket_a, *diagonal_bucket_b; feather_bucket_get_diagonal(buckets, start_bucket_index, end_bucket_index, buckets_per_side, &diagonal_bucket_a, &diagonal_bucket_b); feather_bucket_check_intersect(feather_points, tot_feather_point, end_bucket, cur_a, cur_b); feather_bucket_check_intersect( feather_points, tot_feather_point, diagonal_bucket_a, cur_a, cur_b); feather_bucket_check_intersect( feather_points, tot_feather_point, diagonal_bucket_b, cur_a, cur_b); } } /* free buckets */ for (int i = 0; i < tot_bucket; i++) { if (buckets[i].segments) { MEM_freeN(buckets[i].segments); } } MEM_freeN(buckets); #undef BUCKET_INDEX } /** only called from #BKE_mask_spline_feather_differentiated_points_with_resolution() ! */ static float (*mask_spline_feather_differentiated_points_with_resolution__even( MaskSpline *spline, const unsigned int resol, const bool do_feather_isect, unsigned int *r_tot_feather_point))[2] { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); MaskSplinePoint *point_curr, *point_prev; float(*feather)[2], (*fp)[2]; const int tot = BKE_mask_spline_differentiate_calc_total(spline, resol); int a; /* tot+1 because of 'forward_diff_bezier' function */ feather = fp = MEM_mallocN((tot + 1) * sizeof(*feather), "mask spline feather diff points"); a = spline->tot_point - 1; if (spline->flag & MASK_SPLINE_CYCLIC) { a++; } point_prev = points_array; point_curr = point_prev + 1; while (a--) { /* BezTriple *bezt_prev; */ /* UNUSED */ /* BezTriple *bezt_curr; */ /* UNUSED */ int j; if (a == 0 && (spline->flag & MASK_SPLINE_CYCLIC)) { point_curr = points_array; } /* bezt_prev = &point_prev->bezt; */ /* bezt_curr = &point_curr->bezt; */ for (j = 0; j < resol; j++, fp++) { float u = (float)j / resol, weight; float co[2], n[2]; /* TODO: these calls all calculate similar things * could be unified for some speed */ BKE_mask_point_segment_co(spline, point_prev, u, co); BKE_mask_point_normal(spline, point_prev, u, n); weight = BKE_mask_point_weight(spline, point_prev, u); madd_v2_v2v2fl(*fp, co, n, weight); } if (a == 0 && (spline->flag & MASK_SPLINE_CYCLIC) == 0) { float u = 1.0f, weight; float co[2], n[2]; BKE_mask_point_segment_co(spline, point_prev, u, co); BKE_mask_point_normal(spline, point_prev, u, n); weight = BKE_mask_point_weight(spline, point_prev, u); madd_v2_v2v2fl(*fp, co, n, weight); } point_prev = point_curr; point_curr++; } *r_tot_feather_point = tot; if ((spline->flag & MASK_SPLINE_NOINTERSECT) && do_feather_isect) { BKE_mask_spline_feather_collapse_inner_loops(spline, feather, tot); } return feather; } /** only called from #BKE_mask_spline_feather_differentiated_points_with_resolution() ! */ static float (*mask_spline_feather_differentiated_points_with_resolution__double( MaskSpline *spline, const unsigned int resol, const bool do_feather_isect, unsigned int *r_tot_feather_point))[2] { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); MaskSplinePoint *point_curr, *point_prev; float(*feather)[2], (*fp)[2]; const int tot = BKE_mask_spline_differentiate_calc_total(spline, resol); int a; if (spline->tot_point <= 1) { /* nothing to differentiate */ *r_tot_feather_point = 0; return NULL; } /* len+1 because of 'forward_diff_bezier' function */ *r_tot_feather_point = tot; feather = fp = MEM_mallocN((tot + 1) * sizeof(*feather), "mask spline vets"); a = spline->tot_point - 1; if (spline->flag & MASK_SPLINE_CYCLIC) { a++; } point_prev = points_array; point_curr = point_prev + 1; while (a--) { BezTriple local_prevbezt; BezTriple local_bezt; float point_prev_n[2], point_curr_n[2], tvec[2]; float weight_prev, weight_curr; float len_base, len_feather, len_scalar; BezTriple *bezt_prev; BezTriple *bezt_curr; int j; if (a == 0 && (spline->flag & MASK_SPLINE_CYCLIC)) { point_curr = points_array; } bezt_prev = &point_prev->bezt; bezt_curr = &point_curr->bezt; /* modified copy for feather */ local_prevbezt = *bezt_prev; local_bezt = *bezt_curr; bezt_prev = &local_prevbezt; bezt_curr = &local_bezt; /* calc the normals */ sub_v2_v2v2(tvec, bezt_prev->vec[1], bezt_prev->vec[0]); normalize_v2(tvec); point_prev_n[0] = -tvec[1]; point_prev_n[1] = tvec[0]; sub_v2_v2v2(tvec, bezt_curr->vec[1], bezt_curr->vec[0]); normalize_v2(tvec); point_curr_n[0] = -tvec[1]; point_curr_n[1] = tvec[0]; weight_prev = bezt_prev->weight; weight_curr = bezt_curr->weight; mul_v2_fl(point_prev_n, weight_prev); mul_v2_fl(point_curr_n, weight_curr); /* before we transform verts */ len_base = len_v2v2(bezt_prev->vec[1], bezt_curr->vec[1]); // add_v2_v2(bezt_prev->vec[0], point_prev_n); // not needed add_v2_v2(bezt_prev->vec[1], point_prev_n); add_v2_v2(bezt_prev->vec[2], point_prev_n); add_v2_v2(bezt_curr->vec[0], point_curr_n); add_v2_v2(bezt_curr->vec[1], point_curr_n); // add_v2_v2(bezt_curr->vec[2], point_curr_n); // not needed len_feather = len_v2v2(bezt_prev->vec[1], bezt_curr->vec[1]); /* scale by change in length */ len_scalar = len_feather / len_base; dist_ensure_v2_v2fl(bezt_prev->vec[2], bezt_prev->vec[1], len_scalar * len_v2v2(bezt_prev->vec[2], bezt_prev->vec[1])); dist_ensure_v2_v2fl(bezt_curr->vec[0], bezt_curr->vec[1], len_scalar * len_v2v2(bezt_curr->vec[0], bezt_curr->vec[1])); for (j = 0; j < 2; j++) { BKE_curve_forward_diff_bezier(bezt_prev->vec[1][j], bezt_prev->vec[2][j], bezt_curr->vec[0][j], bezt_curr->vec[1][j], &(*fp)[j], resol, sizeof(float[2])); } /* scale by the uw's */ if (point_prev->tot_uw) { for (j = 0; j < resol; j++, fp++) { float u = (float)j / resol; float weight_uw, weight_scalar; float co[2]; /* TODO: these calls all calculate similar things * could be unified for some speed */ BKE_mask_point_segment_co(spline, point_prev, u, co); weight_uw = BKE_mask_point_weight(spline, point_prev, u); weight_scalar = BKE_mask_point_weight_scalar(spline, point_prev, u); dist_ensure_v2_v2fl(*fp, co, len_v2v2(*fp, co) * (weight_uw / weight_scalar)); } } else { fp += resol; } if (a == 0 && (spline->flag & MASK_SPLINE_CYCLIC) == 0) { copy_v2_v2(*fp, bezt_curr->vec[1]); } point_prev = point_curr; point_curr++; } if ((spline->flag & MASK_SPLINE_NOINTERSECT) && do_feather_isect) { BKE_mask_spline_feather_collapse_inner_loops(spline, feather, tot); } return feather; } float (*BKE_mask_spline_feather_differentiated_points_with_resolution( MaskSpline *spline, const unsigned int resol, const bool do_feather_isect, unsigned int *r_tot_feather_point))[2] { switch (spline->offset_mode) { case MASK_SPLINE_OFFSET_EVEN: return mask_spline_feather_differentiated_points_with_resolution__even( spline, resol, do_feather_isect, r_tot_feather_point); case MASK_SPLINE_OFFSET_SMOOTH: default: return mask_spline_feather_differentiated_points_with_resolution__double( spline, resol, do_feather_isect, r_tot_feather_point); } } float (*BKE_mask_spline_feather_points(MaskSpline *spline, int *r_tot_feather_point))[2] { MaskSplinePoint *points_array = BKE_mask_spline_point_array(spline); int i, tot = 0; float(*feather)[2], (*fp)[2]; /* count */ for (i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &points_array[i]; tot += point->tot_uw + 1; } /* create data */ feather = fp = MEM_mallocN(tot * sizeof(*feather), "mask spline feather points"); for (i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &points_array[i]; BezTriple *bezt = &point->bezt; float weight, n[2]; int j; BKE_mask_point_normal(spline, point, 0.0f, n); weight = BKE_mask_point_weight(spline, point, 0.0f); madd_v2_v2v2fl(*fp, bezt->vec[1], n, weight); fp++; for (j = 0; j < point->tot_uw; j++) { float u = point->uw[j].u; float co[2]; BKE_mask_point_segment_co(spline, point, u, co); BKE_mask_point_normal(spline, point, u, n); weight = BKE_mask_point_weight(spline, point, u); madd_v2_v2v2fl(*fp, co, n, weight); fp++; } } *r_tot_feather_point = tot; return feather; } float *BKE_mask_point_segment_feather_diff(MaskSpline *spline, MaskSplinePoint *point, int width, int height, unsigned int *r_tot_feather_point) { float *feather, *fp; unsigned int resol = BKE_mask_spline_feather_resolution(spline, width, height); feather = fp = MEM_callocN(2 * resol * sizeof(float), "mask point spline feather diff points"); for (uint i = 0; i < resol; i++, fp += 2) { float u = (float)(i % resol) / resol, weight; float co[2], n[2]; BKE_mask_point_segment_co(spline, point, u, co); BKE_mask_point_normal(spline, point, u, n); weight = BKE_mask_point_weight(spline, point, u); fp[0] = co[0] + n[0] * weight; fp[1] = co[1] + n[1] * weight; } *r_tot_feather_point = resol; return feather; } float *BKE_mask_point_segment_diff(MaskSpline *spline, MaskSplinePoint *point, int width, int height, unsigned int *r_tot_diff_point) { MaskSplinePoint *points_array = BKE_mask_spline_point_array_from_point(spline, point); BezTriple *bezt, *bezt_next; float *diff_points, *fp; int j, resol = BKE_mask_spline_resolution(spline, width, height); bezt = &point->bezt; bezt_next = BKE_mask_spline_point_next_bezt(spline, points_array, point); if (!bezt_next) { return NULL; } /* resol+1 because of 'forward_diff_bezier' function */ *r_tot_diff_point = resol + 1; diff_points = fp = MEM_callocN(sizeof(float[2]) * (resol + 1), "mask segment vets"); for (j = 0; j < 2; j++) { BKE_curve_forward_diff_bezier(bezt->vec[1][j], bezt->vec[2][j], bezt_next->vec[0][j], bezt_next->vec[1][j], fp + j, resol, sizeof(float[2])); } copy_v2_v2(fp + 2 * resol, bezt_next->vec[1]); return diff_points; } static void mask_evaluate_apply_point_parent(MaskSplinePoint *point, float ctime) { float parent_matrix[3][3]; BKE_mask_point_parent_matrix_get(point, ctime, parent_matrix); mul_m3_v2(parent_matrix, point->bezt.vec[0]); mul_m3_v2(parent_matrix, point->bezt.vec[1]); mul_m3_v2(parent_matrix, point->bezt.vec[2]); } void BKE_mask_layer_evaluate_animation(MaskLayer *masklay, const float ctime) { /* animation if available */ MaskLayerShape *masklay_shape_a; MaskLayerShape *masklay_shape_b; int found; if ((found = BKE_mask_layer_shape_find_frame_range( masklay, ctime, &masklay_shape_a, &masklay_shape_b))) { if (found == 1) { #if 0 printf("%s: exact %d %d (%d)\n", __func__, (int)ctime, BLI_listbase_count(&masklay->splines_shapes), masklay_shape_a->frame); #endif BKE_mask_layer_shape_to_mask(masklay, masklay_shape_a); } else if (found == 2) { float w = masklay_shape_b->frame - masklay_shape_a->frame; #if 0 printf("%s: tween %d %d (%d %d)\n", __func__, (int)ctime, BLI_listbase_count(&masklay->splines_shapes), masklay_shape_a->frame, masklay_shape_b->frame); #endif BKE_mask_layer_shape_to_mask_interp( masklay, masklay_shape_a, masklay_shape_b, (ctime - masklay_shape_a->frame) / w); } else { /* always fail, should never happen */ BLI_assert(found == 2); } } } void BKE_mask_layer_evaluate_deform(MaskLayer *masklay, const float ctime) { BKE_mask_layer_calc_handles(masklay); for (MaskSpline *spline = masklay->splines.first; spline != NULL; spline = spline->next) { bool need_handle_recalc = false; BKE_mask_spline_ensure_deform(spline); for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point = &spline->points[i]; MaskSplinePoint *point_deform = &spline->points_deform[i]; BKE_mask_point_free(point_deform); *point_deform = *point; point_deform->uw = point->uw ? MEM_dupallocN(point->uw) : NULL; mask_evaluate_apply_point_parent(point_deform, ctime); if (ELEM(point->bezt.h1, HD_AUTO, HD_VECT)) { need_handle_recalc = true; } } /* if the spline has auto or vector handles, these need to be * recalculated after deformation. */ if (need_handle_recalc) { for (int i = 0; i < spline->tot_point; i++) { MaskSplinePoint *point_deform = &spline->points_deform[i]; if (ELEM(point_deform->bezt.h1, HD_AUTO, HD_VECT)) { BKE_mask_calc_handle_point(spline, point_deform); } } } /* end extra calc handles loop */ } } void BKE_mask_eval_animation(struct Depsgraph *depsgraph, Mask *mask) { float ctime = DEG_get_ctime(depsgraph); DEG_debug_print_eval(depsgraph, __func__, mask->id.name, mask); for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { BKE_mask_layer_evaluate_animation(mask_layer, ctime); } } void BKE_mask_eval_update(struct Depsgraph *depsgraph, Mask *mask) { const bool is_depsgraph_active = DEG_is_active(depsgraph); float ctime = DEG_get_ctime(depsgraph); DEG_debug_print_eval(depsgraph, __func__, mask->id.name, mask); for (MaskLayer *mask_layer = mask->masklayers.first; mask_layer != NULL; mask_layer = mask_layer->next) { BKE_mask_layer_evaluate_deform(mask_layer, ctime); } if (is_depsgraph_active) { Mask *mask_orig = (Mask *)DEG_get_original_id(&mask->id); for (MaskLayer *masklay_orig = mask_orig->masklayers.first, *masklay_eval = mask->masklayers.first; masklay_orig != NULL; masklay_orig = masklay_orig->next, masklay_eval = masklay_eval->next) { for (MaskSpline *spline_orig = masklay_orig->splines.first, *spline_eval = masklay_eval->splines.first; spline_orig != NULL; spline_orig = spline_orig->next, spline_eval = spline_eval->next) { for (int i = 0; i < spline_eval->tot_point; i++) { MaskSplinePoint *point_eval = &spline_eval->points[i]; MaskSplinePoint *point_orig = &spline_orig->points[i]; point_orig->bezt = point_eval->bezt; } } } } }