774 lines
26 KiB
C
774 lines
26 KiB
C
/* SPDX-License-Identifier: GPL-2.0-or-later
|
|
* Copyright 2001-2002 NaN Holding BV. All rights reserved. */
|
|
|
|
/** \file
|
|
* \ingroup edtransform
|
|
*/
|
|
|
|
#include "DNA_gpencil_legacy_types.h"
|
|
|
|
#include "MEM_guardedalloc.h"
|
|
|
|
#include "BLI_ghash.h"
|
|
#include "BLI_listbase.h"
|
|
#include "BLI_math.h"
|
|
|
|
#include "BKE_colortools.h"
|
|
#include "BKE_context.h"
|
|
#include "BKE_curve.h"
|
|
#include "BKE_gpencil_curve_legacy.h"
|
|
#include "BKE_gpencil_geom_legacy.h"
|
|
#include "BKE_gpencil_legacy.h"
|
|
#include "BKE_layer.h"
|
|
|
|
#include "ED_gpencil_legacy.h"
|
|
#include "ED_keyframing.h"
|
|
|
|
#include "transform.h"
|
|
#include "transform_convert.h"
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Gpencil Transform Creation
|
|
* \{ */
|
|
|
|
static void createTransGPencil_center_get(bGPDstroke *gps, float r_center[3])
|
|
{
|
|
bGPDspoint *pt;
|
|
int i;
|
|
|
|
zero_v3(r_center);
|
|
int tot_sel = 0;
|
|
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
|
if (pt->flag & GP_SPOINT_SELECT) {
|
|
add_v3_v3(r_center, &pt->x);
|
|
tot_sel++;
|
|
}
|
|
}
|
|
|
|
if (tot_sel > 0) {
|
|
mul_v3_fl(r_center, 1.0f / tot_sel);
|
|
}
|
|
}
|
|
|
|
static short get_bezt_sel_triple_flag(BezTriple *bezt, const bool handles_visible)
|
|
{
|
|
#define SEL_F1 (1 << 0)
|
|
#define SEL_F2 (1 << 1)
|
|
#define SEL_F3 (1 << 2)
|
|
#define SEL_ALL ((1 << 0) | (1 << 1) | (1 << 2))
|
|
|
|
short flag = 0;
|
|
|
|
if (handles_visible) {
|
|
flag = ((bezt->f1 & SELECT) ? SEL_F1 : 0) | ((bezt->f2 & SELECT) ? SEL_F2 : 0) |
|
|
((bezt->f3 & SELECT) ? SEL_F3 : 0);
|
|
}
|
|
else if (bezt->f2 & SELECT) {
|
|
flag = SEL_ALL;
|
|
}
|
|
|
|
/* Special case for auto & aligned handles */
|
|
if ((flag != SEL_ALL) && (flag & SEL_F2)) {
|
|
if (ELEM(bezt->h1, HD_AUTO, HD_ALIGN) && ELEM(bezt->h2, HD_AUTO, HD_ALIGN)) {
|
|
flag = SEL_ALL;
|
|
}
|
|
}
|
|
|
|
#undef SEL_F1
|
|
#undef SEL_F2
|
|
#undef SEL_F3
|
|
return flag;
|
|
}
|
|
|
|
static void createTransGPencil_curves(bContext *C,
|
|
TransInfo *t,
|
|
Depsgraph *depsgraph,
|
|
ToolSettings *ts,
|
|
Object *obact,
|
|
bGPdata *gpd,
|
|
const int cfra_scene,
|
|
const bool is_multiedit,
|
|
const bool use_multiframe_falloff,
|
|
const bool is_prop_edit,
|
|
const bool is_prop_edit_connected,
|
|
const bool is_scale_thickness)
|
|
{
|
|
#define SEL_F1 (1 << 0)
|
|
#define SEL_F2 (1 << 1)
|
|
#define SEL_F3 (1 << 2)
|
|
|
|
View3D *v3d = t->view;
|
|
Scene *scene = CTX_data_scene(C);
|
|
const bool handle_only_selected_visible = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
|
|
const bool handle_all_visible = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
|
|
|
|
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
|
tc->data_len = 0;
|
|
|
|
/* Number of selected curve points */
|
|
uint32_t tot_curve_points = 0, tot_sel_curve_points = 0, tot_points = 0, tot_sel_points = 0;
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
|
/* Only editable and visible layers are considered. */
|
|
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
|
|
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
|
|
for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
|
|
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
|
|
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
|
|
/* skip strokes that are invalid for current view */
|
|
if (ED_gpencil_stroke_can_use(C, gps) == false) {
|
|
continue;
|
|
}
|
|
/* Check if the color is editable. */
|
|
if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
|
|
continue;
|
|
}
|
|
/* Check if stroke has an editcurve */
|
|
if (gps->editcurve == NULL) {
|
|
continue;
|
|
}
|
|
|
|
bGPDcurve *gpc = gps->editcurve;
|
|
for (int i = 0; i < gpc->tot_curve_points; i++) {
|
|
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
|
|
BezTriple *bezt = &gpc_pt->bezt;
|
|
if (bezt->hide) {
|
|
continue;
|
|
}
|
|
|
|
const bool handles_visible = (handle_all_visible ||
|
|
(handle_only_selected_visible &&
|
|
(gpc_pt->flag & GP_CURVE_POINT_SELECT)));
|
|
|
|
const short sel_flag = get_bezt_sel_triple_flag(bezt, handles_visible);
|
|
if (sel_flag & (SEL_F1 | SEL_F2 | SEL_F3)) {
|
|
if (sel_flag & SEL_F1) {
|
|
tot_sel_points++;
|
|
}
|
|
if (sel_flag & SEL_F2) {
|
|
tot_sel_points++;
|
|
}
|
|
if (sel_flag & SEL_F3) {
|
|
tot_sel_points++;
|
|
}
|
|
tot_sel_curve_points++;
|
|
}
|
|
|
|
if (is_prop_edit) {
|
|
tot_points += 3;
|
|
tot_curve_points++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If not multi-edit out of loop. */
|
|
if (!is_multiedit) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if (((is_prop_edit && !is_prop_edit_connected) ? tot_curve_points : tot_sel_points) == 0) {
|
|
tc->data_len = 0;
|
|
return;
|
|
}
|
|
|
|
int data_len_pt = 0;
|
|
|
|
if (is_prop_edit) {
|
|
tc->data_len = tot_points;
|
|
data_len_pt = tot_curve_points;
|
|
}
|
|
else {
|
|
tc->data_len = tot_sel_points;
|
|
data_len_pt = tot_sel_curve_points;
|
|
}
|
|
|
|
if (tc->data_len == 0) {
|
|
return;
|
|
}
|
|
|
|
transform_around_single_fallback_ex(t, data_len_pt);
|
|
|
|
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), __func__);
|
|
TransData *td = tc->data;
|
|
|
|
const bool use_around_origins_for_handles_test = ((t->around == V3D_AROUND_LOCAL_ORIGINS) &&
|
|
transform_mode_use_local_origins(t));
|
|
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
|
/* Only editable and visible layers are considered. */
|
|
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
|
|
const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
|
|
bGPDframe *gpf = gpl->actframe;
|
|
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
|
|
float diff_mat[4][4], mtx[3][3];
|
|
float smtx[3][3];
|
|
|
|
/* Init multiframe falloff options. */
|
|
int f_init = 0;
|
|
int f_end = 0;
|
|
|
|
if (use_multiframe_falloff) {
|
|
BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
|
|
}
|
|
|
|
if ((gpf->framenum != cfra) && (!is_multiedit)) {
|
|
if (IS_AUTOKEY_ON(scene)) {
|
|
gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
|
|
}
|
|
/* In some weird situations (frame-lock enabled) return NULL. */
|
|
if (gpf == NULL) {
|
|
continue;
|
|
}
|
|
if (!is_multiedit) {
|
|
init_gpf = gpf;
|
|
}
|
|
}
|
|
|
|
/* Calculate difference matrix. */
|
|
BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
|
|
copy_m3_m4(mtx, diff_mat);
|
|
pseudoinverse_m3_m3(smtx, mtx, PSEUDOINVERSE_EPSILON);
|
|
|
|
for (gpf = init_gpf; gpf; gpf = gpf->next) {
|
|
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
|
|
/* If multi-frame and falloff, recalculate and save value. */
|
|
float falloff = 1.0f; /* by default no falloff */
|
|
if ((is_multiedit) && (use_multiframe_falloff)) {
|
|
/* Falloff depends on distance to active frame
|
|
* (relative to the overall frame range). */
|
|
falloff = BKE_gpencil_multiframe_falloff_calc(
|
|
gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
|
|
}
|
|
|
|
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
|
|
/* skip strokes that are invalid for current view */
|
|
if (ED_gpencil_stroke_can_use(C, gps) == false) {
|
|
continue;
|
|
}
|
|
/* Check if the color is editable. */
|
|
if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
|
|
continue;
|
|
}
|
|
/* Check if stroke has an editcurve */
|
|
if (gps->editcurve == NULL) {
|
|
continue;
|
|
}
|
|
TransData *head, *tail;
|
|
head = tail = td;
|
|
|
|
gps->runtime.multi_frame_falloff = falloff;
|
|
bool need_handle_recalc = false;
|
|
|
|
bGPDcurve *gpc = gps->editcurve;
|
|
const bool is_cyclic = gps->flag & GP_STROKE_CYCLIC;
|
|
for (int i = 0; i < gpc->tot_curve_points; i++) {
|
|
bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
|
|
BezTriple *bezt = &gpc_pt->bezt;
|
|
if (bezt->hide) {
|
|
continue;
|
|
}
|
|
|
|
TransDataCurveHandleFlags *hdata = NULL;
|
|
bool bezt_use = false;
|
|
const bool handles_visible = (handle_all_visible ||
|
|
(handle_only_selected_visible &&
|
|
(gpc_pt->flag & GP_CURVE_POINT_SELECT)));
|
|
const short sel_flag = get_bezt_sel_triple_flag(bezt, handles_visible);
|
|
/* Iterate over bezier triple */
|
|
for (int j = 0; j < 3; j++) {
|
|
bool is_ctrl_point = (j == 1);
|
|
bool sel = sel_flag & (1 << j);
|
|
|
|
if (is_prop_edit || sel) {
|
|
copy_v3_v3(td->iloc, bezt->vec[j]);
|
|
td->loc = bezt->vec[j];
|
|
bool rotate_around_ctrl = !handles_visible ||
|
|
(t->around == V3D_AROUND_LOCAL_ORIGINS) ||
|
|
(bezt->f2 & SELECT);
|
|
copy_v3_v3(td->center, bezt->vec[rotate_around_ctrl ? 1 : j]);
|
|
|
|
if (!handles_visible || is_ctrl_point) {
|
|
if (bezt->f2 & SELECT) {
|
|
td->flag = TD_SELECTED;
|
|
}
|
|
else {
|
|
td->flag = 0;
|
|
}
|
|
}
|
|
else if (handles_visible) {
|
|
if (sel) {
|
|
td->flag = TD_SELECTED;
|
|
}
|
|
else {
|
|
td->flag = 0;
|
|
}
|
|
}
|
|
|
|
td->ext = NULL;
|
|
if (is_ctrl_point) {
|
|
if (t->mode != TFM_MIRROR) {
|
|
if (t->mode != TFM_GPENCIL_OPACITY) {
|
|
if (is_scale_thickness) {
|
|
td->val = &(gpc_pt->pressure);
|
|
td->ival = gpc_pt->pressure;
|
|
}
|
|
}
|
|
else {
|
|
td->val = &(gpc_pt->strength);
|
|
td->ival = gpc_pt->strength;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
td->val = NULL;
|
|
}
|
|
|
|
if (hdata == NULL) {
|
|
if (is_ctrl_point && ((sel_flag & SEL_F1 & SEL_F3) == 0)) {
|
|
hdata = initTransDataCurveHandles(td, bezt);
|
|
}
|
|
else if (!is_ctrl_point) {
|
|
hdata = initTransDataCurveHandles(td, bezt);
|
|
}
|
|
}
|
|
|
|
td->extra = gps;
|
|
td->ob = obact;
|
|
|
|
copy_m3_m3(td->smtx, smtx);
|
|
copy_m3_m3(td->mtx, mtx);
|
|
copy_m3_m3(td->axismtx, mtx);
|
|
|
|
td++;
|
|
tail++;
|
|
}
|
|
|
|
bezt_use |= sel;
|
|
}
|
|
|
|
/* Update the handle types so transformation is possible */
|
|
if (bezt_use && !ELEM(t->mode, TFM_GPENCIL_OPACITY, TFM_GPENCIL_SHRINKFATTEN)) {
|
|
BKE_nurb_bezt_handle_test(
|
|
bezt, SELECT, handles_visible, use_around_origins_for_handles_test);
|
|
need_handle_recalc = true;
|
|
}
|
|
}
|
|
|
|
if (is_prop_edit && (head != tail)) {
|
|
calc_distanceCurveVerts(head, tail - 1, is_cyclic);
|
|
}
|
|
|
|
if (need_handle_recalc) {
|
|
BKE_gpencil_editcurve_recalculate_handles(gps);
|
|
}
|
|
}
|
|
}
|
|
|
|
/* If not multi-edit out of loop. */
|
|
if (!is_multiedit) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
#undef SEL_F1
|
|
#undef SEL_F2
|
|
#undef SEL_F3
|
|
}
|
|
|
|
static void createTransGPencil_strokes(bContext *C,
|
|
TransInfo *t,
|
|
Depsgraph *depsgraph,
|
|
ToolSettings *ts,
|
|
Object *obact,
|
|
bGPdata *gpd,
|
|
const int cfra_scene,
|
|
const bool is_multiedit,
|
|
const bool use_multiframe_falloff,
|
|
const bool is_prop_edit,
|
|
const bool is_prop_edit_connected,
|
|
const bool is_scale_thickness)
|
|
{
|
|
Scene *scene = CTX_data_scene(C);
|
|
TransData *td = NULL;
|
|
float mtx[3][3], smtx[3][3];
|
|
|
|
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
|
/* == Grease Pencil Strokes to Transform Data ==
|
|
* Grease Pencil stroke points can be a mixture of 2D (screen-space),
|
|
* or 3D coordinates. However, they're always saved as 3D points.
|
|
* For now, we just do these without creating TransData2D for the 2D
|
|
* strokes. This may cause issues in future though.
|
|
*/
|
|
tc->data_len = 0;
|
|
|
|
/* First Pass: Count the number of data-points required for the strokes,
|
|
* (and additional info about the configuration - e.g. 2D/3D?).
|
|
*/
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
|
/* Only editable and visible layers are considered. */
|
|
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
|
|
bGPDframe *gpf;
|
|
bGPDstroke *gps;
|
|
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
|
|
|
|
for (gpf = init_gpf; gpf; gpf = gpf->next) {
|
|
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
|
|
for (gps = gpf->strokes.first; gps; gps = gps->next) {
|
|
/* skip strokes that are invalid for current view */
|
|
if (ED_gpencil_stroke_can_use(C, gps) == false) {
|
|
continue;
|
|
}
|
|
/* Check if the color is editable. */
|
|
if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
|
|
continue;
|
|
}
|
|
|
|
if (is_prop_edit) {
|
|
/* Proportional Editing... */
|
|
if (is_prop_edit_connected) {
|
|
/* Connected only - so only if selected. */
|
|
if (gps->flag & GP_STROKE_SELECT) {
|
|
tc->data_len += gps->totpoints;
|
|
}
|
|
}
|
|
else {
|
|
/* Everything goes - connection status doesn't matter. */
|
|
tc->data_len += gps->totpoints;
|
|
}
|
|
}
|
|
else {
|
|
/* Only selected stroke points are considered. */
|
|
if (gps->flag & GP_STROKE_SELECT) {
|
|
bGPDspoint *pt;
|
|
int i;
|
|
|
|
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
|
if (pt->flag & GP_SPOINT_SELECT) {
|
|
tc->data_len++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* If not multi-edit out of loop. */
|
|
if (!is_multiedit) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/* Stop trying if nothing selected. */
|
|
if (tc->data_len == 0) {
|
|
return;
|
|
}
|
|
|
|
/* Allocate memory for data */
|
|
tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransData(GPencil)");
|
|
td = tc->data;
|
|
|
|
unit_m3(smtx);
|
|
unit_m3(mtx);
|
|
|
|
/* Second Pass: Build transdata array. */
|
|
LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
|
|
/* only editable and visible layers are considered */
|
|
if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
|
|
const int cfra = (gpl->flag & GP_LAYER_FRAMELOCK) ? gpl->actframe->framenum : cfra_scene;
|
|
bGPDframe *gpf = gpl->actframe;
|
|
float diff_mat[3][3];
|
|
float inverse_diff_mat[3][3];
|
|
|
|
bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
|
|
/* Init multiframe falloff options. */
|
|
int f_init = 0;
|
|
int f_end = 0;
|
|
|
|
if (use_multiframe_falloff) {
|
|
BKE_gpencil_frame_range_selected(gpl, &f_init, &f_end);
|
|
}
|
|
|
|
/* Calculate difference matrix. */
|
|
{
|
|
float diff_mat_tmp[4][4];
|
|
BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat_tmp);
|
|
copy_m3_m4(diff_mat, diff_mat_tmp);
|
|
}
|
|
|
|
/* Use safe invert for cases where the input matrix has zero axes. */
|
|
invert_m3_m3_safe_ortho(inverse_diff_mat, diff_mat);
|
|
|
|
/* Make a new frame to work on if the layer's frame
|
|
* and the current scene frame don't match up.
|
|
*
|
|
* - This is useful when animating as it saves that "uh-oh" moment when you realize you've
|
|
* spent too much time editing the wrong frame...
|
|
*/
|
|
if ((gpf->framenum != cfra) && (!is_multiedit)) {
|
|
if (IS_AUTOKEY_ON(scene)) {
|
|
gpf = BKE_gpencil_frame_addcopy(gpl, cfra);
|
|
}
|
|
/* In some weird situations (frame-lock enabled) return NULL. */
|
|
if (gpf == NULL) {
|
|
continue;
|
|
}
|
|
if (!is_multiedit) {
|
|
init_gpf = gpf;
|
|
}
|
|
}
|
|
|
|
/* Loop over strokes, adding TransData for points as needed... */
|
|
for (gpf = init_gpf; gpf; gpf = gpf->next) {
|
|
if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
|
|
|
|
/* If multi-frame and falloff, recalculate and save value. */
|
|
float falloff = 1.0f; /* by default no falloff */
|
|
if ((is_multiedit) && (use_multiframe_falloff)) {
|
|
/* Falloff depends on distance to active frame
|
|
* (relative to the overall frame range). */
|
|
falloff = BKE_gpencil_multiframe_falloff_calc(
|
|
gpf, gpl->actframe->framenum, f_init, f_end, ts->gp_sculpt.cur_falloff);
|
|
}
|
|
|
|
LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
|
|
TransData *head = td;
|
|
TransData *tail = td;
|
|
bool stroke_ok;
|
|
|
|
/* skip strokes that are invalid for current view */
|
|
if (ED_gpencil_stroke_can_use(C, gps) == false) {
|
|
continue;
|
|
}
|
|
/* check if the color is editable */
|
|
if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
|
|
continue;
|
|
}
|
|
/* What we need to include depends on proportional editing settings... */
|
|
if (is_prop_edit) {
|
|
if (is_prop_edit_connected) {
|
|
/* A) "Connected" - Only those in selected strokes */
|
|
stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0;
|
|
}
|
|
else {
|
|
/* B) All points, always */
|
|
stroke_ok = true;
|
|
}
|
|
}
|
|
else {
|
|
/* C) Only selected points in selected strokes */
|
|
stroke_ok = (gps->flag & GP_STROKE_SELECT) != 0;
|
|
}
|
|
|
|
/* Do stroke... */
|
|
if (stroke_ok && gps->totpoints) {
|
|
bGPDspoint *pt;
|
|
int i;
|
|
|
|
/* save falloff factor */
|
|
gps->runtime.multi_frame_falloff = falloff;
|
|
|
|
/* calculate stroke center */
|
|
float center[3];
|
|
createTransGPencil_center_get(gps, center);
|
|
|
|
/* add all necessary points... */
|
|
for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
|
|
bool point_ok;
|
|
|
|
/* include point? */
|
|
if (is_prop_edit) {
|
|
/* Always all points in strokes that get included. */
|
|
point_ok = true;
|
|
}
|
|
else {
|
|
/* Only selected points in selected strokes. */
|
|
point_ok = (pt->flag & GP_SPOINT_SELECT) != 0;
|
|
}
|
|
|
|
/* do point... */
|
|
if (point_ok) {
|
|
copy_v3_v3(td->iloc, &pt->x);
|
|
/* Only copy center in local origins.
|
|
* This allows get interesting effects also when move
|
|
* using proportional editing. */
|
|
if ((gps->flag & GP_STROKE_SELECT) &&
|
|
(ts->transform_pivot_point == V3D_AROUND_LOCAL_ORIGINS)) {
|
|
copy_v3_v3(td->center, center);
|
|
}
|
|
else {
|
|
copy_v3_v3(td->center, &pt->x);
|
|
}
|
|
|
|
td->loc = &pt->x;
|
|
|
|
td->flag = 0;
|
|
|
|
if (pt->flag & GP_SPOINT_SELECT) {
|
|
td->flag |= TD_SELECTED;
|
|
}
|
|
|
|
/* For other transform modes (e.g. shrink-fatten), need to additional data
|
|
* but never for mirror.
|
|
*/
|
|
if (t->mode != TFM_MIRROR) {
|
|
if (t->mode != TFM_GPENCIL_OPACITY) {
|
|
if (is_scale_thickness) {
|
|
td->val = &pt->pressure;
|
|
td->ival = pt->pressure;
|
|
}
|
|
}
|
|
else {
|
|
td->val = &pt->strength;
|
|
td->ival = pt->strength;
|
|
}
|
|
}
|
|
|
|
/* Screen-space needs special matrices. */
|
|
if ((gps->flag & (GP_STROKE_3DSPACE | GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) ==
|
|
0) {
|
|
/* Screen-space. */
|
|
td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
|
|
}
|
|
else {
|
|
/* configure 2D data-space points so that they don't play up. */
|
|
if (gps->flag & (GP_STROKE_2DSPACE | GP_STROKE_2DIMAGE)) {
|
|
td->protectflag = OB_LOCK_LOCZ | OB_LOCK_ROTZ | OB_LOCK_SCALEZ;
|
|
}
|
|
}
|
|
/* apply parent transformations */
|
|
copy_m3_m3(td->smtx, inverse_diff_mat); /* final position */
|
|
copy_m3_m3(td->mtx, diff_mat); /* display position */
|
|
copy_m3_m3(td->axismtx, diff_mat); /* axis orientation */
|
|
|
|
/* Triangulation must be calculated again,
|
|
* so save the stroke for recalculate function. */
|
|
td->extra = gps;
|
|
|
|
/* Save pointer to object. */
|
|
td->ob = obact;
|
|
|
|
td++;
|
|
tail++;
|
|
}
|
|
}
|
|
|
|
/* March over these points, and calculate the proportional editing distances. */
|
|
if (is_prop_edit && (head != tail)) {
|
|
calc_distanceCurveVerts(head, tail - 1, false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
/* If not multi-edit out of loop. */
|
|
if (!is_multiedit) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static void createTransGPencil(bContext *C, TransInfo *t)
|
|
{
|
|
if (t->data_container_len == 0) {
|
|
return;
|
|
}
|
|
|
|
Depsgraph *depsgraph = CTX_data_ensure_evaluated_depsgraph(C);
|
|
const Scene *scene = CTX_data_scene(C);
|
|
ToolSettings *ts = scene->toolsettings;
|
|
BKE_view_layer_synced_ensure(t->scene, t->view_layer);
|
|
Object *obact = BKE_view_layer_active_object_get(t->view_layer);
|
|
bGPdata *gpd = obact->data;
|
|
BLI_assert(gpd != NULL);
|
|
|
|
const int cfra_scene = scene->r.cfra;
|
|
|
|
const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
|
|
const bool use_multiframe_falloff = (ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_FRAME_FALLOFF) !=
|
|
0;
|
|
|
|
const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
|
|
const bool is_prop_edit_connected = (t->flag & T_PROP_CONNECTED) != 0;
|
|
const bool is_scale_thickness = ((t->mode == TFM_GPENCIL_SHRINKFATTEN) ||
|
|
(ts->gp_sculpt.flag & GP_SCULPT_SETT_FLAG_SCALE_THICKNESS));
|
|
|
|
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
|
|
|
|
/* initialize falloff curve */
|
|
if (is_multiedit) {
|
|
BKE_curvemapping_init(ts->gp_sculpt.cur_falloff);
|
|
}
|
|
|
|
if (gpd == NULL) {
|
|
return;
|
|
}
|
|
|
|
if (is_curve_edit) {
|
|
createTransGPencil_curves(C,
|
|
t,
|
|
depsgraph,
|
|
ts,
|
|
obact,
|
|
gpd,
|
|
cfra_scene,
|
|
is_multiedit,
|
|
use_multiframe_falloff,
|
|
is_prop_edit,
|
|
is_prop_edit_connected,
|
|
is_scale_thickness);
|
|
}
|
|
else {
|
|
createTransGPencil_strokes(C,
|
|
t,
|
|
depsgraph,
|
|
ts,
|
|
obact,
|
|
gpd,
|
|
cfra_scene,
|
|
is_multiedit,
|
|
use_multiframe_falloff,
|
|
is_prop_edit,
|
|
is_prop_edit_connected,
|
|
is_scale_thickness);
|
|
}
|
|
}
|
|
|
|
static void recalcData_gpencil_strokes(TransInfo *t)
|
|
{
|
|
TransDataContainer *tc = TRANS_DATA_CONTAINER_FIRST_SINGLE(t);
|
|
GHash *strokes = BLI_ghash_ptr_new(__func__);
|
|
|
|
TransData *td = tc->data;
|
|
bGPdata *gpd = td->ob->data;
|
|
const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
|
|
for (int i = 0; i < tc->data_len; i++, td++) {
|
|
bGPDstroke *gps = td->extra;
|
|
|
|
if ((gps != NULL) && !BLI_ghash_haskey(strokes, gps)) {
|
|
BLI_ghash_insert(strokes, gps, gps);
|
|
if (is_curve_edit && gps->editcurve != NULL) {
|
|
BKE_gpencil_editcurve_recalculate_handles(gps);
|
|
gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
|
|
}
|
|
/* Calc geometry data. */
|
|
BKE_gpencil_stroke_geometry_update(gpd, gps);
|
|
}
|
|
}
|
|
BLI_ghash_free(strokes, NULL, NULL);
|
|
}
|
|
|
|
/** \} */
|
|
|
|
TransConvertTypeInfo TransConvertType_GPencil = {
|
|
/*flags*/ (T_EDIT | T_POINTS),
|
|
/*createTransData*/ createTransGPencil,
|
|
/*recalcData*/ recalcData_gpencil_strokes,
|
|
/*special_aftertrans_update*/ NULL,
|
|
};
|