Cleanup: Remove unused gpencil_geom_legacy.cc functions

See #123468.
This commit is contained in:
Hans Goudey
2024-11-02 21:05:59 +01:00
parent 919aad31d6
commit d4011668dd
2 changed files with 0 additions and 1163 deletions

View File

@@ -13,12 +13,7 @@
#include "BLI_bounds_types.hh"
#include "BLI_math_vector_types.hh"
struct Depsgraph;
struct Main;
struct Object;
struct Scene;
struct bGPDcurve;
struct bGPDlayer;
struct bGPDframe;
struct bGPDspoint;
struct bGPDstroke;
@@ -61,19 +56,6 @@ void BKE_gpencil_stroke_boundingbox_calc(struct bGPDstroke *gps);
/* Stroke geometry utilities. */
/**
* Calculate stroke normals.
* \param gps: Grease pencil stroke
* \param r_normal: Return Normal vector normalized
*/
void BKE_gpencil_stroke_normal(const struct bGPDstroke *gps, float r_normal[3]);
/**
* Trim stroke to the first intersection or loop.
* \param gps: Stroke data
*/
bool BKE_gpencil_stroke_trim(struct bGPdata *gpd, struct bGPDstroke *gps);
/**
* Get points of stroke always flat to view not affected
* by camera view or view position.
@@ -86,24 +68,6 @@ void BKE_gpencil_stroke_2d_flat(const struct bGPDspoint *points,
int totpoints,
float (*points2d)[2],
int *r_direction);
/**
* Get points of stroke always flat to view not affected by camera view or view position
* using another stroke as reference.
* \param ref_points: Array of reference points (3D)
* \param ref_totpoints: Total reference points
* \param points: Array of points to flat (3D)
* \param totpoints: Total points
* \param points2d: Result array of 2D points
* \param scale: Scale factor
* \param r_direction: Return Concave (-1), Convex (1), or Auto-detect (0)
*/
void BKE_gpencil_stroke_2d_flat_ref(const struct bGPDspoint *ref_points,
int ref_totpoints,
const struct bGPDspoint *points,
int totpoints,
float (*points2d)[2],
float scale,
int *r_direction);
/**
* Triangulate stroke to generate data for filling areas.
* \param gps: Grease pencil stroke
@@ -154,89 +118,6 @@ void BKE_gpencil_point_coords_apply_with_mat4(struct bGPdata *gpd,
const GPencilPointCoordinates *elem_data,
const float mat[4][4]);
/**
* Apply smooth position to stroke point.
* \param gps: Stroke to smooth
* \param i: Point index
* \param inf: Amount of smoothing to apply
* \param iterations: Radius of points to consider, equivalent to iterations
* \param smooth_caps: Apply smooth to stroke extremes
* \param keep_shape: Smooth out fine details first
* \param r_gps: Stroke to put the result into
*/
bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps,
int point_index,
float influence,
int iterations,
bool smooth_caps,
bool keep_shape,
struct bGPDstroke *r_gps);
/**
* Apply smooth strength to stroke point.
* \param gps: Stroke to smooth
* \param point_index: Point index
* \param influence: Amount of smoothing to apply
* \param iterations: Radius of points to consider, equivalent to iterations
* \param r_gps: Stroke to put the result into
*/
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps,
int point_index,
float influence,
int iterations,
struct bGPDstroke *r_gps);
/**
* Apply smooth for thickness to stroke point (use pressure).
* \param gps: Stroke to smooth
* \param point_index: Point index
* \param influence: Amount of smoothing to apply
* \param iterations: Radius of points to consider, equivalent to iterations
* \param r_gps: Stroke to put the result into
*/
bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps,
int point_index,
float influence,
int iterations,
struct bGPDstroke *r_gps);
/**
* Apply smooth for UV rotation/factor to stroke point.
* \param gps: Stroke to smooth
* \param point_index: Point index
* \param influence: Amount of smoothing to apply
* \param iterations: Radius of points to consider, equivalent to iterations
* \param r_gps: Stroke to put the result into
*/
bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps,
int point_index,
float influence,
int iterations,
struct bGPDstroke *r_gps);
/**
* Apply smooth operation to the stroke.
* \param gps: Stroke to smooth
* \param influence: The interpolation factor for the smooth and the original stroke
* \param iterations: Radius of points to consider, equivalent to iterations
* \param smooth_position: Smooth point locations
* \param smooth_strength: Smooth point strength
* \param smooth_thickness: Smooth point thickness
* \param smooth_uv: Smooth uv rotation/factor
* \param keep_shape: Use different distribution for smooth locations to keep the shape
* \param weights: per point weights to multiply influence with (optional, can be null)
*/
void BKE_gpencil_stroke_smooth(struct bGPDstroke *gps,
const float influence,
const int iterations,
const bool smooth_position,
const bool smooth_strength,
const bool smooth_thickness,
const bool smooth_uv,
const bool keep_shape,
const float *weights);
/**
* Close grease pencil stroke.
* \param gps: Stroke to close
*/
bool BKE_gpencil_stroke_close(struct bGPDstroke *gps);
/**
* Split the given stroke into several new strokes, partitioning
* it based on whether the stroke points have a particular flag
@@ -250,65 +131,3 @@ struct bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(struct bGPdata *gpd,
bool select,
bool flat_cap,
int limit);
/**
* Flip stroke.
*/
void BKE_gpencil_stroke_flip(struct bGPDstroke *gps);
/**
* Calculate grease pencil stroke length.
* \param gps: Grease pencil stroke.
* \param use_3d: Set to true to use 3D points.
* \return Length of the stroke.
*/
float BKE_gpencil_stroke_length(const struct bGPDstroke *gps, bool use_3d);
/** Calculate grease pencil stroke length between points. */
float BKE_gpencil_stroke_segment_length(const struct bGPDstroke *gps,
int start_index,
int end_index,
bool use_3d);
/**
* Set a random color to stroke using vertex color.
* \param gps: Stroke
*/
void BKE_gpencil_stroke_set_random_color(struct bGPDstroke *gps);
/**
* Join two strokes using the shortest distance (reorder stroke if necessary).
* \param auto_flip: Flip the stroke if the join between two strokes is not end->start points.
*/
void BKE_gpencil_stroke_join(struct bGPDstroke *gps_a,
struct bGPDstroke *gps_b,
bool leave_gaps,
bool fit_thickness,
bool smooth,
bool auto_flip);
/**
* Stroke to view space
* Transforms a stroke to view space.
* This allows for manipulations in 2D but also easy conversion back to 3D.
* \note also takes care of parent space transform.
*/
void BKE_gpencil_stroke_to_view_space(struct bGPDstroke *gps,
float viewmat[4][4],
const float diff_mat[4][4]);
/**
* Stroke from view space
* Transforms a stroke from view space back to world space.
* Inverse of #BKE_gpencil_stroke_to_view_space
* \note also takes care of parent space transform.
*/
void BKE_gpencil_stroke_from_view_space(struct bGPDstroke *gps,
float viewinv[4][4],
const float diff_mat[4][4]);
/**
* Get average pressure.
*/
float BKE_gpencil_stroke_average_pressure_get(struct bGPDstroke *gps);
/**
* Check if the thickness of the stroke is constant.
*/
bool BKE_gpencil_stroke_is_pressure_constant(struct bGPDstroke *gps);

View File

@@ -126,365 +126,6 @@ void BKE_gpencil_stroke_boundingbox_calc(bGPDstroke *gps)
/** \name Stroke Smooth Positions
* \{ */
bool BKE_gpencil_stroke_smooth_point(bGPDstroke *gps,
int point_index,
float influence,
int iterations,
const bool smooth_caps,
const bool keep_shape,
bGPDstroke *r_gps)
{
/* If nothing to do, return early */
if (gps->totpoints <= 2 || iterations <= 0) {
return false;
}
/* - Overview of the algorithm here and in the following smooth functions:
*
* The smooth functions return the new attribute in question for a single point.
* The result is stored in r_gps->points[point_index], while the data is read from gps.
* To get a correct result, duplicate the stroke point data and read from the copy,
* while writing to the real stroke. Not doing that will result in acceptable, but
* asymmetric results.
*
* This algorithm works as long as all points are being smoothed. If there is
* points that should not get smoothed, use the old repeat smooth pattern with
* the parameter "iterations" set to 1 or 2. (2 matches the old algorithm).
*/
const bGPDspoint *pt = &gps->points[point_index];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
/* If smooth_caps is false, the caps will not be translated by smoothing. */
if (!smooth_caps && !is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
copy_v3_v3(&r_gps->points[point_index].x, &pt->x);
return true;
}
/* This function uses a binomial kernel, which is the discrete version of gaussian blur.
* The weight for a vertex at the relative index point_index is
* `w = nCr(n, j + n/2) / 2^n = (n/1 * (n-1)/2 * ... * (n-j-n/2)/(j+n/2)) / 2^n`
* All weights together sum up to 1
* This is equivalent to doing multiple iterations of averaging neighbors,
* where n = iterations * 2 and -n/2 <= j <= n/2
*
* Now the problem is that `nCr(n, j + n/2)` is very hard to compute for `n > 500`, since even
* double precision isn't sufficient. A very good robust approximation for n > 20 is
* `nCr(n, j + n/2) / 2^n = sqrt(2/(pi*n)) * exp(-2*j*j/n)`
*
* There is one more problem left: The old smooth algorithm was doing a more aggressive
* smooth. To solve that problem, choose a different n/2, which does not match the range and
* normalize the weights on finish. This may cause some artifacts at low values.
*
* keep_shape is a new option to stop the stroke from severely deforming.
* It uses different partially negative weights.
* w = `2 * (nCr(n, j + n/2) / 2^n) - (nCr(3*n, j + n) / 2^(3*n))`
* ~ `2 * sqrt(2/(pi*n)) * exp(-2*j*j/n) - sqrt(2/(pi*3*n)) * exp(-2*j*j/(3*n))`
* All weights still sum up to 1.
* Note these weights only work because the averaging is done in relative coordinates.
*/
float sco[3] = {0.0f, 0.0f, 0.0f};
float tmp[3];
const int n_half = keep_shape ? (iterations * iterations) / 8 + iterations :
(iterations * iterations) / 4 + 2 * iterations + 12;
double w = keep_shape ? 2.0 : 1.0;
double w2 = keep_shape ?
(1.0 / M_SQRT3) * exp((2 * iterations * iterations) / double(n_half * 3)) :
0.0;
double total_w = 0.0;
for (int step = iterations; step > 0; step--) {
int before = point_index - step;
int after = point_index + step;
float w_before = float(w - w2);
float w_after = float(w - w2);
if (is_cyclic) {
before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
after = after % gps->totpoints;
}
else {
if (before < 0) {
if (!smooth_caps) {
w_before *= -before / float(point_index);
}
before = 0;
}
if (after > gps->totpoints - 1) {
if (!smooth_caps) {
w_after *= (after - (gps->totpoints - 1)) / float(gps->totpoints - 1 - point_index);
}
after = gps->totpoints - 1;
}
}
/* Add both these points in relative coordinates to the weighted average sum. */
sub_v3_v3v3(tmp, &gps->points[before].x, &pt->x);
madd_v3_v3fl(sco, tmp, w_before);
sub_v3_v3v3(tmp, &gps->points[after].x, &pt->x);
madd_v3_v3fl(sco, tmp, w_after);
total_w += w_before;
total_w += w_after;
w *= (n_half + step) / double(n_half + 1 - step);
w2 *= (n_half * 3 + step) / double(n_half * 3 + 1 - step);
}
total_w += w - w2;
/* The accumulated weight total_w should be
* `~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100`
* here, but sometimes not quite. */
mul_v3_fl(sco, float(1.0 / total_w));
/* Shift back to global coordinates. */
add_v3_v3(sco, &pt->x);
/* Based on influence factor, blend between original and optimal smoothed coordinate. */
interp_v3_v3v3(&r_gps->points[point_index].x, &pt->x, sco, influence);
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Smooth Strength
* \{ */
bool BKE_gpencil_stroke_smooth_strength(
bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
{
/* If nothing to do, return early */
if (gps->totpoints <= 2 || iterations <= 0) {
return false;
}
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
const bGPDspoint *pt = &gps->points[point_index];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
float strength = 0.0f;
const int n_half = (iterations * iterations) / 4 + iterations;
double w = 1.0;
double total_w = 0.0;
for (int step = iterations; step > 0; step--) {
int before = point_index - step;
int after = point_index + step;
float w_before = float(w);
float w_after = float(w);
if (is_cyclic) {
before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
after = after % gps->totpoints;
}
else {
CLAMP_MIN(before, 0);
CLAMP_MAX(after, gps->totpoints - 1);
}
/* Add both these points in relative coordinates to the weighted average sum. */
strength += w_before * (gps->points[before].strength - pt->strength);
strength += w_after * (gps->points[after].strength - pt->strength);
total_w += w_before;
total_w += w_after;
w *= (n_half + step) / double(n_half + 1 - step);
}
total_w += w;
/* The accumulated weight total_w should be
* ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
* here, but sometimes not quite. */
strength /= total_w;
/* Based on influence factor, blend between original and optimal smoothed value. */
r_gps->points[point_index].strength = pt->strength + strength * influence;
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Smooth Thickness
* \{ */
bool BKE_gpencil_stroke_smooth_thickness(
bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
{
/* If nothing to do, return early */
if (gps->totpoints <= 2 || iterations <= 0) {
return false;
}
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
const bGPDspoint *pt = &gps->points[point_index];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
float pressure = 0.0f;
const int n_half = (iterations * iterations) / 4 + iterations;
double w = 1.0;
double total_w = 0.0;
for (int step = iterations; step > 0; step--) {
int before = point_index - step;
int after = point_index + step;
float w_before = float(w);
float w_after = float(w);
if (is_cyclic) {
before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
after = after % gps->totpoints;
}
else {
CLAMP_MIN(before, 0);
CLAMP_MAX(after, gps->totpoints - 1);
}
/* Add both these points in relative coordinates to the weighted average sum. */
pressure += w_before * (gps->points[before].pressure - pt->pressure);
pressure += w_after * (gps->points[after].pressure - pt->pressure);
total_w += w_before;
total_w += w_after;
w *= (n_half + step) / double(n_half + 1 - step);
}
total_w += w;
/* The accumulated weight total_w should be
* ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
* here, but sometimes not quite. */
pressure /= total_w;
/* Based on influence factor, blend between original and optimal smoothed value. */
r_gps->points[point_index].pressure = pt->pressure + pressure * influence;
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Stroke Smooth UV
* \{ */
bool BKE_gpencil_stroke_smooth_uv(
bGPDstroke *gps, int point_index, float influence, int iterations, bGPDstroke *r_gps)
{
/* If nothing to do, return early */
if (gps->totpoints <= 2 || iterations <= 0) {
return false;
}
/* See BKE_gpencil_stroke_smooth_point for details on the algorithm. */
const bGPDspoint *pt = &gps->points[point_index];
const bool is_cyclic = (gps->flag & GP_STROKE_CYCLIC) != 0;
/* If don't change the caps. */
if (!is_cyclic && ELEM(point_index, 0, gps->totpoints - 1)) {
r_gps->points[point_index].uv_rot = pt->uv_rot;
r_gps->points[point_index].uv_fac = pt->uv_fac;
return true;
}
float uv_rot = 0.0f;
float uv_fac = 0.0f;
const int n_half = iterations * iterations + iterations;
double w = 1.0;
double total_w = 0.0;
for (int step = iterations; step > 0; step--) {
int before = point_index - step;
int after = point_index + step;
float w_before = float(w);
float w_after = float(w);
if (is_cyclic) {
before = (before % gps->totpoints + gps->totpoints) % gps->totpoints;
after = after % gps->totpoints;
}
else {
if (before < 0) {
w_before *= -before / float(point_index);
before = 0;
}
if (after > gps->totpoints - 1) {
w_after *= (after - (gps->totpoints - 1)) / float(gps->totpoints - 1 - point_index);
after = gps->totpoints - 1;
}
}
/* Add both these points in relative coordinates to the weighted average sum. */
uv_rot += w_before * (gps->points[before].uv_rot - pt->uv_rot);
uv_rot += w_after * (gps->points[after].uv_rot - pt->uv_rot);
uv_fac += w_before * (gps->points[before].uv_fac - pt->uv_fac);
uv_fac += w_after * (gps->points[after].uv_fac - pt->uv_fac);
total_w += w_before;
total_w += w_after;
w *= (n_half + step) / double(n_half + 1 - step);
}
total_w += w;
/* The accumulated weight total_w should be
* ~sqrt(M_PI * n_half) * exp((iterations * iterations) / n_half) < 100
* here, but sometimes not quite. */
uv_rot /= total_w;
uv_fac /= total_w;
/* Based on influence factor, blend between original and optimal smoothed value. */
r_gps->points[point_index].uv_rot = pt->uv_rot + uv_rot * influence;
r_gps->points[point_index].uv_fac = pt->uv_fac + uv_fac * influence;
return true;
}
void BKE_gpencil_stroke_smooth(bGPDstroke *gps,
const float influence,
const int iterations,
const bool smooth_position,
const bool smooth_strength,
const bool smooth_thickness,
const bool smooth_uv,
const bool keep_shape,
const float *weights)
{
if (influence <= 0 || iterations <= 0) {
return;
}
/* Make a copy of the point data to avoid directionality of the smooth operation. */
bGPDstroke gps_old = blender::dna::shallow_copy(*gps);
gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
/* Smooth stroke. */
for (int i = 0; i < gps->totpoints; i++) {
float val = influence;
if (weights != nullptr) {
val *= weights[i];
if (val <= 0.0f) {
continue;
}
}
/* TODO: Currently the weights only control the influence, but is would be much better if they
* would control the distribution used in smooth, similar to how the ends are handled. */
/* Perform smoothing. */
if (smooth_position) {
BKE_gpencil_stroke_smooth_point(&gps_old, i, val, iterations, false, keep_shape, gps);
}
if (smooth_strength) {
BKE_gpencil_stroke_smooth_strength(&gps_old, i, val, iterations, gps);
}
if (smooth_thickness) {
BKE_gpencil_stroke_smooth_thickness(&gps_old, i, val, iterations, gps);
}
if (smooth_uv) {
BKE_gpencil_stroke_smooth_uv(&gps_old, i, val, iterations, gps);
}
}
/* Free the copied points array. */
MEM_freeN(gps_old.points);
}
void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
int totpoints,
float (*points2d)[2],
@@ -559,90 +200,6 @@ void BKE_gpencil_stroke_2d_flat(const bGPDspoint *points,
*r_direction = (cross >= 0.0f) ? 1 : -1;
}
void BKE_gpencil_stroke_2d_flat_ref(const bGPDspoint *ref_points,
int ref_totpoints,
const bGPDspoint *points,
int totpoints,
float (*points2d)[2],
const float scale,
int *r_direction)
{
BLI_assert(totpoints >= 2);
const bGPDspoint *pt0 = &ref_points[0];
const bGPDspoint *pt1 = &ref_points[1];
const bGPDspoint *pt3 = &ref_points[int(ref_totpoints * 0.75)];
float locx[3];
float locy[3];
float loc3[3];
float normal[3];
/* local X axis (p0 -> p1) */
sub_v3_v3v3(locx, &pt1->x, &pt0->x);
/* point vector at 3/4 */
float v3[3];
if (totpoints == 2) {
mul_v3_v3fl(v3, &pt3->x, 0.001f);
}
else {
copy_v3_v3(v3, &pt3->x);
}
sub_v3_v3v3(loc3, v3, &pt0->x);
/* vector orthogonal to polygon plane */
cross_v3_v3v3(normal, locx, loc3);
/* local Y axis (cross to normal/x axis) */
cross_v3_v3v3(locy, normal, locx);
/* Normalize vectors */
normalize_v3(locx);
normalize_v3(locy);
/* Get all points in local space */
for (int i = 0; i < totpoints; i++) {
const bGPDspoint *pt = &points[i];
float loc[3];
float v1[3];
float vn[3] = {0.0f, 0.0f, 0.0f};
/* apply scale to extremes of the stroke to get better collision detection
* the scale is divided to get more control in the UI parameter
*/
/* first point */
if (i == 0) {
const bGPDspoint *pt_next = &points[i + 1];
sub_v3_v3v3(vn, &pt->x, &pt_next->x);
normalize_v3(vn);
mul_v3_fl(vn, scale / 10.0f);
add_v3_v3v3(v1, &pt->x, vn);
}
/* last point */
else if (i == totpoints - 1) {
const bGPDspoint *pt_prev = &points[i - 1];
sub_v3_v3v3(vn, &pt->x, &pt_prev->x);
normalize_v3(vn);
mul_v3_fl(vn, scale / 10.0f);
add_v3_v3v3(v1, &pt->x, vn);
}
else {
copy_v3_v3(v1, &pt->x);
}
/* Get local space using first point as origin (ref stroke) */
sub_v3_v3v3(loc, v1, &pt0->x);
points2d[i][0] = dot_v3v3(loc, locx);
points2d[i][1] = dot_v3v3(loc, locy);
}
/* Concave (-1), Convex (1), or Auto-detect (0)? */
*r_direction = int(locy[2]);
}
/* Calc texture coordinates using flat projected points. */
static void gpencil_calc_stroke_fill_uv(const float (*points2d)[2],
bGPDstroke *gps,
@@ -783,272 +340,6 @@ void BKE_gpencil_stroke_geometry_update(bGPdata * /*gpd*/, bGPDstroke *gps)
BKE_gpencil_stroke_boundingbox_calc(gps);
}
float BKE_gpencil_stroke_length(const bGPDstroke *gps, bool use_3d)
{
if (!gps->points || gps->totpoints < 2) {
return 0.0f;
}
float *last_pt = &gps->points[0].x;
float total_length = 0.0f;
for (int i = 1; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
if (use_3d) {
total_length += len_v3v3(&pt->x, last_pt);
}
else {
total_length += len_v2v2(&pt->x, last_pt);
}
last_pt = &pt->x;
}
return total_length;
}
float BKE_gpencil_stroke_segment_length(const bGPDstroke *gps,
const int start_index,
const int end_index,
bool use_3d)
{
if (!gps->points || gps->totpoints < 2 || end_index <= start_index) {
return 0.0f;
}
int index = std::max(start_index, 0) + 1;
int last_index = std::min(end_index, gps->totpoints - 1) + 1;
float *last_pt = &gps->points[index - 1].x;
float total_length = 0.0f;
for (int i = index; i < last_index; i++) {
bGPDspoint *pt = &gps->points[i];
if (use_3d) {
total_length += len_v3v3(&pt->x, last_pt);
}
else {
total_length += len_v2v2(&pt->x, last_pt);
}
last_pt = &pt->x;
}
return total_length;
}
bool BKE_gpencil_stroke_trim(bGPdata *gpd, bGPDstroke *gps)
{
if (gps->totpoints < 4) {
return false;
}
bool intersect = false;
int start = 0;
int end = 0;
float point[3];
/* loop segments from start until we have an intersection */
for (int i = 0; i < gps->totpoints - 2; i++) {
start = i;
bGPDspoint *a = &gps->points[start];
bGPDspoint *b = &gps->points[start + 1];
for (int j = start + 2; j < gps->totpoints - 1; j++) {
end = j + 1;
bGPDspoint *c = &gps->points[j];
bGPDspoint *d = &gps->points[end];
float pointb[3];
/* get intersection */
if (isect_line_line_v3(&a->x, &b->x, &c->x, &d->x, point, pointb)) {
if (len_v3(point) > 0.0f) {
float closest[3];
/* check intersection is on both lines */
float lambda = closest_to_line_v3(closest, point, &a->x, &b->x);
if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
continue;
}
lambda = closest_to_line_v3(closest, point, &c->x, &d->x);
if ((lambda <= 0.0f) || (lambda >= 1.0f)) {
continue;
}
intersect = true;
break;
}
}
}
if (intersect) {
break;
}
}
/* trim unwanted points */
if (intersect) {
/* save points */
bGPDspoint *old_points = (bGPDspoint *)MEM_dupallocN(gps->points);
MDeformVert *old_dvert = nullptr;
MDeformVert *dvert_src = nullptr;
if (gps->dvert != nullptr) {
old_dvert = (MDeformVert *)MEM_dupallocN(gps->dvert);
}
/* resize gps */
int newtot = end - start + 1;
gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * newtot);
if (gps->dvert != nullptr) {
gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * newtot);
}
for (int i = 0; i < newtot; i++) {
int idx = start + i;
bGPDspoint *pt_src = &old_points[idx];
bGPDspoint *pt_new = &gps->points[i];
*pt_new = blender::dna::shallow_copy(*pt_src);
if (gps->dvert != nullptr) {
dvert_src = &old_dvert[idx];
MDeformVert *dvert = &gps->dvert[i];
memcpy(dvert, dvert_src, sizeof(MDeformVert));
if (dvert_src->dw) {
memcpy(dvert->dw, dvert_src->dw, sizeof(MDeformWeight));
}
}
if (ELEM(idx, start, end)) {
copy_v3_v3(&pt_new->x, point);
}
}
gps->totpoints = newtot;
MEM_SAFE_FREE(old_points);
MEM_SAFE_FREE(old_dvert);
}
BKE_gpencil_stroke_geometry_update(gpd, gps);
return intersect;
}
bool BKE_gpencil_stroke_close(bGPDstroke *gps)
{
bGPDspoint *pt1 = nullptr;
bGPDspoint *pt2 = nullptr;
/* Only can close a stroke with 3 points or more. */
if (gps->totpoints < 3) {
return false;
}
/* Calc average distance between points to get same level of sampling. */
float dist_tot = 0.0f;
for (int i = 0; i < gps->totpoints - 1; i++) {
pt1 = &gps->points[i];
pt2 = &gps->points[i + 1];
dist_tot += len_v3v3(&pt1->x, &pt2->x);
}
/* Calc the average distance. */
float dist_avg = dist_tot / (gps->totpoints - 1);
/* Calc distance between last and first point. */
pt1 = &gps->points[gps->totpoints - 1];
pt2 = &gps->points[0];
float dist_close = len_v3v3(&pt1->x, &pt2->x);
/* if the distance to close is very small, don't need add points and just enable cyclic. */
if (dist_close <= dist_avg) {
gps->flag |= GP_STROKE_CYCLIC;
return true;
}
/* Calc number of points required using the average distance. */
int tot_newpoints = std::max<int>(dist_close / dist_avg, 1);
/* Resize stroke array. */
int old_tot = gps->totpoints;
gps->totpoints += tot_newpoints;
gps->points = (bGPDspoint *)MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
if (gps->dvert != nullptr) {
gps->dvert = (MDeformVert *)MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
}
/* Generate new points */
pt1 = &gps->points[old_tot - 1];
pt2 = &gps->points[0];
bGPDspoint *pt = &gps->points[old_tot];
for (int i = 1; i < tot_newpoints + 1; i++, pt++) {
float step = (tot_newpoints > 1) ? (float(i) / float(tot_newpoints)) : 0.99f;
/* Clamp last point to be near, but not on top of first point. */
if ((tot_newpoints > 1) && (i == tot_newpoints)) {
step *= 0.99f;
}
/* Average point. */
interp_v3_v3v3(&pt->x, &pt1->x, &pt2->x, step);
pt->pressure = interpf(pt2->pressure, pt1->pressure, step);
pt->strength = interpf(pt2->strength, pt1->strength, step);
pt->flag = 0;
interp_v4_v4v4(pt->vert_color, pt1->vert_color, pt2->vert_color, step);
/* Set point as selected. */
if (gps->flag & GP_STROKE_SELECT) {
pt->flag |= GP_SPOINT_SELECT;
}
/* Set weights. */
if (gps->dvert != nullptr) {
MDeformVert *dvert1 = &gps->dvert[old_tot - 1];
MDeformWeight *dw1 = BKE_defvert_ensure_index(dvert1, 0);
float weight_1 = dw1 ? dw1->weight : 0.0f;
MDeformVert *dvert2 = &gps->dvert[0];
MDeformWeight *dw2 = BKE_defvert_ensure_index(dvert2, 0);
float weight_2 = dw2 ? dw2->weight : 0.0f;
MDeformVert *dvert_final = &gps->dvert[old_tot + i - 1];
dvert_final->totweight = 0;
MDeformWeight *dw = BKE_defvert_ensure_index(dvert_final, 0);
if (dvert_final->dw) {
dw->weight = interpf(weight_2, weight_1, step);
}
}
}
/* Enable cyclic flag. */
gps->flag |= GP_STROKE_CYCLIC;
return true;
}
/** \} */
/* -------------------------------------------------------------------- */
/** \name Normal Calculation
* \{ */
void BKE_gpencil_stroke_normal(const bGPDstroke *gps, float r_normal[3])
{
if (gps->totpoints < 3) {
zero_v3(r_normal);
return;
}
bGPDspoint *points = gps->points;
int totpoints = gps->totpoints;
const bGPDspoint *pt0 = &points[0];
const bGPDspoint *pt1 = &points[1];
const bGPDspoint *pt3 = &points[int(totpoints * 0.75)];
float vec1[3];
float vec2[3];
/* initial vector (p0 -> p1) */
sub_v3_v3v3(vec1, &pt1->x, &pt0->x);
/* point vector at 3/4 */
sub_v3_v3v3(vec2, &pt3->x, &pt0->x);
/* vector orthogonal to polygon plane */
cross_v3_v3v3(r_normal, vec1, vec2);
/* Normalize vector */
normalize_v3(r_normal);
}
/** \} */
void BKE_gpencil_transform(bGPdata *gpd, const float mat[4][4])
{
if (gpd == nullptr) {
@@ -1207,32 +498,6 @@ void BKE_gpencil_point_coords_apply_with_mat4(bGPdata *gpd,
}
}
void BKE_gpencil_stroke_set_random_color(bGPDstroke *gps)
{
BLI_assert(gps->totpoints > 0);
float color[4] = {1.0f, 1.0f, 1.0f, 1.0f};
bGPDspoint *pt = &gps->points[0];
color[0] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints / 5, pt->x + pt->z));
color[1] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints + pt->x, pt->y * pt->z + pt->x));
color[2] *= BLI_hash_int_01(BLI_hash_int_2d(gps->totpoints - pt->x, pt->z * pt->x + pt->y));
for (int i = 0; i < gps->totpoints; i++) {
pt = &gps->points[i];
copy_v4_v4(pt->vert_color, color);
}
}
void BKE_gpencil_stroke_flip(bGPDstroke *gps)
{
/* Reverse points. */
BLI_array_reverse(gps->points, gps->totpoints);
/* Reverse vertex groups if available. */
if (gps->dvert) {
BLI_array_reverse(gps->dvert, gps->totpoints);
}
}
/* Temp data for storing information about an "island" of points
* that should be kept when splitting up a stroke. Used in:
* gpencil_stroke_delete_tagged_points()
@@ -1498,250 +763,3 @@ bGPDstroke *BKE_gpencil_stroke_delete_tagged_points(bGPdata *gpd,
return new_stroke;
}
/* Helper: copy point between strokes */
static void gpencil_stroke_copy_point(bGPDstroke *gps,
MDeformVert *dvert,
bGPDspoint *point,
const float delta[3],
float pressure,
float strength,
float deltatime)
{
bGPDspoint *newpoint;
gps->points = (bGPDspoint *)MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
if (gps->dvert != nullptr) {
gps->dvert = (MDeformVert *)MEM_reallocN(gps->dvert,
sizeof(MDeformVert) * (gps->totpoints + 1));
}
else {
/* If destination has weight add weight to origin. */
if (dvert != nullptr) {
gps->dvert = (MDeformVert *)MEM_callocN(sizeof(MDeformVert) * (gps->totpoints + 1),
__func__);
}
}
gps->totpoints++;
newpoint = &gps->points[gps->totpoints - 1];
newpoint->x = point->x * delta[0];
newpoint->y = point->y * delta[1];
newpoint->z = point->z * delta[2];
newpoint->flag = point->flag;
newpoint->pressure = pressure;
newpoint->strength = strength;
newpoint->time = point->time + deltatime;
copy_v4_v4(newpoint->vert_color, point->vert_color);
if (gps->dvert != nullptr) {
MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1];
if (dvert != nullptr) {
newdvert->totweight = dvert->totweight;
newdvert->dw = (MDeformWeight *)MEM_dupallocN(dvert->dw);
}
else {
newdvert->totweight = 0;
newdvert->dw = nullptr;
}
}
}
void BKE_gpencil_stroke_join(bGPDstroke *gps_a,
bGPDstroke *gps_b,
const bool leave_gaps,
const bool fit_thickness,
const bool smooth,
bool auto_flip)
{
bGPDspoint point;
bGPDspoint *pt;
int i;
const float delta[3] = {1.0f, 1.0f, 1.0f};
float deltatime = 0.0f;
/* sanity checks */
if (ELEM(nullptr, gps_a, gps_b)) {
return;
}
if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0)) {
return;
}
if (auto_flip) {
/* define start and end points of each stroke */
float start_a[3], start_b[3], end_a[3], end_b[3];
pt = &gps_a->points[0];
copy_v3_v3(start_a, &pt->x);
pt = &gps_a->points[gps_a->totpoints - 1];
copy_v3_v3(end_a, &pt->x);
pt = &gps_b->points[0];
copy_v3_v3(start_b, &pt->x);
pt = &gps_b->points[gps_b->totpoints - 1];
copy_v3_v3(end_b, &pt->x);
/* Check if need flip strokes. */
float dist = len_squared_v3v3(end_a, start_b);
bool flip_a = false;
bool flip_b = false;
float lowest = dist;
dist = len_squared_v3v3(end_a, end_b);
if (dist < lowest) {
lowest = dist;
flip_a = false;
flip_b = true;
}
dist = len_squared_v3v3(start_a, start_b);
if (dist < lowest) {
lowest = dist;
flip_a = true;
flip_b = false;
}
dist = len_squared_v3v3(start_a, end_b);
if (dist < lowest) {
lowest = dist;
flip_a = true;
flip_b = true;
}
if (flip_a) {
BKE_gpencil_stroke_flip(gps_a);
}
if (flip_b) {
BKE_gpencil_stroke_flip(gps_b);
}
}
/* don't visibly link the first and last points? */
if (leave_gaps) {
/* 1st: add one tail point to start invisible area */
point = blender::dna::shallow_copy(gps_a->points[gps_a->totpoints - 1]);
deltatime = point.time;
gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, 0.0f);
/* 2nd: add one head point to finish invisible area */
point = blender::dna::shallow_copy(gps_b->points[0]);
gpencil_stroke_copy_point(gps_a, nullptr, &point, delta, 0.0f, 0.0f, deltatime);
}
/* Ratio to apply in the points to keep the same thickness in the joined stroke using the
* destination stroke thickness. */
const float ratio = (fit_thickness && gps_a->thickness > 0.0f) ?
float(gps_b->thickness) / float(gps_a->thickness) :
1.0f;
/* 3rd: add all points */
const int totpoints_a = gps_a->totpoints;
for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
MDeformVert *dvert = (gps_b->dvert) ? &gps_b->dvert[i] : nullptr;
gpencil_stroke_copy_point(
gps_a, dvert, pt, delta, pt->pressure * ratio, pt->strength, deltatime);
}
/* Smooth the join to avoid hard thickness changes. */
if (smooth) {
const int sample_points = 8;
/* Get the segment to smooth using n points on each side of the join. */
int start = std::max(0, totpoints_a - sample_points);
int end = std::min(gps_a->totpoints - 1, start + (sample_points * 2));
const int len = (end - start);
float step = 1.0f / ((len / 2) + 1);
/* Calc the average pressure. */
float avg_pressure = 0.0f;
for (i = start; i < end; i++) {
pt = &gps_a->points[i];
avg_pressure += pt->pressure;
}
avg_pressure = avg_pressure / len;
/* Smooth segment thickness and position. */
float ratio = step;
for (i = start; i < end; i++) {
pt = &gps_a->points[i];
pt->pressure += (avg_pressure - pt->pressure) * ratio;
BKE_gpencil_stroke_smooth_point(gps_a, i, ratio * 0.6f, 2, false, true, gps_a);
ratio += step;
/* In the center, reverse the ratio. */
if (ratio > 1.0f) {
ratio = ratio - step - step;
step *= -1.0f;
}
}
}
}
/** \} */
void BKE_gpencil_stroke_to_view_space(bGPDstroke *gps,
float viewmat[4][4],
const float diff_mat[4][4])
{
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
/* Point to parent space. */
mul_v3_m4v3(&pt->x, diff_mat, &pt->x);
/* point to view space */
mul_m4_v3(viewmat, &pt->x);
}
}
void BKE_gpencil_stroke_from_view_space(bGPDstroke *gps,
float viewinv[4][4],
const float diff_mat[4][4])
{
float inverse_diff_mat[4][4];
invert_m4_m4(inverse_diff_mat, diff_mat);
for (int i = 0; i < gps->totpoints; i++) {
bGPDspoint *pt = &gps->points[i];
mul_v3_m4v3(&pt->x, viewinv, &pt->x);
mul_m4_v3(inverse_diff_mat, &pt->x);
}
}
/** \} */
float BKE_gpencil_stroke_average_pressure_get(bGPDstroke *gps)
{
if (gps->totpoints == 1) {
return gps->points[0].pressure;
}
float tot = 0.0f;
for (int i = 0; i < gps->totpoints; i++) {
const bGPDspoint *pt = &gps->points[i];
tot += pt->pressure;
}
return tot / float(gps->totpoints);
}
bool BKE_gpencil_stroke_is_pressure_constant(bGPDstroke *gps)
{
if (gps->totpoints == 1) {
return true;
}
const float first_pressure = gps->points[0].pressure;
for (int i = 0; i < gps->totpoints; i++) {
const bGPDspoint *pt = &gps->points[i];
if (pt->pressure != first_pressure) {
return false;
}
}
return true;
}
/** \} */