Code Cleanup
* Stroke editing functions should be in gpencil_edit.c not gpencil_data.c (the latter is only for handling "CRUD" operations on things like layers, brushes, and palettes) * Deduplicate the GP_STROKE_BUFFER_MAX define
This commit is contained in:
@@ -75,9 +75,6 @@
|
|||||||
|
|
||||||
#include "gpencil_intern.h"
|
#include "gpencil_intern.h"
|
||||||
|
|
||||||
/* maximum sizes of gp-session buffer */
|
|
||||||
#define GP_STROKE_BUFFER_MAX 5000
|
|
||||||
|
|
||||||
/* ************************************************ */
|
/* ************************************************ */
|
||||||
/* Datablock Operators */
|
/* Datablock Operators */
|
||||||
|
|
||||||
@@ -965,431 +962,6 @@ void GPENCIL_OT_stroke_lock_color(wmOperatorType *ot)
|
|||||||
ot->poll = gp_active_layer_poll;
|
ot->poll = gp_active_layer_poll;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* ******************* Apply layer thickness change to Strokes ************************** */
|
|
||||||
|
|
||||||
static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
|
|
||||||
{
|
|
||||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
|
||||||
bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
|
|
||||||
|
|
||||||
/* sanity checks */
|
|
||||||
if (ELEM(NULL, gpd, gpl, gpl->frames.first))
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
|
|
||||||
/* loop all strokes */
|
|
||||||
for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
|
||||||
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
|
|
||||||
/* Apply thickness */
|
|
||||||
gps->thickness = gps->thickness + gpl->thickness;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* clear value */
|
|
||||||
gpl->thickness = 0.0f;
|
|
||||||
|
|
||||||
/* notifiers */
|
|
||||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
/* identifiers */
|
|
||||||
ot->name = "Apply Stroke Thickness";
|
|
||||||
ot->idname = "GPENCIL_OT_stroke_apply_thickness";
|
|
||||||
ot->description = "Apply the thickness change of the layer to its strokes";
|
|
||||||
|
|
||||||
/* api callbacks */
|
|
||||||
ot->exec = gp_stroke_apply_thickness_exec;
|
|
||||||
ot->poll = gp_active_layer_poll;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ******************* Close Strokes ************************** */
|
|
||||||
|
|
||||||
enum {
|
|
||||||
GP_STROKE_CYCLIC_CLOSE = 1,
|
|
||||||
GP_STROKE_CYCLIC_OPEN = 2,
|
|
||||||
GP_STROKE_CYCLIC_TOGGLE = 3
|
|
||||||
};
|
|
||||||
|
|
||||||
static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
|
|
||||||
{
|
|
||||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
|
||||||
const int type = RNA_enum_get(op->ptr, "type");
|
|
||||||
|
|
||||||
/* sanity checks */
|
|
||||||
if (ELEM(NULL, gpd))
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
|
|
||||||
/* loop all selected strokes */
|
|
||||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
|
||||||
{
|
|
||||||
for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
|
|
||||||
bGPDpalettecolor *palcolor = gps->palcolor;
|
|
||||||
|
|
||||||
/* skip strokes that are not selected or invalid for current view */
|
|
||||||
if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
|
|
||||||
continue;
|
|
||||||
/* skip hidden or locked colors */
|
|
||||||
if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
|
|
||||||
continue;
|
|
||||||
|
|
||||||
switch (type) {
|
|
||||||
case GP_STROKE_CYCLIC_CLOSE:
|
|
||||||
/* Close all (enable) */
|
|
||||||
gps->flag |= GP_STROKE_CYCLIC;
|
|
||||||
break;
|
|
||||||
case GP_STROKE_CYCLIC_OPEN:
|
|
||||||
/* Open all (disable) */
|
|
||||||
gps->flag &= ~GP_STROKE_CYCLIC;
|
|
||||||
break;
|
|
||||||
case GP_STROKE_CYCLIC_TOGGLE:
|
|
||||||
/* Just toggle flag... */
|
|
||||||
gps->flag ^= GP_STROKE_CYCLIC;
|
|
||||||
break;
|
|
||||||
default:
|
|
||||||
BLI_assert(0);
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CTX_DATA_END;
|
|
||||||
|
|
||||||
/* notifiers */
|
|
||||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
|
|
||||||
* option to force opened/closed strokes instead of just toggle behavior.
|
|
||||||
*/
|
|
||||||
void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
static EnumPropertyItem cyclic_type[] = {
|
|
||||||
{GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
|
|
||||||
{GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
|
|
||||||
{GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
|
|
||||||
{0, NULL, 0, NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* identifiers */
|
|
||||||
ot->name = "Set Cyclical State";
|
|
||||||
ot->idname = "GPENCIL_OT_stroke_cyclical_set";
|
|
||||||
ot->description = "Close or open the selected stroke adding an edge from last to first point";
|
|
||||||
|
|
||||||
/* api callbacks */
|
|
||||||
ot->exec = gp_stroke_cyclical_set_exec;
|
|
||||||
ot->poll = gp_active_layer_poll;
|
|
||||||
|
|
||||||
/* flags */
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
||||||
|
|
||||||
ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ******************* Stroke join ************************** */
|
|
||||||
|
|
||||||
/* Helper: flip stroke */
|
|
||||||
static void gpencil_flip_stroke(bGPDstroke *gps)
|
|
||||||
{
|
|
||||||
bGPDspoint pt, *point, *point2;
|
|
||||||
int end = gps->totpoints - 1;
|
|
||||||
|
|
||||||
for (int i = 0; i < gps->totpoints / 2; i++) {
|
|
||||||
/* save first point */
|
|
||||||
point = &gps->points[i];
|
|
||||||
pt.x = point->x;
|
|
||||||
pt.y = point->y;
|
|
||||||
pt.z = point->z;
|
|
||||||
pt.flag = point->flag;
|
|
||||||
pt.pressure = point->pressure;
|
|
||||||
pt.strength = point->strength;
|
|
||||||
pt.time = point->time;
|
|
||||||
|
|
||||||
/* replace first point with last point */
|
|
||||||
point2 = &gps->points[end];
|
|
||||||
point->x = point2->x;
|
|
||||||
point->y = point2->y;
|
|
||||||
point->z = point2->z;
|
|
||||||
point->flag = point2->flag;
|
|
||||||
point->pressure = point2->pressure;
|
|
||||||
point->strength = point2->strength;
|
|
||||||
point->time = point2->time;
|
|
||||||
|
|
||||||
/* replace last point with first saved before */
|
|
||||||
point = &gps->points[end];
|
|
||||||
point->x = pt.x;
|
|
||||||
point->y = pt.y;
|
|
||||||
point->z = pt.z;
|
|
||||||
point->flag = pt.flag;
|
|
||||||
point->pressure = pt.pressure;
|
|
||||||
point->strength = pt.strength;
|
|
||||||
point->time = pt.time;
|
|
||||||
|
|
||||||
end--;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper: copy point between strokes */
|
|
||||||
static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
|
|
||||||
float pressure, float strength, float deltatime)
|
|
||||||
{
|
|
||||||
bGPDspoint *newpoint;
|
|
||||||
|
|
||||||
gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
|
|
||||||
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;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
|
|
||||||
static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
|
|
||||||
{
|
|
||||||
bGPDspoint point, *pt;
|
|
||||||
int i;
|
|
||||||
float delta[3] = {1.0f, 1.0f, 1.0f};
|
|
||||||
float deltatime = 0.0f;
|
|
||||||
|
|
||||||
/* sanity checks */
|
|
||||||
if (ELEM(NULL, gps_a, gps_b))
|
|
||||||
return;
|
|
||||||
|
|
||||||
if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
|
|
||||||
return;
|
|
||||||
|
|
||||||
/* define start and end points of each stroke */
|
|
||||||
float sa[3], sb[3], ea[3], eb[3];
|
|
||||||
pt = &gps_a->points[0];
|
|
||||||
copy_v3_v3(sa, &pt->x);
|
|
||||||
|
|
||||||
pt = &gps_a->points[gps_a->totpoints - 1];
|
|
||||||
copy_v3_v3(ea, &pt->x);
|
|
||||||
|
|
||||||
pt = &gps_b->points[0];
|
|
||||||
copy_v3_v3(sb, &pt->x);
|
|
||||||
|
|
||||||
pt = &gps_b->points[gps_b->totpoints - 1];
|
|
||||||
copy_v3_v3(eb, &pt->x);
|
|
||||||
|
|
||||||
/* review if need flip stroke B */
|
|
||||||
float ea_sb = len_squared_v3v3(ea, sb);
|
|
||||||
float ea_eb = len_squared_v3v3(ea, eb);
|
|
||||||
/* flip if distance to end point is shorter */
|
|
||||||
if (ea_eb < ea_sb) {
|
|
||||||
gpencil_flip_stroke(gps_b);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* don't visibly link the first and last points? */
|
|
||||||
if (leave_gaps) {
|
|
||||||
/* 1st: add one tail point to start invisible area */
|
|
||||||
point = gps_a->points[gps_a->totpoints - 1];
|
|
||||||
deltatime = point.time;
|
|
||||||
gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
|
|
||||||
|
|
||||||
/* 2nd: add one head point to finish invisible area */
|
|
||||||
point = gps_b->points[0];
|
|
||||||
gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 3rd: add all points */
|
|
||||||
for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
|
|
||||||
/* check if still room in buffer */
|
|
||||||
if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
|
|
||||||
gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
static int gp_stroke_join_exec(bContext *C, wmOperator *op)
|
|
||||||
{
|
|
||||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
|
||||||
bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
|
|
||||||
bGPDstroke *gps, *gpsn;
|
|
||||||
bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
|
|
||||||
bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
|
|
||||||
|
|
||||||
bGPDframe *gpf_a = NULL;
|
|
||||||
bGPDstroke *stroke_a = NULL;
|
|
||||||
bGPDstroke *stroke_b = NULL;
|
|
||||||
bGPDstroke *new_stroke = NULL;
|
|
||||||
|
|
||||||
const int type = RNA_enum_get(op->ptr, "type");
|
|
||||||
const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
|
|
||||||
|
|
||||||
/* sanity checks */
|
|
||||||
if (ELEM(NULL, gpd))
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
|
|
||||||
if (activegpl->flag & GP_LAYER_LOCKED)
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
|
|
||||||
BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
|
|
||||||
|
|
||||||
|
|
||||||
/* read all selected strokes */
|
|
||||||
bool first = false;
|
|
||||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
|
||||||
{
|
|
||||||
bGPDframe *gpf = gpl->actframe;
|
|
||||||
for (gps = gpf->strokes.first; gps; gps = gpsn) {
|
|
||||||
gpsn = gps->next;
|
|
||||||
if (gps->flag & GP_STROKE_SELECT) {
|
|
||||||
/* 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_color_use(gpl, gps) == false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* to join strokes, cyclic must be disabled */
|
|
||||||
gps->flag &= ~GP_STROKE_CYCLIC;
|
|
||||||
/* saves first frame and stroke */
|
|
||||||
if (!first) {
|
|
||||||
first = true;
|
|
||||||
gpf_a = gpf;
|
|
||||||
stroke_a = gps;
|
|
||||||
}
|
|
||||||
else {
|
|
||||||
stroke_b = gps;
|
|
||||||
/* create a new stroke if was not created before (only created if something to join) */
|
|
||||||
if (new_stroke == NULL) {
|
|
||||||
new_stroke = MEM_dupallocN(stroke_a);
|
|
||||||
new_stroke->points = MEM_dupallocN(stroke_a->points);
|
|
||||||
new_stroke->triangles = NULL;
|
|
||||||
new_stroke->tot_triangles = 0;
|
|
||||||
new_stroke->flag |= GP_STROKE_RECALC_CACHES;
|
|
||||||
/* if new, set current color */
|
|
||||||
if (type == GP_STROKE_JOINCOPY) {
|
|
||||||
new_stroke->palcolor = palcolor;
|
|
||||||
BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname));
|
|
||||||
new_stroke->flag |= GP_STROKE_RECALC_COLOR;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
/* join new_stroke and stroke B. New stroke will contain all the previous data */
|
|
||||||
gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
|
|
||||||
|
|
||||||
/* if join only, delete old strokes */
|
|
||||||
if (type == GP_STROKE_JOIN) {
|
|
||||||
if (stroke_a) {
|
|
||||||
BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
|
|
||||||
BLI_remlink(&gpf->strokes, stroke_a);
|
|
||||||
BKE_gpencil_free_stroke(stroke_a);
|
|
||||||
stroke_a = NULL;
|
|
||||||
}
|
|
||||||
if (stroke_b) {
|
|
||||||
BLI_remlink(&gpf->strokes, stroke_b);
|
|
||||||
BKE_gpencil_free_stroke(stroke_b);
|
|
||||||
stroke_b = NULL;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CTX_DATA_END;
|
|
||||||
/* add new stroke if was not added before */
|
|
||||||
if (type == GP_STROKE_JOINCOPY) {
|
|
||||||
if (new_stroke) {
|
|
||||||
/* Add a new frame if needed */
|
|
||||||
if (activegpl->actframe == NULL)
|
|
||||||
activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
|
|
||||||
|
|
||||||
BLI_addtail(&activegpl->actframe->strokes, new_stroke);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* notifiers */
|
|
||||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPENCIL_OT_stroke_join(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
static EnumPropertyItem join_type[] = {
|
|
||||||
{GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
|
|
||||||
{GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
|
|
||||||
{0, NULL, 0, NULL, NULL}
|
|
||||||
};
|
|
||||||
|
|
||||||
/* identifiers */
|
|
||||||
ot->name = "Join Strokes";
|
|
||||||
ot->idname = "GPENCIL_OT_stroke_join";
|
|
||||||
ot->description = "Join selected strokes (optionally as new stroke)";
|
|
||||||
|
|
||||||
/* api callbacks */
|
|
||||||
ot->exec = gp_stroke_join_exec;
|
|
||||||
ot->poll = gp_active_layer_poll;
|
|
||||||
|
|
||||||
/* flags */
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
||||||
|
|
||||||
ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
|
|
||||||
RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ******************* Stroke flip ************************** */
|
|
||||||
|
|
||||||
static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
|
|
||||||
{
|
|
||||||
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
|
||||||
|
|
||||||
/* sanity checks */
|
|
||||||
if (ELEM(NULL, gpd))
|
|
||||||
return OPERATOR_CANCELLED;
|
|
||||||
|
|
||||||
/* read all selected strokes */
|
|
||||||
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
|
||||||
{
|
|
||||||
bGPDframe *gpf = gpl->actframe;
|
|
||||||
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
|
|
||||||
if (gps->flag & GP_STROKE_SELECT) {
|
|
||||||
/* 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_color_use(gpl, gps) == false) {
|
|
||||||
continue;
|
|
||||||
}
|
|
||||||
/* flip stroke */
|
|
||||||
gpencil_flip_stroke(gps);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
CTX_DATA_END;
|
|
||||||
|
|
||||||
/* notifiers */
|
|
||||||
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
|
||||||
|
|
||||||
return OPERATOR_FINISHED;
|
|
||||||
}
|
|
||||||
|
|
||||||
void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
|
|
||||||
{
|
|
||||||
/* identifiers */
|
|
||||||
ot->name = "Flip Stroke";
|
|
||||||
ot->idname = "GPENCIL_OT_stroke_flip";
|
|
||||||
ot->description = "Change direction of the points of the selected strokes";
|
|
||||||
|
|
||||||
/* api callbacks */
|
|
||||||
ot->exec = gp_stroke_flip_exec;
|
|
||||||
ot->poll = gp_active_layer_poll;
|
|
||||||
|
|
||||||
/* flags */
|
|
||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
|
||||||
}
|
|
||||||
|
|
||||||
/* ************************************************ */
|
/* ************************************************ */
|
||||||
/* Drawing Brushes Operators */
|
/* Drawing Brushes Operators */
|
||||||
|
|
||||||
|
@@ -1455,5 +1455,429 @@ void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
|
|||||||
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* ******************* Apply layer thickness change to Strokes ************************** */
|
||||||
|
|
||||||
|
static int gp_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
|
||||||
|
{
|
||||||
|
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||||
|
bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
|
||||||
|
|
||||||
|
/* sanity checks */
|
||||||
|
if (ELEM(NULL, gpd, gpl, gpl->frames.first))
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
|
||||||
|
/* loop all strokes */
|
||||||
|
for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
|
||||||
|
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||||
|
/* Apply thickness */
|
||||||
|
gps->thickness = gps->thickness + gpl->thickness;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* clear value */
|
||||||
|
gpl->thickness = 0.0f;
|
||||||
|
|
||||||
|
/* notifiers */
|
||||||
|
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||||
|
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Apply Stroke Thickness";
|
||||||
|
ot->idname = "GPENCIL_OT_stroke_apply_thickness";
|
||||||
|
ot->description = "Apply the thickness change of the layer to its strokes";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->exec = gp_stroke_apply_thickness_exec;
|
||||||
|
ot->poll = gp_active_layer_poll;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ******************* Close Strokes ************************** */
|
||||||
|
|
||||||
|
enum {
|
||||||
|
GP_STROKE_CYCLIC_CLOSE = 1,
|
||||||
|
GP_STROKE_CYCLIC_OPEN = 2,
|
||||||
|
GP_STROKE_CYCLIC_TOGGLE = 3
|
||||||
|
};
|
||||||
|
|
||||||
|
static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
|
||||||
|
{
|
||||||
|
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||||
|
const int type = RNA_enum_get(op->ptr, "type");
|
||||||
|
|
||||||
|
/* sanity checks */
|
||||||
|
if (ELEM(NULL, gpd))
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
|
||||||
|
/* loop all selected strokes */
|
||||||
|
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||||
|
{
|
||||||
|
for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
|
||||||
|
bGPDpalettecolor *palcolor = gps->palcolor;
|
||||||
|
|
||||||
|
/* skip strokes that are not selected or invalid for current view */
|
||||||
|
if (((gps->flag & GP_STROKE_SELECT) == 0) || ED_gpencil_stroke_can_use(C, gps) == false)
|
||||||
|
continue;
|
||||||
|
/* skip hidden or locked colors */
|
||||||
|
if (!palcolor || (palcolor->flag & PC_COLOR_HIDE) || (palcolor->flag & PC_COLOR_LOCKED))
|
||||||
|
continue;
|
||||||
|
|
||||||
|
switch (type) {
|
||||||
|
case GP_STROKE_CYCLIC_CLOSE:
|
||||||
|
/* Close all (enable) */
|
||||||
|
gps->flag |= GP_STROKE_CYCLIC;
|
||||||
|
break;
|
||||||
|
case GP_STROKE_CYCLIC_OPEN:
|
||||||
|
/* Open all (disable) */
|
||||||
|
gps->flag &= ~GP_STROKE_CYCLIC;
|
||||||
|
break;
|
||||||
|
case GP_STROKE_CYCLIC_TOGGLE:
|
||||||
|
/* Just toggle flag... */
|
||||||
|
gps->flag ^= GP_STROKE_CYCLIC;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
BLI_assert(0);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CTX_DATA_END;
|
||||||
|
|
||||||
|
/* notifiers */
|
||||||
|
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||||
|
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Similar to #CURVE_OT_cyclic_toggle or #MASK_OT_cyclic_toggle, but with
|
||||||
|
* option to force opened/closed strokes instead of just toggle behavior.
|
||||||
|
*/
|
||||||
|
void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
static EnumPropertyItem cyclic_type[] = {
|
||||||
|
{GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close all", ""},
|
||||||
|
{GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open all", ""},
|
||||||
|
{GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
|
||||||
|
{0, NULL, 0, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Set Cyclical State";
|
||||||
|
ot->idname = "GPENCIL_OT_stroke_cyclical_set";
|
||||||
|
ot->description = "Close or open the selected stroke adding an edge from last to first point";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->exec = gp_stroke_cyclical_set_exec;
|
||||||
|
ot->poll = gp_active_layer_poll;
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ******************* Stroke join ************************** */
|
||||||
|
|
||||||
|
/* Helper: flip stroke */
|
||||||
|
static void gpencil_flip_stroke(bGPDstroke *gps)
|
||||||
|
{
|
||||||
|
bGPDspoint pt, *point, *point2;
|
||||||
|
int end = gps->totpoints - 1;
|
||||||
|
|
||||||
|
for (int i = 0; i < gps->totpoints / 2; i++) {
|
||||||
|
/* save first point */
|
||||||
|
point = &gps->points[i];
|
||||||
|
pt.x = point->x;
|
||||||
|
pt.y = point->y;
|
||||||
|
pt.z = point->z;
|
||||||
|
pt.flag = point->flag;
|
||||||
|
pt.pressure = point->pressure;
|
||||||
|
pt.strength = point->strength;
|
||||||
|
pt.time = point->time;
|
||||||
|
|
||||||
|
/* replace first point with last point */
|
||||||
|
point2 = &gps->points[end];
|
||||||
|
point->x = point2->x;
|
||||||
|
point->y = point2->y;
|
||||||
|
point->z = point2->z;
|
||||||
|
point->flag = point2->flag;
|
||||||
|
point->pressure = point2->pressure;
|
||||||
|
point->strength = point2->strength;
|
||||||
|
point->time = point2->time;
|
||||||
|
|
||||||
|
/* replace last point with first saved before */
|
||||||
|
point = &gps->points[end];
|
||||||
|
point->x = pt.x;
|
||||||
|
point->y = pt.y;
|
||||||
|
point->z = pt.z;
|
||||||
|
point->flag = pt.flag;
|
||||||
|
point->pressure = pt.pressure;
|
||||||
|
point->strength = pt.strength;
|
||||||
|
point->time = pt.time;
|
||||||
|
|
||||||
|
end--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper: copy point between strokes */
|
||||||
|
static void gpencil_stroke_copy_point(bGPDstroke *gps, bGPDspoint *point, float delta[3],
|
||||||
|
float pressure, float strength, float deltatime)
|
||||||
|
{
|
||||||
|
bGPDspoint *newpoint;
|
||||||
|
|
||||||
|
gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
|
/* Helper: join two strokes using the shortest distance (reorder stroke if necessary ) */
|
||||||
|
static void gpencil_stroke_join_strokes(bGPDstroke *gps_a, bGPDstroke *gps_b, const bool leave_gaps)
|
||||||
|
{
|
||||||
|
bGPDspoint point, *pt;
|
||||||
|
int i;
|
||||||
|
float delta[3] = {1.0f, 1.0f, 1.0f};
|
||||||
|
float deltatime = 0.0f;
|
||||||
|
|
||||||
|
/* sanity checks */
|
||||||
|
if (ELEM(NULL, gps_a, gps_b))
|
||||||
|
return;
|
||||||
|
|
||||||
|
if ((gps_a->totpoints == 0) || (gps_b->totpoints == 0))
|
||||||
|
return;
|
||||||
|
|
||||||
|
/* define start and end points of each stroke */
|
||||||
|
float sa[3], sb[3], ea[3], eb[3];
|
||||||
|
pt = &gps_a->points[0];
|
||||||
|
copy_v3_v3(sa, &pt->x);
|
||||||
|
|
||||||
|
pt = &gps_a->points[gps_a->totpoints - 1];
|
||||||
|
copy_v3_v3(ea, &pt->x);
|
||||||
|
|
||||||
|
pt = &gps_b->points[0];
|
||||||
|
copy_v3_v3(sb, &pt->x);
|
||||||
|
|
||||||
|
pt = &gps_b->points[gps_b->totpoints - 1];
|
||||||
|
copy_v3_v3(eb, &pt->x);
|
||||||
|
|
||||||
|
/* review if need flip stroke B */
|
||||||
|
float ea_sb = len_squared_v3v3(ea, sb);
|
||||||
|
float ea_eb = len_squared_v3v3(ea, eb);
|
||||||
|
/* flip if distance to end point is shorter */
|
||||||
|
if (ea_eb < ea_sb) {
|
||||||
|
gpencil_flip_stroke(gps_b);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* don't visibly link the first and last points? */
|
||||||
|
if (leave_gaps) {
|
||||||
|
/* 1st: add one tail point to start invisible area */
|
||||||
|
point = gps_a->points[gps_a->totpoints - 1];
|
||||||
|
deltatime = point.time;
|
||||||
|
gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, 0.0f);
|
||||||
|
|
||||||
|
/* 2nd: add one head point to finish invisible area */
|
||||||
|
point = gps_b->points[0];
|
||||||
|
gpencil_stroke_copy_point(gps_a, &point, delta, 0.0f, 0.0f, deltatime);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 3rd: add all points */
|
||||||
|
for (i = 0, pt = gps_b->points; i < gps_b->totpoints && pt; i++, pt++) {
|
||||||
|
/* check if still room in buffer */
|
||||||
|
if (gps_a->totpoints <= GP_STROKE_BUFFER_MAX - 2) {
|
||||||
|
gpencil_stroke_copy_point(gps_a, pt, delta, pt->pressure, pt->strength, deltatime);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static int gp_stroke_join_exec(bContext *C, wmOperator *op)
|
||||||
|
{
|
||||||
|
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||||
|
bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
|
||||||
|
bGPDstroke *gps, *gpsn;
|
||||||
|
bGPDpalette *palette = BKE_gpencil_palette_getactive(gpd);
|
||||||
|
bGPDpalettecolor *palcolor = BKE_gpencil_palettecolor_getactive(palette);
|
||||||
|
|
||||||
|
bGPDframe *gpf_a = NULL;
|
||||||
|
bGPDstroke *stroke_a = NULL;
|
||||||
|
bGPDstroke *stroke_b = NULL;
|
||||||
|
bGPDstroke *new_stroke = NULL;
|
||||||
|
|
||||||
|
const int type = RNA_enum_get(op->ptr, "type");
|
||||||
|
const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
|
||||||
|
|
||||||
|
/* sanity checks */
|
||||||
|
if (ELEM(NULL, gpd))
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
|
||||||
|
if (activegpl->flag & GP_LAYER_LOCKED)
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
|
||||||
|
BLI_assert(ELEM(type, GP_STROKE_JOIN, GP_STROKE_JOINCOPY));
|
||||||
|
|
||||||
|
|
||||||
|
/* read all selected strokes */
|
||||||
|
bool first = false;
|
||||||
|
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||||
|
{
|
||||||
|
bGPDframe *gpf = gpl->actframe;
|
||||||
|
for (gps = gpf->strokes.first; gps; gps = gpsn) {
|
||||||
|
gpsn = gps->next;
|
||||||
|
if (gps->flag & GP_STROKE_SELECT) {
|
||||||
|
/* 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_color_use(gpl, gps) == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* to join strokes, cyclic must be disabled */
|
||||||
|
gps->flag &= ~GP_STROKE_CYCLIC;
|
||||||
|
/* saves first frame and stroke */
|
||||||
|
if (!first) {
|
||||||
|
first = true;
|
||||||
|
gpf_a = gpf;
|
||||||
|
stroke_a = gps;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
stroke_b = gps;
|
||||||
|
/* create a new stroke if was not created before (only created if something to join) */
|
||||||
|
if (new_stroke == NULL) {
|
||||||
|
new_stroke = MEM_dupallocN(stroke_a);
|
||||||
|
new_stroke->points = MEM_dupallocN(stroke_a->points);
|
||||||
|
new_stroke->triangles = NULL;
|
||||||
|
new_stroke->tot_triangles = 0;
|
||||||
|
new_stroke->flag |= GP_STROKE_RECALC_CACHES;
|
||||||
|
/* if new, set current color */
|
||||||
|
if (type == GP_STROKE_JOINCOPY) {
|
||||||
|
new_stroke->palcolor = palcolor;
|
||||||
|
BLI_strncpy(new_stroke->colorname, palcolor->info, sizeof(new_stroke->colorname));
|
||||||
|
new_stroke->flag |= GP_STROKE_RECALC_COLOR;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/* join new_stroke and stroke B. New stroke will contain all the previous data */
|
||||||
|
gpencil_stroke_join_strokes(new_stroke, stroke_b, leave_gaps);
|
||||||
|
|
||||||
|
/* if join only, delete old strokes */
|
||||||
|
if (type == GP_STROKE_JOIN) {
|
||||||
|
if (stroke_a) {
|
||||||
|
BLI_insertlinkbefore(&gpf_a->strokes, stroke_a, new_stroke);
|
||||||
|
BLI_remlink(&gpf->strokes, stroke_a);
|
||||||
|
BKE_gpencil_free_stroke(stroke_a);
|
||||||
|
stroke_a = NULL;
|
||||||
|
}
|
||||||
|
if (stroke_b) {
|
||||||
|
BLI_remlink(&gpf->strokes, stroke_b);
|
||||||
|
BKE_gpencil_free_stroke(stroke_b);
|
||||||
|
stroke_b = NULL;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CTX_DATA_END;
|
||||||
|
/* add new stroke if was not added before */
|
||||||
|
if (type == GP_STROKE_JOINCOPY) {
|
||||||
|
if (new_stroke) {
|
||||||
|
/* Add a new frame if needed */
|
||||||
|
if (activegpl->actframe == NULL)
|
||||||
|
activegpl->actframe = BKE_gpencil_frame_addnew(activegpl, gpf_a->framenum);
|
||||||
|
|
||||||
|
BLI_addtail(&activegpl->actframe->strokes, new_stroke);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/* notifiers */
|
||||||
|
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||||
|
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPENCIL_OT_stroke_join(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
static EnumPropertyItem join_type[] = {
|
||||||
|
{GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
|
||||||
|
{GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
|
||||||
|
{0, NULL, 0, NULL, NULL}
|
||||||
|
};
|
||||||
|
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Join Strokes";
|
||||||
|
ot->idname = "GPENCIL_OT_stroke_join";
|
||||||
|
ot->description = "Join selected strokes (optionally as new stroke)";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->exec = gp_stroke_join_exec;
|
||||||
|
ot->poll = gp_active_layer_poll;
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
|
||||||
|
ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
|
||||||
|
RNA_def_boolean(ot->srna, "leave_gaps", false, "Leave Gaps", "Leave gaps between joined strokes instead of linking them");
|
||||||
|
}
|
||||||
|
|
||||||
|
/* ******************* Stroke flip ************************** */
|
||||||
|
|
||||||
|
static int gp_stroke_flip_exec(bContext *C, wmOperator *UNUSED(op))
|
||||||
|
{
|
||||||
|
bGPdata *gpd = ED_gpencil_data_get_active(C);
|
||||||
|
|
||||||
|
/* sanity checks */
|
||||||
|
if (ELEM(NULL, gpd))
|
||||||
|
return OPERATOR_CANCELLED;
|
||||||
|
|
||||||
|
/* read all selected strokes */
|
||||||
|
CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
|
||||||
|
{
|
||||||
|
bGPDframe *gpf = gpl->actframe;
|
||||||
|
for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
|
||||||
|
if (gps->flag & GP_STROKE_SELECT) {
|
||||||
|
/* 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_color_use(gpl, gps) == false) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
/* flip stroke */
|
||||||
|
gpencil_flip_stroke(gps);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
CTX_DATA_END;
|
||||||
|
|
||||||
|
/* notifiers */
|
||||||
|
WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
|
||||||
|
|
||||||
|
return OPERATOR_FINISHED;
|
||||||
|
}
|
||||||
|
|
||||||
|
void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
|
||||||
|
{
|
||||||
|
/* identifiers */
|
||||||
|
ot->name = "Flip Stroke";
|
||||||
|
ot->idname = "GPENCIL_OT_stroke_flip";
|
||||||
|
ot->description = "Change direction of the points of the selected strokes";
|
||||||
|
|
||||||
|
/* api callbacks */
|
||||||
|
ot->exec = gp_stroke_flip_exec;
|
||||||
|
ot->poll = gp_active_layer_poll;
|
||||||
|
|
||||||
|
/* flags */
|
||||||
|
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
|
||||||
|
}
|
||||||
|
|
||||||
/* ************************************************ */
|
/* ************************************************ */
|
||||||
|
@@ -214,6 +214,9 @@ typedef enum eGPencil_PaintModes {
|
|||||||
GP_PAINTMODE_DRAW_POLY
|
GP_PAINTMODE_DRAW_POLY
|
||||||
} eGPencil_PaintModes;
|
} eGPencil_PaintModes;
|
||||||
|
|
||||||
|
/* maximum sizes of gp-session buffer */
|
||||||
|
#define GP_STROKE_BUFFER_MAX 5000
|
||||||
|
|
||||||
/* stroke editing ----- */
|
/* stroke editing ----- */
|
||||||
|
|
||||||
void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot);
|
void GPENCIL_OT_editmode_toggle(struct wmOperatorType *ot);
|
||||||
|
@@ -164,9 +164,6 @@ typedef struct tGPsdata {
|
|||||||
|
|
||||||
/* ------ */
|
/* ------ */
|
||||||
|
|
||||||
/* maximum sizes of gp-session buffer */
|
|
||||||
#define GP_STROKE_BUFFER_MAX 5000
|
|
||||||
|
|
||||||
/* Macros for accessing sensitivity thresholds... */
|
/* Macros for accessing sensitivity thresholds... */
|
||||||
/* minimum number of pixels mouse should move before new point created */
|
/* minimum number of pixels mouse should move before new point created */
|
||||||
#define MIN_MANHATTEN_PX (U.gp_manhattendist)
|
#define MIN_MANHATTEN_PX (U.gp_manhattendist)
|
||||||
|
Reference in New Issue
Block a user