Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/gpencil/gpencil_edit.c')
-rw-r--r--source/blender/editors/gpencil/gpencil_edit.c7454
1 files changed, 3737 insertions, 3717 deletions
diff --git a/source/blender/editors/gpencil/gpencil_edit.c b/source/blender/editors/gpencil/gpencil_edit.c
index e1eaedd435c..40e1e483f41 100644
--- a/source/blender/editors/gpencil/gpencil_edit.c
+++ b/source/blender/editors/gpencil/gpencil_edit.c
@@ -86,452 +86,455 @@
#include "gpencil_intern.h"
- /* ************************************************ */
- /* Stroke Edit Mode Management */
+/* ************************************************ */
+/* Stroke Edit Mode Management */
/* poll callback for all stroke editing operators */
static bool gp_stroke_edit_poll(bContext *C)
{
- /* edit only supported with grease pencil objects */
- Object *ob = CTX_data_active_object(C);
- if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
- return false;
- }
-
- /* NOTE: this is a bit slower, but is the most accurate... */
- return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
+ /* edit only supported with grease pencil objects */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+
+ /* NOTE: this is a bit slower, but is the most accurate... */
+ return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
}
/* poll callback to verify edit mode in 3D view only */
static bool gp_strokes_edit3d_poll(bContext *C)
{
- /* edit only supported with grease pencil objects */
- Object *ob = CTX_data_active_object(C);
- if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
- return false;
- }
-
-
- /* 2 Requirements:
- * - 1) Editable GP data
- * - 2) 3D View only
- */
- return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C));
+ /* edit only supported with grease pencil objects */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+
+ /* 2 Requirements:
+ * - 1) Editable GP data
+ * - 2) 3D View only
+ */
+ return (gp_stroke_edit_poll(C) && ED_operator_view3d_active(C));
}
static bool gpencil_editmode_toggle_poll(bContext *C)
{
- /* edit only supported with grease pencil objects */
- Object *ob = CTX_data_active_object(C);
- if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
- return false;
- }
-
- /* if using gpencil object, use this gpd */
- if (ob->type == OB_GPENCIL) {
- return ob->data != NULL;
- }
-
- return ED_gpencil_data_get_active(C) != NULL;
+ /* edit only supported with grease pencil objects */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
+ return false;
+ }
+
+ /* if using gpencil object, use this gpd */
+ if (ob->type == OB_GPENCIL) {
+ return ob->data != NULL;
+ }
+
+ return ED_gpencil_data_get_active(C) != NULL;
}
static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
{
- const int back = RNA_boolean_get(op->ptr, "back");
-
- struct wmMsgBus *mbus = CTX_wm_message_bus(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bool is_object = false;
- short mode;
- /* if using a gpencil object, use this datablock */
- Object *ob = CTX_data_active_object(C);
- if ((ob) && (ob->type == OB_GPENCIL)) {
- gpd = ob->data;
- is_object = true;
- }
-
- if (gpd == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No active GP data");
- return OPERATOR_CANCELLED;
- }
-
- /* Just toggle editmode flag... */
- gpd->flag ^= GP_DATA_STROKE_EDITMODE;
- /* recalculate parent matrix */
- if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
- ED_gpencil_reset_layers_parent(depsgraph, ob, gpd);
- }
- /* set mode */
- if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
- mode = OB_MODE_EDIT_GPENCIL;
- }
- else {
- mode = OB_MODE_OBJECT;
- }
-
- if (is_object) {
- /* try to back previous mode */
- if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) {
- mode = ob->restore_mode;
- }
- ob->restore_mode = ob->mode;
- ob->mode = mode;
- }
-
- /* setup other modes */
- ED_gpencil_setup_modes(C, gpd, mode);
- /* set cache as dirty */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
-
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
- WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL);
- WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
-
- if (is_object) {
- WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
- }
- if (G.background == false) {
- WM_toolsystem_update_from_context_view3d(C);
- }
-
- return OPERATOR_FINISHED;
+ const int back = RNA_boolean_get(op->ptr, "back");
+
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bool is_object = false;
+ short mode;
+ /* if using a gpencil object, use this datablock */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ gpd = ob->data;
+ is_object = true;
+ }
+
+ if (gpd == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No active GP data");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* Just toggle editmode flag... */
+ gpd->flag ^= GP_DATA_STROKE_EDITMODE;
+ /* recalculate parent matrix */
+ if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
+ ED_gpencil_reset_layers_parent(depsgraph, ob, gpd);
+ }
+ /* set mode */
+ if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
+ mode = OB_MODE_EDIT_GPENCIL;
+ }
+ else {
+ mode = OB_MODE_OBJECT;
+ }
+
+ if (is_object) {
+ /* try to back previous mode */
+ if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) {
+ mode = ob->restore_mode;
+ }
+ ob->restore_mode = ob->mode;
+ ob->mode = mode;
+ }
+
+ /* setup other modes */
+ ED_gpencil_setup_modes(C, gpd, mode);
+ /* set cache as dirty */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
+
+ if (is_object) {
+ WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
+ }
+ if (G.background == false) {
+ WM_toolsystem_update_from_context_view3d(C);
+ }
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_editmode_toggle(wmOperatorType *ot)
{
- PropertyRNA *prop;
+ PropertyRNA *prop;
- /* identifiers */
- ot->name = "Strokes Edit Mode Toggle";
- ot->idname = "GPENCIL_OT_editmode_toggle";
- ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
+ /* identifiers */
+ ot->name = "Strokes Edit Mode Toggle";
+ ot->idname = "GPENCIL_OT_editmode_toggle";
+ ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
- /* callbacks */
- ot->exec = gpencil_editmode_toggle_exec;
- ot->poll = gpencil_editmode_toggle_poll;
+ /* callbacks */
+ ot->exec = gpencil_editmode_toggle_exec;
+ ot->poll = gpencil_editmode_toggle_poll;
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
- /* properties */
- prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ /* properties */
+ prop = RNA_def_boolean(
+ ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* set select mode */
static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op)
{
- Scene *scene = CTX_data_scene(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- const int mode = RNA_int_get(op->ptr, "mode");
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ const int mode = RNA_int_get(op->ptr, "mode");
- /* Just set mode */
- ts->gpencil_selectmode = mode;
+ /* Just set mode */
+ ts->gpencil_selectmode = mode;
- WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
- DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+ WM_main_add_notifier(NC_SCENE | ND_TOOLSETTINGS, NULL);
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
- return OPERATOR_FINISHED;
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot)
{
- PropertyRNA *prop;
+ PropertyRNA *prop;
- /* identifiers */
- ot->name = "Select Mode Toggle";
- ot->idname = "GPENCIL_OT_selectmode_toggle";
- ot->description = "Set selection mode for Grease Pencil strokes";
+ /* identifiers */
+ ot->name = "Select Mode Toggle";
+ ot->idname = "GPENCIL_OT_selectmode_toggle";
+ ot->description = "Set selection mode for Grease Pencil strokes";
- /* callbacks */
- ot->exec = gpencil_selectmode_toggle_exec;
- ot->poll = gp_strokes_edit3d_poll;
+ /* callbacks */
+ ot->exec = gpencil_selectmode_toggle_exec;
+ ot->poll = gp_strokes_edit3d_poll;
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
- /* properties */
- prop = RNA_def_int(ot->srna, "mode", 0, 0, 2, "Select mode", "Select mode", 0, 2);
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ /* properties */
+ prop = RNA_def_int(ot->srna, "mode", 0, 0, 2, "Select mode", "Select mode", 0, 2);
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* Stroke Paint Mode Management */
static bool gpencil_paintmode_toggle_poll(bContext *C)
{
- /* if using gpencil object, use this gpd */
- Object *ob = CTX_data_active_object(C);
- if ((ob) && (ob->type == OB_GPENCIL)) {
- return ob->data != NULL;
- }
- return ED_gpencil_data_get_active(C) != NULL;
+ /* if using gpencil object, use this gpd */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ return ob->data != NULL;
+ }
+ return ED_gpencil_data_get_active(C) != NULL;
}
static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op)
{
- const bool back = RNA_boolean_get(op->ptr, "back");
-
- struct wmMsgBus *mbus = CTX_wm_message_bus(C);
- Main *bmain = CTX_data_main(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
-
- bool is_object = false;
- short mode;
- /* if using a gpencil object, use this datablock */
- Object *ob = CTX_data_active_object(C);
- if ((ob) && (ob->type == OB_GPENCIL)) {
- gpd = ob->data;
- is_object = true;
- }
-
- if (gpd == NULL)
- return OPERATOR_CANCELLED;
-
- /* Just toggle paintmode flag... */
- gpd->flag ^= GP_DATA_STROKE_PAINTMODE;
- /* set mode */
- if (gpd->flag & GP_DATA_STROKE_PAINTMODE) {
- mode = OB_MODE_PAINT_GPENCIL;
- }
- else {
- mode = OB_MODE_OBJECT;
- }
-
- if (is_object) {
- /* try to back previous mode */
- if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) {
- mode = ob->restore_mode;
- }
- ob->restore_mode = ob->mode;
- ob->mode = mode;
- }
-
- if (mode == OB_MODE_PAINT_GPENCIL) {
- /* be sure we have brushes */
- BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
- Paint *paint = &ts->gp_paint->paint;
- /* if not exist, create a new one */
- if (paint->brush == NULL) {
- BKE_brush_gpencil_presets(C);
- }
- BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint);
- }
-
- /* setup other modes */
- ED_gpencil_setup_modes(C, gpd, mode);
- /* set cache as dirty */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
-
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
- WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
-
- if (is_object) {
- WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
- }
- if (G.background == false) {
- WM_toolsystem_update_from_context_view3d(C);
- }
-
- return OPERATOR_FINISHED;
+ const bool back = RNA_boolean_get(op->ptr, "back");
+
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ Main *bmain = CTX_data_main(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+
+ bool is_object = false;
+ short mode;
+ /* if using a gpencil object, use this datablock */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ gpd = ob->data;
+ is_object = true;
+ }
+
+ if (gpd == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* Just toggle paintmode flag... */
+ gpd->flag ^= GP_DATA_STROKE_PAINTMODE;
+ /* set mode */
+ if (gpd->flag & GP_DATA_STROKE_PAINTMODE) {
+ mode = OB_MODE_PAINT_GPENCIL;
+ }
+ else {
+ mode = OB_MODE_OBJECT;
+ }
+
+ if (is_object) {
+ /* try to back previous mode */
+ if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) {
+ mode = ob->restore_mode;
+ }
+ ob->restore_mode = ob->mode;
+ ob->mode = mode;
+ }
+
+ if (mode == OB_MODE_PAINT_GPENCIL) {
+ /* be sure we have brushes */
+ BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
+ Paint *paint = &ts->gp_paint->paint;
+ /* if not exist, create a new one */
+ if (paint->brush == NULL) {
+ BKE_brush_gpencil_presets(C);
+ }
+ BKE_paint_toolslots_brush_validate(bmain, &ts->gp_paint->paint);
+ }
+
+ /* setup other modes */
+ ED_gpencil_setup_modes(C, gpd, mode);
+ /* set cache as dirty */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
+
+ if (is_object) {
+ WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
+ }
+ if (G.background == false) {
+ WM_toolsystem_update_from_context_view3d(C);
+ }
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_paintmode_toggle(wmOperatorType *ot)
{
- PropertyRNA *prop;
+ PropertyRNA *prop;
- /* identifiers */
- ot->name = "Strokes Paint Mode Toggle";
- ot->idname = "GPENCIL_OT_paintmode_toggle";
- ot->description = "Enter/Exit paint mode for Grease Pencil strokes";
+ /* identifiers */
+ ot->name = "Strokes Paint Mode Toggle";
+ ot->idname = "GPENCIL_OT_paintmode_toggle";
+ ot->description = "Enter/Exit paint mode for Grease Pencil strokes";
- /* callbacks */
- ot->exec = gpencil_paintmode_toggle_exec;
- ot->poll = gpencil_paintmode_toggle_poll;
+ /* callbacks */
+ ot->exec = gpencil_paintmode_toggle_exec;
+ ot->poll = gpencil_paintmode_toggle_poll;
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
- /* properties */
- prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ /* properties */
+ prop = RNA_def_boolean(
+ ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* Stroke Sculpt Mode Management */
static bool gpencil_sculptmode_toggle_poll(bContext *C)
{
- /* if using gpencil object, use this gpd */
- Object *ob = CTX_data_active_object(C);
- if ((ob) && (ob->type == OB_GPENCIL)) {
- return ob->data != NULL;
- }
- return ED_gpencil_data_get_active(C) != NULL;
+ /* if using gpencil object, use this gpd */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ return ob->data != NULL;
+ }
+ return ED_gpencil_data_get_active(C) != NULL;
}
static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
{
- const bool back = RNA_boolean_get(op->ptr, "back");
-
- struct wmMsgBus *mbus = CTX_wm_message_bus(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bool is_object = false;
- short mode;
- /* if using a gpencil object, use this datablock */
- Object *ob = CTX_data_active_object(C);
- if ((ob) && (ob->type == OB_GPENCIL)) {
- gpd = ob->data;
- is_object = true;
- }
-
- if (gpd == NULL)
- return OPERATOR_CANCELLED;
-
- /* Just toggle sculptmode flag... */
- gpd->flag ^= GP_DATA_STROKE_SCULPTMODE;
- /* set mode */
- if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) {
- mode = OB_MODE_SCULPT_GPENCIL;
- }
- else {
- mode = OB_MODE_OBJECT;
- }
-
- if (is_object) {
- /* try to back previous mode */
- if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) {
- mode = ob->restore_mode;
- }
- ob->restore_mode = ob->mode;
- ob->mode = mode;
- }
-
- /* setup other modes */
- ED_gpencil_setup_modes(C, gpd, mode);
- /* set cache as dirty */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
-
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
- WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
-
- if (is_object) {
- WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
- }
- if (G.background == false) {
- WM_toolsystem_update_from_context_view3d(C);
- }
-
- return OPERATOR_FINISHED;
+ const bool back = RNA_boolean_get(op->ptr, "back");
+
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bool is_object = false;
+ short mode;
+ /* if using a gpencil object, use this datablock */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ gpd = ob->data;
+ is_object = true;
+ }
+
+ if (gpd == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* Just toggle sculptmode flag... */
+ gpd->flag ^= GP_DATA_STROKE_SCULPTMODE;
+ /* set mode */
+ if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) {
+ mode = OB_MODE_SCULPT_GPENCIL;
+ }
+ else {
+ mode = OB_MODE_OBJECT;
+ }
+
+ if (is_object) {
+ /* try to back previous mode */
+ if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) {
+ mode = ob->restore_mode;
+ }
+ ob->restore_mode = ob->mode;
+ ob->mode = mode;
+ }
+
+ /* setup other modes */
+ ED_gpencil_setup_modes(C, gpd, mode);
+ /* set cache as dirty */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
+
+ if (is_object) {
+ WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
+ }
+ if (G.background == false) {
+ WM_toolsystem_update_from_context_view3d(C);
+ }
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_sculptmode_toggle(wmOperatorType *ot)
{
- PropertyRNA *prop;
+ PropertyRNA *prop;
- /* identifiers */
- ot->name = "Strokes Sculpt Mode Toggle";
- ot->idname = "GPENCIL_OT_sculptmode_toggle";
- ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes";
+ /* identifiers */
+ ot->name = "Strokes Sculpt Mode Toggle";
+ ot->idname = "GPENCIL_OT_sculptmode_toggle";
+ ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes";
- /* callbacks */
- ot->exec = gpencil_sculptmode_toggle_exec;
- ot->poll = gpencil_sculptmode_toggle_poll;
+ /* callbacks */
+ ot->exec = gpencil_sculptmode_toggle_exec;
+ ot->poll = gpencil_sculptmode_toggle_poll;
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
- /* properties */
- prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ /* properties */
+ prop = RNA_def_boolean(
+ ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* Stroke Weight Paint Mode Management */
static bool gpencil_weightmode_toggle_poll(bContext *C)
{
- /* if using gpencil object, use this gpd */
- Object *ob = CTX_data_active_object(C);
- if ((ob) && (ob->type == OB_GPENCIL)) {
- return ob->data != NULL;
- }
- return ED_gpencil_data_get_active(C) != NULL;
+ /* if using gpencil object, use this gpd */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ return ob->data != NULL;
+ }
+ return ED_gpencil_data_get_active(C) != NULL;
}
static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
{
- const bool back = RNA_boolean_get(op->ptr, "back");
-
- struct wmMsgBus *mbus = CTX_wm_message_bus(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bool is_object = false;
- short mode;
- /* if using a gpencil object, use this datablock */
- Object *ob = CTX_data_active_object(C);
- if ((ob) && (ob->type == OB_GPENCIL)) {
- gpd = ob->data;
- is_object = true;
- }
-
- if (gpd == NULL)
- return OPERATOR_CANCELLED;
-
- /* Just toggle weightmode flag... */
- gpd->flag ^= GP_DATA_STROKE_WEIGHTMODE;
- /* set mode */
- if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) {
- mode = OB_MODE_WEIGHT_GPENCIL;
- }
- else {
- mode = OB_MODE_OBJECT;
- }
-
- if (is_object) {
- /* try to back previous mode */
- if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) {
- mode = ob->restore_mode;
- }
- ob->restore_mode = ob->mode;
- ob->mode = mode;
- }
-
- /* setup other modes */
- ED_gpencil_setup_modes(C, gpd, mode);
- /* set cache as dirty */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
-
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
- WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
-
- if (is_object) {
- WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
- }
- if (G.background == false) {
- WM_toolsystem_update_from_context_view3d(C);
- }
-
- return OPERATOR_FINISHED;
+ const bool back = RNA_boolean_get(op->ptr, "back");
+
+ struct wmMsgBus *mbus = CTX_wm_message_bus(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bool is_object = false;
+ short mode;
+ /* if using a gpencil object, use this datablock */
+ Object *ob = CTX_data_active_object(C);
+ if ((ob) && (ob->type == OB_GPENCIL)) {
+ gpd = ob->data;
+ is_object = true;
+ }
+
+ if (gpd == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* Just toggle weightmode flag... */
+ gpd->flag ^= GP_DATA_STROKE_WEIGHTMODE;
+ /* set mode */
+ if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) {
+ mode = OB_MODE_WEIGHT_GPENCIL;
+ }
+ else {
+ mode = OB_MODE_OBJECT;
+ }
+
+ if (is_object) {
+ /* try to back previous mode */
+ if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) {
+ mode = ob->restore_mode;
+ }
+ ob->restore_mode = ob->mode;
+ ob->mode = mode;
+ }
+
+ /* setup other modes */
+ ED_gpencil_setup_modes(C, gpd, mode);
+ /* set cache as dirty */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | ND_GPENCIL_EDITMODE, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
+
+ if (is_object) {
+ WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
+ }
+ if (G.background == false) {
+ WM_toolsystem_update_from_context_view3d(C);
+ }
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot)
{
- PropertyRNA *prop;
+ PropertyRNA *prop;
- /* identifiers */
- ot->name = "Strokes Weight Mode Toggle";
- ot->idname = "GPENCIL_OT_weightmode_toggle";
- ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes";
+ /* identifiers */
+ ot->name = "Strokes Weight Mode Toggle";
+ ot->idname = "GPENCIL_OT_weightmode_toggle";
+ ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes";
- /* callbacks */
- ot->exec = gpencil_weightmode_toggle_exec;
- ot->poll = gpencil_weightmode_toggle_poll;
+ /* callbacks */
+ ot->exec = gpencil_weightmode_toggle_exec;
+ ot->poll = gpencil_weightmode_toggle_poll;
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
- /* properties */
- prop = RNA_def_boolean(ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
- RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
+ /* properties */
+ prop = RNA_def_boolean(
+ ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
+ RNA_def_property_flag(prop, PROP_HIDDEN | PROP_SKIP_SAVE);
}
/* ************************************************ */
@@ -541,440 +544,437 @@ void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot)
static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op))
{
- View3D *v3d = CTX_wm_view3d(C);
- if (v3d == NULL)
- return OPERATOR_CANCELLED;
-
- /* Just toggle alpha... */
- if (v3d->vertex_opacity > 0.0f) {
- v3d->vertex_opacity = 0.0f;
- }
- else {
- v3d->vertex_opacity = 1.0f;
- }
-
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
- WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL);
- WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
-
- return OPERATOR_FINISHED;
+ View3D *v3d = CTX_wm_view3d(C);
+ if (v3d == NULL)
+ return OPERATOR_CANCELLED;
+
+ /* Just toggle alpha... */
+ if (v3d->vertex_opacity > 0.0f) {
+ v3d->vertex_opacity = 0.0f;
+ }
+ else {
+ v3d->vertex_opacity = 1.0f;
+ }
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_GPENCIL_EDITMODE, NULL);
+ WM_event_add_notifier(C, NC_SCENE | ND_MODE, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Hide Selected";
- ot->idname = "GPENCIL_OT_selection_opacity_toggle";
- ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
+ /* identifiers */
+ ot->name = "Hide Selected";
+ ot->idname = "GPENCIL_OT_selection_opacity_toggle";
+ ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
- /* callbacks */
- ot->exec = gpencil_hideselect_toggle_exec;
- ot->poll = gp_stroke_edit_poll;
+ /* callbacks */
+ ot->exec = gpencil_hideselect_toggle_exec;
+ ot->poll = gp_stroke_edit_poll;
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
}
/* ************** Duplicate Selected Strokes **************** */
/* Make copies of selected point segments in a selected stroke */
-static void gp_duplicate_points(const bGPDstroke *gps, ListBase *new_strokes, const char *layername)
+static void gp_duplicate_points(const bGPDstroke *gps,
+ ListBase *new_strokes,
+ const char *layername)
{
- bGPDspoint *pt;
- int i;
-
- int start_idx = -1;
-
-
- /* Step through the original stroke's points:
- * - We accumulate selected points (from start_idx to current index)
- * and then convert that to a new stroke
- */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- /* searching for start, are waiting for end? */
- if (start_idx == -1) {
- /* is this the first selected point for a new island? */
- if (pt->flag & GP_SPOINT_SELECT) {
- start_idx = i;
- }
- }
- else {
- size_t len = 0;
-
- /* is this the end of current island yet?
- * 1) Point i-1 was the last one that was selected
- * 2) Point i is the last in the array
- */
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- len = i - start_idx;
- }
- else if (i == gps->totpoints - 1) {
- len = i - start_idx + 1;
- }
- //printf("copying from %d to %d = %d\n", start_idx, i, len);
-
- /* make copies of the relevant data */
- if (len) {
- bGPDstroke *gpsd;
-
- /* make a stupid copy first of the entire stroke (to get the flags too) */
- gpsd = MEM_dupallocN(gps);
-
- /* saves original layer name */
- BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
-
- /* initialize triangle memory - will be calculated on next redraw */
- gpsd->triangles = NULL;
- gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
- gpsd->tot_triangles = 0;
-
- /* now, make a new points array, and copy of the relevant parts */
- gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
- memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
- gpsd->totpoints = len;
-
- if (gps->dvert != NULL) {
- gpsd->dvert = MEM_callocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
- memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
-
- /* Copy weights */
- int e = start_idx;
- for (int j = 0; j < gpsd->totpoints; j++) {
- MDeformVert *dvert_dst = &gps->dvert[e];
- MDeformVert *dvert_src = &gps->dvert[j];
- dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
- e++;
- }
- }
-
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
- BLI_addtail(new_strokes, gpsd);
-
- /* cleanup + reset for next */
- start_idx = -1;
- }
- }
- }
+ bGPDspoint *pt;
+ int i;
+
+ int start_idx = -1;
+
+ /* Step through the original stroke's points:
+ * - We accumulate selected points (from start_idx to current index)
+ * and then convert that to a new stroke
+ */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ /* searching for start, are waiting for end? */
+ if (start_idx == -1) {
+ /* is this the first selected point for a new island? */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ start_idx = i;
+ }
+ }
+ else {
+ size_t len = 0;
+
+ /* is this the end of current island yet?
+ * 1) Point i-1 was the last one that was selected
+ * 2) Point i is the last in the array
+ */
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ len = i - start_idx;
+ }
+ else if (i == gps->totpoints - 1) {
+ len = i - start_idx + 1;
+ }
+ //printf("copying from %d to %d = %d\n", start_idx, i, len);
+
+ /* make copies of the relevant data */
+ if (len) {
+ bGPDstroke *gpsd;
+
+ /* make a stupid copy first of the entire stroke (to get the flags too) */
+ gpsd = MEM_dupallocN(gps);
+
+ /* saves original layer name */
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
+
+ /* initialize triangle memory - will be calculated on next redraw */
+ gpsd->triangles = NULL;
+ gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gpsd->tot_triangles = 0;
+
+ /* now, make a new points array, and copy of the relevant parts */
+ gpsd->points = MEM_callocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
+ memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
+ gpsd->totpoints = len;
+
+ if (gps->dvert != NULL) {
+ gpsd->dvert = MEM_callocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
+ memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
+
+ /* Copy weights */
+ int e = start_idx;
+ for (int j = 0; j < gpsd->totpoints; j++) {
+ MDeformVert *dvert_dst = &gps->dvert[e];
+ MDeformVert *dvert_src = &gps->dvert[j];
+ dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ e++;
+ }
+ }
+
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(new_strokes, gpsd);
+
+ /* cleanup + reset for next */
+ start_idx = -1;
+ }
+ }
+ }
}
static int gp_duplicate_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
-
- if (gpd == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
- return OPERATOR_CANCELLED;
- }
-
- if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
- return OPERATOR_CANCELLED;
- }
-
- /* for each visible (and editable) layer's selected strokes,
- * copy the strokes into a temporary buffer, then append
- * once all done
- */
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- ListBase new_strokes = {NULL, NULL};
- bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps;
-
- if (gpf == NULL)
- continue;
-
- /* make copies of selected strokes, and deselect these once we're done */
- 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;
- }
-
- if (gps->flag & GP_STROKE_SELECT) {
- if (gps->totpoints == 1) {
- /* Special Case: If there's just a single point in this stroke... */
- bGPDstroke *gpsd;
-
- /* make direct copies of the stroke and its points */
- gpsd = MEM_dupallocN(gps);
- BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- gpsd->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- gpsd->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
- }
-
- /* triangle information - will be calculated on next redraw */
- gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
- gpsd->triangles = NULL;
-
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
- BLI_addtail(&new_strokes, gpsd);
- }
- else {
- /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gp_duplicate_points(gps, &new_strokes, gpl->info);
- }
-
- /* deselect original stroke, or else the originals get moved too
- * (when using the copy + move macro)
- */
- gps->flag &= ~GP_STROKE_SELECT;
- }
- }
-
- /* add all new strokes in temp buffer to the frame (preventing double-copies) */
- BLI_movelisttolist(&gpf->strokes, &new_strokes);
- BLI_assert(new_strokes.first == NULL);
- }
- CTX_DATA_END;
-
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ if (gpd == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
+ return OPERATOR_CANCELLED;
+ }
+
+ if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
+ BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* for each visible (and editable) layer's selected strokes,
+ * copy the strokes into a temporary buffer, then append
+ * once all done
+ */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ ListBase new_strokes = {NULL, NULL};
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+
+ if (gpf == NULL)
+ continue;
+
+ /* make copies of selected strokes, and deselect these once we're done */
+ 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;
+ }
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ if (gps->totpoints == 1) {
+ /* Special Case: If there's just a single point in this stroke... */
+ bGPDstroke *gpsd;
+
+ /* make direct copies of the stroke and its points */
+ gpsd = MEM_dupallocN(gps);
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
+ gpsd->points = MEM_dupallocN(gps->points);
+ if (gps->dvert != NULL) {
+ gpsd->dvert = MEM_dupallocN(gps->dvert);
+ BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
+ }
+
+ /* triangle information - will be calculated on next redraw */
+ gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gpsd->triangles = NULL;
+
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(&new_strokes, gpsd);
+ }
+ else {
+ /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
+ gp_duplicate_points(gps, &new_strokes, gpl->info);
+ }
+
+ /* deselect original stroke, or else the originals get moved too
+ * (when using the copy + move macro)
+ */
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+ }
+
+ /* add all new strokes in temp buffer to the frame (preventing double-copies) */
+ BLI_movelisttolist(&gpf->strokes, &new_strokes);
+ BLI_assert(new_strokes.first == NULL);
+ }
+ CTX_DATA_END;
+
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_duplicate(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Duplicate Strokes";
- ot->idname = "GPENCIL_OT_duplicate";
- ot->description = "Duplicate the selected Grease Pencil strokes";
+ /* identifiers */
+ ot->name = "Duplicate Strokes";
+ ot->idname = "GPENCIL_OT_duplicate";
+ ot->description = "Duplicate the selected Grease Pencil strokes";
- /* callbacks */
- ot->exec = gp_duplicate_exec;
- ot->poll = gp_stroke_edit_poll;
+ /* callbacks */
+ ot->exec = gp_duplicate_exec;
+ ot->poll = gp_stroke_edit_poll;
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ************** Extrude Selected Strokes **************** */
/* helper to copy a point to temp area */
-static void copy_move_point(
- bGPDstroke *gps,
- bGPDspoint *temp_points,
- MDeformVert *temp_dverts,
- int from_idx, int to_idx, const bool copy)
+static void copy_move_point(bGPDstroke *gps,
+ bGPDspoint *temp_points,
+ MDeformVert *temp_dverts,
+ int from_idx,
+ int to_idx,
+ const bool copy)
{
- bGPDspoint *pt = &temp_points[from_idx];
- bGPDspoint *pt_final = &gps->points[to_idx];
-
- copy_v3_v3(&pt_final->x, &pt->x);
- pt_final->pressure = pt->pressure;
- pt_final->strength = pt->strength;
- pt_final->time = pt->time;
- pt_final->flag = pt->flag;
- pt_final->uv_fac = pt->uv_fac;
- pt_final->uv_rot = pt->uv_rot;
-
- if (gps->dvert != NULL) {
- MDeformVert *dvert = &temp_dverts[from_idx];
- MDeformVert *dvert_final = &gps->dvert[to_idx];
-
- dvert_final->totweight = dvert->totweight;
- /* if copy, duplicate memory, otherwise move only the pointer */
- if (copy) {
- dvert_final->dw = MEM_dupallocN(dvert->dw);
- }
- else {
- dvert_final->dw = dvert->dw;
- }
- }
+ bGPDspoint *pt = &temp_points[from_idx];
+ bGPDspoint *pt_final = &gps->points[to_idx];
+
+ copy_v3_v3(&pt_final->x, &pt->x);
+ pt_final->pressure = pt->pressure;
+ pt_final->strength = pt->strength;
+ pt_final->time = pt->time;
+ pt_final->flag = pt->flag;
+ pt_final->uv_fac = pt->uv_fac;
+ pt_final->uv_rot = pt->uv_rot;
+
+ if (gps->dvert != NULL) {
+ MDeformVert *dvert = &temp_dverts[from_idx];
+ MDeformVert *dvert_final = &gps->dvert[to_idx];
+
+ dvert_final->totweight = dvert->totweight;
+ /* if copy, duplicate memory, otherwise move only the pointer */
+ if (copy) {
+ dvert_final->dw = MEM_dupallocN(dvert->dw);
+ }
+ else {
+ dvert_final->dw = dvert->dw;
+ }
+ }
}
static void gpencil_add_move_points(bGPDframe *gpf, bGPDstroke *gps)
{
- bGPDspoint *temp_points = NULL;
- MDeformVert *temp_dverts = NULL;
- bGPDspoint *pt = NULL;
- const bGPDspoint *pt_start = &gps->points[0];
- const bGPDspoint *pt_last = &gps->points[gps->totpoints - 1];
- const bool do_first = (pt_start->flag & GP_SPOINT_SELECT);
- const bool do_last = ((pt_last->flag & GP_SPOINT_SELECT) && (pt_start != pt_last));
- const bool do_stroke = (do_first || do_last);
-
- /* review points in the middle of stroke to create new strokes */
- for (int i = 0; i < gps->totpoints; i++) {
- /* skip first and last point */
- if ((i == 0) || (i == gps->totpoints - 1)) {
- continue;
- }
-
- pt = &gps->points[i];
- if (pt->flag == GP_SPOINT_SELECT) {
- /* duplicate original stroke data */
- bGPDstroke *gps_new = MEM_dupallocN(gps);
- gps_new->prev = gps_new->next = NULL;
-
- /* add new points array */
- gps_new->totpoints = 1;
- gps_new->points = MEM_callocN(sizeof(bGPDspoint), __func__);
- gps_new->dvert = NULL;
-
- if (gps->dvert != NULL) {
- gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__);
- }
-
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps_new->triangles = NULL;
- gps_new->tot_triangles = 0;
- BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
-
- /* copy selected point data to new stroke */
- copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
-
- /* deselect orinal point */
- pt->flag &= ~GP_SPOINT_SELECT;
- }
- }
-
- /* review first and last point to reuse same stroke */
- int i2 = 0;
- int totnewpoints, oldtotpoints;
- /* if first or last, reuse stroke and resize */
- if ((do_first) || (do_last)) {
- totnewpoints = gps->totpoints;
- if (do_first) {
- totnewpoints++;
- }
- if (do_last) {
- totnewpoints++;
- }
-
- /* duplicate points in a temp area */
- temp_points = MEM_dupallocN(gps->points);
- oldtotpoints = gps->totpoints;
- if (gps->dvert != NULL) {
- temp_dverts = MEM_dupallocN(gps->dvert);
- }
-
- /* if first point, need move all one position */
- if (do_first) {
- i2 = 1;
- }
-
- /* resize the points arrays */
- gps->totpoints = totnewpoints;
- gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
- }
-
- /* move points to new position */
- for (int i = 0; i < oldtotpoints; i++) {
- copy_move_point(gps, temp_points, temp_dverts, i, i2, false);
- i2++;
- }
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
-
- /* if first point, add new point at the begining */
- if (do_first) {
- copy_move_point(gps, temp_points, temp_dverts, 0, 0, true);
- /* deselect old */
- pt = &gps->points[1];
- pt->flag &= ~GP_SPOINT_SELECT;
- /* select new */
- pt = &gps->points[0];
- pt->flag |= GP_SPOINT_SELECT;
- }
-
- /* if last point, add new point at the end */
- if (do_last) {
- copy_move_point(
- gps, temp_points, temp_dverts,
- oldtotpoints - 1, gps->totpoints - 1, true);
-
- /* deselect old */
- pt = &gps->points[gps->totpoints - 2];
- pt->flag &= ~GP_SPOINT_SELECT;
- /* select new */
- pt = &gps->points[gps->totpoints - 1];
- pt->flag |= GP_SPOINT_SELECT;
- }
-
- MEM_SAFE_FREE(temp_points);
- MEM_SAFE_FREE(temp_dverts);
- }
-
- /* if the stroke is not reused, deselect */
- if (!do_stroke) {
- gps->flag &= ~GP_STROKE_SELECT;
- }
+ bGPDspoint *temp_points = NULL;
+ MDeformVert *temp_dverts = NULL;
+ bGPDspoint *pt = NULL;
+ const bGPDspoint *pt_start = &gps->points[0];
+ const bGPDspoint *pt_last = &gps->points[gps->totpoints - 1];
+ const bool do_first = (pt_start->flag & GP_SPOINT_SELECT);
+ const bool do_last = ((pt_last->flag & GP_SPOINT_SELECT) && (pt_start != pt_last));
+ const bool do_stroke = (do_first || do_last);
+
+ /* review points in the middle of stroke to create new strokes */
+ for (int i = 0; i < gps->totpoints; i++) {
+ /* skip first and last point */
+ if ((i == 0) || (i == gps->totpoints - 1)) {
+ continue;
+ }
+
+ pt = &gps->points[i];
+ if (pt->flag == GP_SPOINT_SELECT) {
+ /* duplicate original stroke data */
+ bGPDstroke *gps_new = MEM_dupallocN(gps);
+ gps_new->prev = gps_new->next = NULL;
+
+ /* add new points array */
+ gps_new->totpoints = 1;
+ gps_new->points = MEM_callocN(sizeof(bGPDspoint), __func__);
+ gps_new->dvert = NULL;
+
+ if (gps->dvert != NULL) {
+ gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__);
+ }
+
+ gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps_new->triangles = NULL;
+ gps_new->tot_triangles = 0;
+ BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
+
+ /* copy selected point data to new stroke */
+ copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
+
+ /* deselect orinal point */
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ }
+
+ /* review first and last point to reuse same stroke */
+ int i2 = 0;
+ int totnewpoints, oldtotpoints;
+ /* if first or last, reuse stroke and resize */
+ if ((do_first) || (do_last)) {
+ totnewpoints = gps->totpoints;
+ if (do_first) {
+ totnewpoints++;
+ }
+ if (do_last) {
+ totnewpoints++;
+ }
+
+ /* duplicate points in a temp area */
+ temp_points = MEM_dupallocN(gps->points);
+ oldtotpoints = gps->totpoints;
+ if (gps->dvert != NULL) {
+ temp_dverts = MEM_dupallocN(gps->dvert);
+ }
+
+ /* if first point, need move all one position */
+ if (do_first) {
+ i2 = 1;
+ }
+
+ /* resize the points arrays */
+ gps->totpoints = totnewpoints;
+ gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
+ if (gps->dvert != NULL) {
+ gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
+ }
+
+ /* move points to new position */
+ for (int i = 0; i < oldtotpoints; i++) {
+ copy_move_point(gps, temp_points, temp_dverts, i, i2, false);
+ i2++;
+ }
+ gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+
+ /* if first point, add new point at the begining */
+ if (do_first) {
+ copy_move_point(gps, temp_points, temp_dverts, 0, 0, true);
+ /* deselect old */
+ pt = &gps->points[1];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ /* select new */
+ pt = &gps->points[0];
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+
+ /* if last point, add new point at the end */
+ if (do_last) {
+ copy_move_point(gps, temp_points, temp_dverts, oldtotpoints - 1, gps->totpoints - 1, true);
+
+ /* deselect old */
+ pt = &gps->points[gps->totpoints - 2];
+ pt->flag &= ~GP_SPOINT_SELECT;
+ /* select new */
+ pt = &gps->points[gps->totpoints - 1];
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+
+ MEM_SAFE_FREE(temp_points);
+ MEM_SAFE_FREE(temp_dverts);
+ }
+
+ /* if the stroke is not reused, deselect */
+ if (!do_stroke) {
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
}
static int gp_extrude_exec(bContext *C, wmOperator *op)
{
- Object *obact = CTX_data_active_object(C);
- bGPdata *gpd = (bGPdata *)obact->data;
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- bGPDstroke *gps = NULL;
-
- if (gpd == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
- return OPERATOR_CANCELLED;
- }
-
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
-
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- if (gpf == NULL)
- continue;
-
- 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;
-
- if (gps->flag & GP_STROKE_SELECT) {
- gpencil_add_move_points(gpf, gps);
- }
- }
- /* if not multiedit, exit loop*/
- if (!is_multiedit) {
- break;
- }
- }
- }
- }
- CTX_DATA_END;
-
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
- DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ Object *obact = CTX_data_active_object(C);
+ bGPdata *gpd = (bGPdata *)obact->data;
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ bGPDstroke *gps = NULL;
+
+ if (gpd == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
+ return OPERATOR_CANCELLED;
+ }
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = gpl->actframe;
+ if (is_multiedit) {
+ init_gpf = gpl->frames.first;
+ }
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ if (gpf == NULL)
+ continue;
+
+ 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;
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ gpencil_add_move_points(gpf, gps);
+ }
+ }
+ /* if not multiedit, exit loop*/
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_extrude(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Extrude Stroke Points";
- ot->idname = "GPENCIL_OT_extrude";
- ot->description = "Extrude the selected Grease Pencil points";
+ /* identifiers */
+ ot->name = "Extrude Stroke Points";
+ ot->idname = "GPENCIL_OT_extrude";
+ ot->description = "Extrude the selected Grease Pencil points";
- /* callbacks */
- ot->exec = gp_extrude_exec;
- ot->poll = gp_stroke_edit_poll;
+ /* callbacks */
+ ot->exec = gp_extrude_exec;
+ ot->poll = gp_stroke_edit_poll;
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
-
/* ******************* Copy/Paste Strokes ************************* */
/* Grease Pencil stroke data copy/paste buffer:
* - The copy operation collects all segments of selected strokes,
@@ -984,8 +984,8 @@ void GPENCIL_OT_extrude(wmOperatorType *ot)
* from several different layers into a single layer.
*/
- /* list of bGPDstroke instances */
- /* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */
+/* list of bGPDstroke instances */
+/* NOTE: is exposed within the editors/gpencil module so that other tools can use it too */
ListBase gp_strokes_copypastebuf = {NULL, NULL};
/* Hash for hanging on to all the colors used by strokes in the buffer
@@ -997,69 +997,69 @@ static GHash *gp_strokes_copypastebuf_colors = NULL;
static GHash *gp_strokes_copypastebuf_colors_material_to_name_create(Main *bmain)
{
- GHash *ma_to_name = BLI_ghash_ptr_new(__func__);
+ GHash *ma_to_name = BLI_ghash_ptr_new(__func__);
- for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) {
- char *name = BKE_id_to_unique_string_key(&ma->id);
- BLI_ghash_insert(ma_to_name, ma, name);
- }
+ for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) {
+ char *name = BKE_id_to_unique_string_key(&ma->id);
+ BLI_ghash_insert(ma_to_name, ma, name);
+ }
- return ma_to_name;
+ return ma_to_name;
}
static void gp_strokes_copypastebuf_colors_material_to_name_free(GHash *ma_to_name)
{
- BLI_ghash_free(ma_to_name, NULL, MEM_freeN);
+ BLI_ghash_free(ma_to_name, NULL, MEM_freeN);
}
static GHash *gp_strokes_copypastebuf_colors_name_to_material_create(Main *bmain)
{
- GHash *name_to_ma = BLI_ghash_str_new(__func__);
+ GHash *name_to_ma = BLI_ghash_str_new(__func__);
- for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) {
- char *name = BKE_id_to_unique_string_key(&ma->id);
- BLI_ghash_insert(name_to_ma, name, ma);
- }
+ for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) {
+ char *name = BKE_id_to_unique_string_key(&ma->id);
+ BLI_ghash_insert(name_to_ma, name, ma);
+ }
- return name_to_ma;
+ return name_to_ma;
}
static void gp_strokes_copypastebuf_colors_name_to_material_free(GHash *name_to_ma)
{
- BLI_ghash_free(name_to_ma, MEM_freeN, NULL);
+ BLI_ghash_free(name_to_ma, MEM_freeN, NULL);
}
/* Free copy/paste buffer data */
void ED_gpencil_strokes_copybuf_free(void)
{
- bGPDstroke *gps, *gpsn;
-
- /* Free the colors buffer
- * NOTE: This is done before the strokes so that the ptrs are still safe
- */
- if (gp_strokes_copypastebuf_colors) {
- BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN);
- gp_strokes_copypastebuf_colors = NULL;
- }
-
- /* Free the stroke buffer */
- for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
-
- MEM_SAFE_FREE(gps->triangles);
-
- BLI_freelinkN(&gp_strokes_copypastebuf, gps);
- }
-
- gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL;
+ bGPDstroke *gps, *gpsn;
+
+ /* Free the colors buffer
+ * NOTE: This is done before the strokes so that the ptrs are still safe
+ */
+ if (gp_strokes_copypastebuf_colors) {
+ BLI_ghash_free(gp_strokes_copypastebuf_colors, NULL, MEM_freeN);
+ gp_strokes_copypastebuf_colors = NULL;
+ }
+
+ /* Free the stroke buffer */
+ for (gps = gp_strokes_copypastebuf.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+
+ if (gps->points) {
+ MEM_freeN(gps->points);
+ }
+ if (gps->dvert) {
+ BKE_gpencil_free_stroke_weights(gps);
+ MEM_freeN(gps->dvert);
+ }
+
+ MEM_SAFE_FREE(gps->triangles);
+
+ BLI_freelinkN(&gp_strokes_copypastebuf, gps);
+ }
+
+ gp_strokes_copypastebuf.first = gp_strokes_copypastebuf.last = NULL;
}
/* Ensure that destination datablock has all the colours the pasted strokes need
@@ -1067,30 +1067,30 @@ void ED_gpencil_strokes_copybuf_free(void)
*/
GHash *gp_copybuf_validate_colormap(bContext *C)
{
- Main *bmain = CTX_data_main(C);
- Object *ob = CTX_data_active_object(C);
- GHash *new_colors = BLI_ghash_int_new("GPencil Paste Dst Colors");
- GHashIterator gh_iter;
+ Main *bmain = CTX_data_main(C);
+ Object *ob = CTX_data_active_object(C);
+ GHash *new_colors = BLI_ghash_int_new("GPencil Paste Dst Colors");
+ GHashIterator gh_iter;
- /* For each color, check if exist and add if not */
- GHash *name_to_ma = gp_strokes_copypastebuf_colors_name_to_material_create(bmain);
+ /* For each color, check if exist and add if not */
+ GHash *name_to_ma = gp_strokes_copypastebuf_colors_name_to_material_create(bmain);
- GHASH_ITER(gh_iter, gp_strokes_copypastebuf_colors) {
- int *key = BLI_ghashIterator_getKey(&gh_iter);
- char *ma_name = BLI_ghashIterator_getValue(&gh_iter);
- Material *ma = BLI_ghash_lookup(name_to_ma, ma_name);
+ GHASH_ITER (gh_iter, gp_strokes_copypastebuf_colors) {
+ int *key = BLI_ghashIterator_getKey(&gh_iter);
+ char *ma_name = BLI_ghashIterator_getValue(&gh_iter);
+ Material *ma = BLI_ghash_lookup(name_to_ma, ma_name);
- BKE_gpencil_object_material_ensure(bmain, ob, ma);
+ BKE_gpencil_object_material_ensure(bmain, ob, ma);
- /* Store this mapping (for use later when pasting) */
- if (!BLI_ghash_haskey(new_colors, POINTER_FROM_INT(*key))) {
- BLI_ghash_insert(new_colors, POINTER_FROM_INT(*key), ma);
- }
- }
+ /* Store this mapping (for use later when pasting) */
+ if (!BLI_ghash_haskey(new_colors, POINTER_FROM_INT(*key))) {
+ BLI_ghash_insert(new_colors, POINTER_FROM_INT(*key), ma);
+ }
+ }
- gp_strokes_copypastebuf_colors_name_to_material_free(name_to_ma);
+ gp_strokes_copypastebuf_colors_name_to_material_free(name_to_ma);
- return new_colors;
+ return new_colors;
}
/* --------------------- */
@@ -1098,111 +1098,111 @@ GHash *gp_copybuf_validate_colormap(bContext *C)
static int gp_strokes_copy_exec(bContext *C, wmOperator *op)
{
- Main *bmain = CTX_data_main(C);
- Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
-
- if (gpd == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
- return OPERATOR_CANCELLED;
- }
-
- if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
- return OPERATOR_CANCELLED;
- }
-
- /* clear the buffer first */
- ED_gpencil_strokes_copybuf_free();
-
- /* for each visible (and editable) layer's selected strokes,
- * copy the strokes into a temporary buffer, then append
- * once all done
- */
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps;
-
- if (gpf == NULL)
- continue;
-
- /* make copies of selected strokes, and deselect these once we're done */
- 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;
-
- if (gps->flag & GP_STROKE_SELECT) {
- if (gps->totpoints == 1) {
- /* Special Case: If there's just a single point in this stroke... */
- bGPDstroke *gpsd;
-
- /* make direct copies of the stroke and its points */
- gpsd = MEM_dupallocN(gps);
- /* saves original layer name */
- BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
- gpsd->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- gpsd->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
- }
-
- /* triangles cache - will be recalculated on next redraw */
- gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
- gpsd->tot_triangles = 0;
- gpsd->triangles = NULL;
-
- /* add to temp buffer */
- gpsd->next = gpsd->prev = NULL;
- BLI_addtail(&gp_strokes_copypastebuf, gpsd);
- }
- else {
- /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
- gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
- }
- }
- }
- }
- CTX_DATA_END;
-
- /* Build up hash of material colors used in these strokes */
- if (gp_strokes_copypastebuf.first) {
- gp_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors");
- GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain);
- for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- char **ma_name_val;
- if (!BLI_ghash_ensure_p(gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
- Material *ma = give_current_material(ob, gps->mat_nr + 1);
- char *ma_name = BLI_ghash_lookup(ma_to_name, ma);
- *ma_name_val = MEM_dupallocN(ma_name);
- }
- }
- }
- gp_strokes_copypastebuf_colors_material_to_name_free(ma_to_name);
- }
-
- /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); // XXX?
-
- /* done */
- return OPERATOR_FINISHED;
+ Main *bmain = CTX_data_main(C);
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ if (gpd == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
+ return OPERATOR_CANCELLED;
+ }
+
+ if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
+ BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* clear the buffer first */
+ ED_gpencil_strokes_copybuf_free();
+
+ /* for each visible (and editable) layer's selected strokes,
+ * copy the strokes into a temporary buffer, then append
+ * once all done
+ */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps;
+
+ if (gpf == NULL)
+ continue;
+
+ /* make copies of selected strokes, and deselect these once we're done */
+ 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;
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ if (gps->totpoints == 1) {
+ /* Special Case: If there's just a single point in this stroke... */
+ bGPDstroke *gpsd;
+
+ /* make direct copies of the stroke and its points */
+ gpsd = MEM_dupallocN(gps);
+ /* saves original layer name */
+ BLI_strncpy(gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
+ gpsd->points = MEM_dupallocN(gps->points);
+ if (gps->dvert != NULL) {
+ gpsd->dvert = MEM_dupallocN(gps->dvert);
+ BKE_gpencil_stroke_weights_duplicate(gps, gpsd);
+ }
+
+ /* triangles cache - will be recalculated on next redraw */
+ gpsd->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gpsd->tot_triangles = 0;
+ gpsd->triangles = NULL;
+
+ /* add to temp buffer */
+ gpsd->next = gpsd->prev = NULL;
+ BLI_addtail(&gp_strokes_copypastebuf, gpsd);
+ }
+ else {
+ /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
+ gp_duplicate_points(gps, &gp_strokes_copypastebuf, gpl->info);
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* Build up hash of material colors used in these strokes */
+ if (gp_strokes_copypastebuf.first) {
+ gp_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors");
+ GHash *ma_to_name = gp_strokes_copypastebuf_colors_material_to_name_create(bmain);
+ for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
+ if (ED_gpencil_stroke_can_use(C, gps)) {
+ char **ma_name_val;
+ if (!BLI_ghash_ensure_p(
+ gp_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
+ Material *ma = give_current_material(ob, gps->mat_nr + 1);
+ char *ma_name = BLI_ghash_lookup(ma_to_name, ma);
+ *ma_name_val = MEM_dupallocN(ma_name);
+ }
+ }
+ }
+ gp_strokes_copypastebuf_colors_material_to_name_free(ma_to_name);
+ }
+
+ /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); // XXX?
+
+ /* done */
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_copy(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Copy Strokes";
- ot->idname = "GPENCIL_OT_copy";
- ot->description = "Copy selected Grease Pencil points and strokes";
+ /* identifiers */
+ ot->name = "Copy Strokes";
+ ot->idname = "GPENCIL_OT_copy";
+ ot->description = "Copy selected Grease Pencil points and strokes";
- /* callbacks */
- ot->exec = gp_strokes_copy_exec;
- ot->poll = gp_stroke_edit_poll;
+ /* callbacks */
+ ot->exec = gp_strokes_copy_exec;
+ ot->poll = gp_stroke_edit_poll;
- /* flags */
- //ot->flag = OPTYPE_REGISTER;
+ /* flags */
+ //ot->flag = OPTYPE_REGISTER;
}
/* --------------------- */
@@ -1210,299 +1210,302 @@ void GPENCIL_OT_copy(wmOperatorType *ot)
static bool gp_strokes_paste_poll(bContext *C)
{
- /* 1) Must have GP datablock to paste to
- * - We don't need to have an active layer though, as that can easily get added
- * - If the active layer is locked, we can't paste there, but that should prompt a warning instead
- * 2) Copy buffer must at least have something (though it may be the wrong sort...)
- */
- return (ED_gpencil_data_get_active(C) != NULL) && (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
+ /* 1) Must have GP datablock to paste to
+ * - We don't need to have an active layer though, as that can easily get added
+ * - If the active layer is locked, we can't paste there, but that should prompt a warning instead
+ * 2) Copy buffer must at least have something (though it may be the wrong sort...)
+ */
+ return (ED_gpencil_data_get_active(C) != NULL) &&
+ (!BLI_listbase_is_empty(&gp_strokes_copypastebuf));
}
typedef enum eGP_PasteMode {
- GP_COPY_ONLY = -1,
- GP_COPY_MERGE = 1,
+ GP_COPY_ONLY = -1,
+ GP_COPY_MERGE = 1,
} eGP_PasteMode;
static int gp_strokes_paste_exec(bContext *C, wmOperator *op)
{
- Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- int cfra_eval = (int)DEG_get_ctime(depsgraph);
- bGPDframe *gpf;
-
- eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
- GHash *new_colors;
-
- /* check for various error conditions */
- if (gpd == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
- return OPERATOR_CANCELLED;
- }
- else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
- return OPERATOR_CANCELLED;
- }
- else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
- BKE_report(op->reports, RPT_ERROR, "No strokes to paste, select and copy some points before trying again");
- return OPERATOR_CANCELLED;
- }
- else if (gpl == NULL) {
- /* no active layer - let's just create one */
- gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
- }
- else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
- BKE_report(op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
- return OPERATOR_CANCELLED;
- }
- else {
- /* Check that some of the strokes in the buffer can be used */
- bGPDstroke *gps;
- bool ok = false;
-
- for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- ok = true;
- break;
- }
- }
-
- if (ok == false) {
- /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes),
- * but should be enough to give users a good idea of what's going on
- */
- if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D)
- BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View");
- else
- BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors");
-
- return OPERATOR_CANCELLED;
- }
- }
-
- /* Deselect all strokes first */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- bGPDspoint *pt;
- int i;
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
-
- gps->flag &= ~GP_STROKE_SELECT;
- }
- CTX_DATA_END;
-
- /* Ensure that all the necessary colors exist */
- new_colors = gp_copybuf_validate_colormap(C);
-
- /* Copy over the strokes from the buffer (and adjust the colors) */
- for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
- if (ED_gpencil_stroke_can_use(C, gps)) {
- /* Need to verify if layer exists */
- if (type != GP_COPY_MERGE) {
- gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
- if (gpl == NULL) {
- /* no layer - use active (only if layer deleted before paste) */
- gpl = CTX_data_active_gpencil_layer(C);
- }
- }
-
- /* Ensure we have a frame to draw into
- * NOTE: Since this is an op which creates strokes,
- * we are obliged to add a new frame if one
- * doesn't exist already
- */
- gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
- if (gpf) {
- /* Create new stroke */
- bGPDstroke *new_stroke = MEM_dupallocN(gps);
- new_stroke->runtime.tmp_layerinfo[0] = '\0';
-
- new_stroke->points = MEM_dupallocN(gps->points);
- if (gps->dvert != NULL) {
- new_stroke->dvert = MEM_dupallocN(gps->dvert);
- BKE_gpencil_stroke_weights_duplicate(gps, new_stroke);
- }
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
- new_stroke->triangles = NULL;
-
- new_stroke->next = new_stroke->prev = NULL;
- BLI_addtail(&gpf->strokes, new_stroke);
-
- /* Remap material */
- Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
- new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma);
- BLI_assert(new_stroke->mat_nr >= 0); /* have to add the material first */
- }
- }
- }
-
- /* free temp data */
- BLI_ghash_free(new_colors, NULL, NULL);
-
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = CTX_data_active_gpencil_layer(C); /* only use active for copy merge */
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ int cfra_eval = (int)DEG_get_ctime(depsgraph);
+ bGPDframe *gpf;
+
+ eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
+ GHash *new_colors;
+
+ /* check for various error conditions */
+ if (gpd == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
+ return OPERATOR_CANCELLED;
+ }
+ else if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
+ BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
+ return OPERATOR_CANCELLED;
+ }
+ else if (BLI_listbase_is_empty(&gp_strokes_copypastebuf)) {
+ BKE_report(op->reports,
+ RPT_ERROR,
+ "No strokes to paste, select and copy some points before trying again");
+ return OPERATOR_CANCELLED;
+ }
+ else if (gpl == NULL) {
+ /* no active layer - let's just create one */
+ gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ }
+ else if ((gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_MERGE)) {
+ BKE_report(
+ op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
+ return OPERATOR_CANCELLED;
+ }
+ else {
+ /* Check that some of the strokes in the buffer can be used */
+ bGPDstroke *gps;
+ bool ok = false;
+
+ for (gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
+ if (ED_gpencil_stroke_can_use(C, gps)) {
+ ok = true;
+ break;
+ }
+ }
+
+ if (ok == false) {
+ /* XXX: this check is not 100% accurate (i.e. image editor is incompatible with normal 2D strokes),
+ * but should be enough to give users a good idea of what's going on
+ */
+ if (CTX_wm_area(C)->spacetype == SPACE_VIEW3D)
+ BKE_report(op->reports, RPT_ERROR, "Cannot paste 2D strokes in 3D View");
+ else
+ BKE_report(op->reports, RPT_ERROR, "Cannot paste 3D strokes in 2D editors");
+
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ /* Deselect all strokes first */
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ bGPDspoint *pt;
+ int i;
+
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+ CTX_DATA_END;
+
+ /* Ensure that all the necessary colors exist */
+ new_colors = gp_copybuf_validate_colormap(C);
+
+ /* Copy over the strokes from the buffer (and adjust the colors) */
+ for (bGPDstroke *gps = gp_strokes_copypastebuf.first; gps; gps = gps->next) {
+ if (ED_gpencil_stroke_can_use(C, gps)) {
+ /* Need to verify if layer exists */
+ if (type != GP_COPY_MERGE) {
+ gpl = BLI_findstring(&gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
+ if (gpl == NULL) {
+ /* no layer - use active (only if layer deleted before paste) */
+ gpl = CTX_data_active_gpencil_layer(C);
+ }
+ }
+
+ /* Ensure we have a frame to draw into
+ * NOTE: Since this is an op which creates strokes,
+ * we are obliged to add a new frame if one
+ * doesn't exist already
+ */
+ gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
+ if (gpf) {
+ /* Create new stroke */
+ bGPDstroke *new_stroke = MEM_dupallocN(gps);
+ new_stroke->runtime.tmp_layerinfo[0] = '\0';
+
+ new_stroke->points = MEM_dupallocN(gps->points);
+ if (gps->dvert != NULL) {
+ new_stroke->dvert = MEM_dupallocN(gps->dvert);
+ BKE_gpencil_stroke_weights_duplicate(gps, new_stroke);
+ }
+ new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
+ new_stroke->triangles = NULL;
+
+ new_stroke->next = new_stroke->prev = NULL;
+ BLI_addtail(&gpf->strokes, new_stroke);
+
+ /* Remap material */
+ Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
+ new_stroke->mat_nr = BKE_gpencil_object_material_get_index(ob, ma);
+ BLI_assert(new_stroke->mat_nr >= 0); /* have to add the material first */
+ }
+ }
+ }
+
+ /* free temp data */
+ BLI_ghash_free(new_colors, NULL, NULL);
+
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_paste(wmOperatorType *ot)
{
- static const EnumPropertyItem copy_type[] = {
- {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
- {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
- {0, NULL, 0, NULL, NULL},
- };
-
- /* identifiers */
- ot->name = "Paste Strokes";
- ot->idname = "GPENCIL_OT_paste";
- ot->description = "Paste previously copied strokes or copy and merge in active layer";
-
- /* callbacks */
- ot->exec = gp_strokes_paste_exec;
- ot->poll = gp_strokes_paste_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
-
- /* properties */
- ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
+ static const EnumPropertyItem copy_type[] = {
+ {GP_COPY_ONLY, "COPY", 0, "Copy", ""},
+ {GP_COPY_MERGE, "MERGE", 0, "Merge", ""},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Paste Strokes";
+ ot->idname = "GPENCIL_OT_paste";
+ ot->description = "Paste previously copied strokes or copy and merge in active layer";
+
+ /* callbacks */
+ ot->exec = gp_strokes_paste_exec;
+ ot->poll = gp_strokes_paste_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", copy_type, 0, "Type", "");
}
/* ******************* Move To Layer ****************************** */
static int gp_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(evt))
{
- uiPopupMenu *pup;
- uiLayout *layout;
+ uiPopupMenu *pup;
+ uiLayout *layout;
- /* call the menu, which will call this operator again, hence the canceled */
- pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
- layout = UI_popup_menu_layout(pup);
- uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
- UI_popup_menu_end(C, pup);
+ /* call the menu, which will call this operator again, hence the canceled */
+ pup = UI_popup_menu_begin(C, op->type->name, ICON_NONE);
+ layout = UI_popup_menu_layout(pup);
+ uiItemsEnumO(layout, "GPENCIL_OT_move_to_layer", "layer");
+ UI_popup_menu_end(C, pup);
- return OPERATOR_INTERFACE;
+ return OPERATOR_INTERFACE;
}
// FIXME: allow moving partial strokes
static int gp_move_to_layer_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = CTX_data_gpencil_data(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- int cfra_eval = (int)DEG_get_ctime(depsgraph);
- bGPDlayer *target_layer = NULL;
- ListBase strokes = {NULL, NULL};
- int layer_num = RNA_enum_get(op->ptr, "layer");
- const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
-
- if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
- BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
- return OPERATOR_CANCELLED;
- }
-
- /* if autolock enabled, disabled now */
- if (use_autolock) {
- gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
- }
-
- /* Get layer or create new one */
- if (layer_num == -1) {
- /* Create layer */
- target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
- }
- else {
- /* Try to get layer */
- target_layer = BLI_findlink(&gpd->layers, layer_num);
-
- if (target_layer == NULL) {
- /* back autolock status */
- if (use_autolock) {
- gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
- }
- BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
- return OPERATOR_CANCELLED;
- }
- }
-
- /* Extract all strokes to move to this layer
- * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
- * getting repeatedly moved
- */
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- bGPDframe *gpf = gpl->actframe;
- bGPDstroke *gps, *gpsn;
-
- /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
- if ((gpl == target_layer) || (gpf == NULL))
- continue;
-
- /* make copies of selected strokes, and deselect these once we're done */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false)
- continue;
-
- /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
- if (gps->flag & GP_STROKE_SELECT) {
- BLI_remlink(&gpf->strokes, gps);
- BLI_addtail(&strokes, gps);
- }
- }
-
- /* if new layer and autolock, lock old layer */
- if ((layer_num == -1) && (use_autolock)) {
- gpl->flag |= GP_LAYER_LOCKED;
- }
- }
- CTX_DATA_END;
-
- /* Paste them all in one go */
- if (strokes.first) {
- bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, cfra_eval, GP_GETFRAME_ADD_NEW);
-
- BLI_movelisttolist(&gpf->strokes, &strokes);
- BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
- }
-
- /* back autolock status */
- if (use_autolock) {
- gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
- }
-
- /* updates */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ int cfra_eval = (int)DEG_get_ctime(depsgraph);
+ bGPDlayer *target_layer = NULL;
+ ListBase strokes = {NULL, NULL};
+ int layer_num = RNA_enum_get(op->ptr, "layer");
+ const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
+
+ if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
+ BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* if autolock enabled, disabled now */
+ if (use_autolock) {
+ gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
+ }
+
+ /* Get layer or create new one */
+ if (layer_num == -1) {
+ /* Create layer */
+ target_layer = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
+ }
+ else {
+ /* Try to get layer */
+ target_layer = BLI_findlink(&gpd->layers, layer_num);
+
+ if (target_layer == NULL) {
+ /* back autolock status */
+ if (use_autolock) {
+ gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
+ }
+ BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
+ return OPERATOR_CANCELLED;
+ }
+ }
+
+ /* Extract all strokes to move to this layer
+ * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
+ * getting repeatedly moved
+ */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *gpf = gpl->actframe;
+ bGPDstroke *gps, *gpsn;
+
+ /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
+ if ((gpl == target_layer) || (gpf == NULL))
+ continue;
+
+ /* make copies of selected strokes, and deselect these once we're done */
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+
+ /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
+ if (gps->flag & GP_STROKE_SELECT) {
+ BLI_remlink(&gpf->strokes, gps);
+ BLI_addtail(&strokes, gps);
+ }
+ }
+
+ /* if new layer and autolock, lock old layer */
+ if ((layer_num == -1) && (use_autolock)) {
+ gpl->flag |= GP_LAYER_LOCKED;
+ }
+ }
+ CTX_DATA_END;
+
+ /* Paste them all in one go */
+ if (strokes.first) {
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(target_layer, cfra_eval, GP_GETFRAME_ADD_NEW);
+
+ BLI_movelisttolist(&gpf->strokes, &strokes);
+ BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
+ }
+
+ /* back autolock status */
+ if (use_autolock) {
+ gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
+ }
+
+ /* updates */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Move Strokes to Layer";
- ot->idname = "GPENCIL_OT_move_to_layer";
- ot->description = "Move selected strokes to another layer"; // XXX: allow moving individual points too?
-
- /* callbacks */
- ot->invoke = gp_move_to_layer_invoke;
- ot->exec = gp_move_to_layer_exec;
- ot->poll = gp_stroke_edit_poll; // XXX?
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* gp layer to use (dynamic enum) */
- ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
- RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
+ /* identifiers */
+ ot->name = "Move Strokes to Layer";
+ ot->idname = "GPENCIL_OT_move_to_layer";
+ ot->description =
+ "Move selected strokes to another layer"; // XXX: allow moving individual points too?
+
+ /* callbacks */
+ ot->invoke = gp_move_to_layer_invoke;
+ ot->exec = gp_move_to_layer_exec;
+ ot->poll = gp_stroke_edit_poll; // XXX?
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* gp layer to use (dynamic enum) */
+ ot->prop = RNA_def_enum(ot->srna, "layer", DummyRNA_DEFAULT_items, 0, "Grease Pencil Layer", "");
+ RNA_def_enum_funcs(ot->prop, ED_gpencil_layers_with_new_enum_itemf);
}
/* ********************* Add Blank Frame *************************** */
@@ -1510,221 +1513,224 @@ void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
/* Basically the same as the drawing op */
static bool UNUSED_FUNCTION(gp_blank_frame_add_poll)(bContext *C)
{
- if (ED_operator_regionactive(C)) {
- /* check if current context can support GPencil data */
- if (ED_gpencil_data_get_pointers(C, NULL) != NULL) {
- return 1;
- }
- else {
- CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into");
- }
- }
- else {
- CTX_wm_operator_poll_msg_set(C, "Active region not set");
- }
-
- return 0;
+ if (ED_operator_regionactive(C)) {
+ /* check if current context can support GPencil data */
+ if (ED_gpencil_data_get_pointers(C, NULL) != NULL) {
+ return 1;
+ }
+ else {
+ CTX_wm_operator_poll_msg_set(C, "Failed to find Grease Pencil data to draw into");
+ }
+ }
+ else {
+ CTX_wm_operator_poll_msg_set(C, "Active region not set");
+ }
+
+ return 0;
}
static int gp_blank_frame_add_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- int cfra_eval = (int)DEG_get_ctime(depsgraph);
-
- bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd);
-
- const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
-
- /* Initialise datablock and an active layer if nothing exists yet */
- if (ELEM(NULL, gpd, active_gpl)) {
- /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */
- WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL);
- }
-
- /* Go through each layer, adding a frame after the active one
- * and/or shunting all the others out of the way
- */
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- if ((all_layers == false) && (gpl != active_gpl)) {
- continue;
- }
-
- /* 1) Check for an existing frame on the current frame */
- bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra_eval);
- if (gpf) {
- /* Shunt all frames after (and including) the existing one later by 1-frame */
- for (; gpf; gpf = gpf->next) {
- gpf->framenum += 1;
- }
- }
-
- /* 2) Now add a new frame, with nothing in it */
- gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
- }
- CTX_DATA_END;
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ int cfra_eval = (int)DEG_get_ctime(depsgraph);
+
+ bGPDlayer *active_gpl = BKE_gpencil_layer_getactive(gpd);
+
+ const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
+
+ /* Initialise datablock and an active layer if nothing exists yet */
+ if (ELEM(NULL, gpd, active_gpl)) {
+ /* let's just be lazy, and call the "Add New Layer" operator, which sets everything up as required */
+ WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL);
+ }
+
+ /* Go through each layer, adding a frame after the active one
+ * and/or shunting all the others out of the way
+ */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ if ((all_layers == false) && (gpl != active_gpl)) {
+ continue;
+ }
+
+ /* 1) Check for an existing frame on the current frame */
+ bGPDframe *gpf = BKE_gpencil_layer_find_frame(gpl, cfra_eval);
+ if (gpf) {
+ /* Shunt all frames after (and including) the existing one later by 1-frame */
+ for (; gpf; gpf = gpf->next) {
+ gpf->framenum += 1;
+ }
+ }
+
+ /* 2) Now add a new frame, with nothing in it */
+ gpl->actframe = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_ADD_NEW);
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_blank_frame_add(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Insert Blank Frame";
- ot->idname = "GPENCIL_OT_blank_frame_add";
- ot->description = "Insert a blank frame on the current frame "
- "(all subsequently existing frames, if any, are shifted right by one frame)";
-
- /* callbacks */
- ot->exec = gp_blank_frame_add_exec;
- ot->poll = gp_add_poll;
-
- /* properties */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- RNA_def_boolean(ot->srna, "all_layers", false, "All Layers", "Create blank frame in all layers, not only active");
+ /* identifiers */
+ ot->name = "Insert Blank Frame";
+ ot->idname = "GPENCIL_OT_blank_frame_add";
+ ot->description =
+ "Insert a blank frame on the current frame "
+ "(all subsequently existing frames, if any, are shifted right by one frame)";
+
+ /* callbacks */
+ ot->exec = gp_blank_frame_add_exec;
+ ot->poll = gp_add_poll;
+
+ /* properties */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ RNA_def_boolean(ot->srna,
+ "all_layers",
+ false,
+ "All Layers",
+ "Create blank frame in all layers, not only active");
}
/* ******************* Delete Active Frame ************************ */
static bool gp_actframe_delete_poll(bContext *C)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
- /* only if there's an active layer with an active frame */
- return (gpl && gpl->actframe);
+ /* only if there's an active layer with an active frame */
+ return (gpl && gpl->actframe);
}
/* delete active frame - wrapper around API calls */
static int gp_actframe_delete_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *gpl = BKE_gpencil_layer_getactive(gpd);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- int cfra_eval = (int)DEG_get_ctime(depsgraph);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ int cfra_eval = (int)DEG_get_ctime(depsgraph);
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
- /* if there's no existing Grease-Pencil data there, add some */
- if (gpd == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
- return OPERATOR_CANCELLED;
- }
- if (ELEM(NULL, gpl, gpf)) {
- BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
- return OPERATOR_CANCELLED;
- }
+ /* if there's no existing Grease-Pencil data there, add some */
+ if (gpd == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
+ return OPERATOR_CANCELLED;
+ }
+ if (ELEM(NULL, gpl, gpf)) {
+ BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
+ return OPERATOR_CANCELLED;
+ }
- /* delete it... */
- BKE_gpencil_layer_delframe(gpl, gpf);
+ /* delete it... */
+ BKE_gpencil_layer_delframe(gpl, gpf);
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Delete Active Frame";
- ot->idname = "GPENCIL_OT_active_frame_delete";
- ot->description = "Delete the active frame for the active Grease Pencil Layer";
+ /* identifiers */
+ ot->name = "Delete Active Frame";
+ ot->idname = "GPENCIL_OT_active_frame_delete";
+ ot->description = "Delete the active frame for the active Grease Pencil Layer";
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- /* callbacks */
- ot->exec = gp_actframe_delete_exec;
- ot->poll = gp_actframe_delete_poll;
+ /* callbacks */
+ ot->exec = gp_actframe_delete_exec;
+ ot->poll = gp_actframe_delete_poll;
}
/* **************** Delete All Active Frames ****************** */
static bool gp_actframe_delete_all_poll(bContext *C)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
- /* 1) There must be grease pencil data
- * 2) Hopefully some of the layers have stuff we can use
- */
- return (gpd && gpd->layers.first);
+ /* 1) There must be grease pencil data
+ * 2) Hopefully some of the layers have stuff we can use
+ */
+ return (gpd && gpd->layers.first);
}
static int gp_actframe_delete_all_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- int cfra_eval = (int)DEG_get_ctime(depsgraph);
-
- bool success = false;
-
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- /* try to get the "active" frame - but only if it actually occurs on this frame */
- bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
-
- if (gpf == NULL)
- continue;
-
- /* delete it... */
- BKE_gpencil_layer_delframe(gpl, gpf);
-
- /* we successfully modified something */
- success = true;
- }
- CTX_DATA_END;
-
- /* updates */
- if (success) {
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
- }
- else {
- BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
- return OPERATOR_CANCELLED;
- }
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ int cfra_eval = (int)DEG_get_ctime(depsgraph);
+
+ bool success = false;
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ /* try to get the "active" frame - but only if it actually occurs on this frame */
+ bGPDframe *gpf = BKE_gpencil_layer_getframe(gpl, cfra_eval, GP_GETFRAME_USE_PREV);
+
+ if (gpf == NULL)
+ continue;
+
+ /* delete it... */
+ BKE_gpencil_layer_delframe(gpl, gpf);
+
+ /* we successfully modified something */
+ success = true;
+ }
+ CTX_DATA_END;
+
+ /* updates */
+ if (success) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
+ return OPERATOR_CANCELLED;
+ }
}
void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Delete All Active Frames";
- ot->idname = "GPENCIL_OT_active_frames_delete_all";
- ot->description = "Delete the active frame(s) of all editable Grease Pencil layers";
+ /* identifiers */
+ ot->name = "Delete All Active Frames";
+ ot->idname = "GPENCIL_OT_active_frames_delete_all";
+ ot->description = "Delete the active frame(s) of all editable Grease Pencil layers";
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- /* callbacks */
- ot->exec = gp_actframe_delete_all_exec;
- ot->poll = gp_actframe_delete_all_poll;
+ /* callbacks */
+ ot->exec = gp_actframe_delete_all_exec;
+ ot->poll = gp_actframe_delete_all_poll;
}
/* ******************* Delete Operator ************************ */
typedef enum eGP_DeleteMode {
- /* delete selected stroke points */
- GP_DELETEOP_POINTS = 0,
- /* delete selected strokes */
- GP_DELETEOP_STROKES = 1,
- /* delete active frame */
- GP_DELETEOP_FRAME = 2,
+ /* delete selected stroke points */
+ GP_DELETEOP_POINTS = 0,
+ /* delete selected strokes */
+ GP_DELETEOP_STROKES = 1,
+ /* delete active frame */
+ GP_DELETEOP_FRAME = 2,
} eGP_DeleteMode;
typedef enum eGP_DissolveMode {
- /* dissolve all selected points */
- GP_DISSOLVE_POINTS = 0,
- /* dissolve between selected points */
- GP_DISSOLVE_BETWEEN = 1,
- /* dissolve unselected points */
- GP_DISSOLVE_UNSELECT = 2,
+ /* dissolve all selected points */
+ GP_DISSOLVE_POINTS = 0,
+ /* dissolve between selected points */
+ GP_DISSOLVE_BETWEEN = 1,
+ /* dissolve unselected points */
+ GP_DISSOLVE_UNSELECT = 2,
} eGP_DissolveMode;
/* ----------------------------------- */
@@ -1732,61 +1738,60 @@ typedef enum eGP_DissolveMode {
/* Delete selected strokes */
static int gp_delete_selected_strokes(bContext *C)
{
- bool changed = false;
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
-
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
-
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
-
- if (gpf == NULL)
- continue;
-
- /* simply delete strokes which are selected */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false)
- continue;
-
- /* free stroke if selected */
- if (gps->flag & GP_STROKE_SELECT) {
- /* free stroke memory arrays, then stroke itself */
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
- MEM_SAFE_FREE(gps->triangles);
- BLI_freelinkN(&gpf->strokes, gps);
-
- changed = true;
- }
- }
- }
- }
- }
- CTX_DATA_END;
-
- if (changed) {
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
+ bool changed = false;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = gpl->actframe;
+ if (is_multiedit) {
+ init_gpf = gpl->frames.first;
+ }
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ bGPDstroke *gps, *gpsn;
+
+ if (gpf == NULL)
+ continue;
+
+ /* simply delete strokes which are selected */
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+
+ /* free stroke if selected */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* free stroke memory arrays, then stroke itself */
+ if (gps->points) {
+ MEM_freeN(gps->points);
+ }
+ if (gps->dvert) {
+ BKE_gpencil_free_stroke_weights(gps);
+ MEM_freeN(gps->dvert);
+ }
+ MEM_SAFE_FREE(gps->triangles);
+ BLI_freelinkN(&gpf->strokes, gps);
+
+ changed = true;
+ }
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
}
/* ----------------------------------- */
@@ -1794,242 +1799,242 @@ static int gp_delete_selected_strokes(bContext *C)
/* Delete selected points but keep the stroke */
static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
{
- Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- bool changed = false;
- int first = 0;
- int last = 0;
-
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
-
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
-
- bGPDstroke *gps, *gpsn;
-
- if (gpf == NULL)
- continue;
-
- /* simply delete points from selected strokes
- * NOTE: we may still have to remove the stroke if it ends up having no points!
- */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = 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_color_use(ob, gpl, gps) == false)
- continue;
-
- /* the stroke must have at least one point selected for any operator */
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- MDeformVert *dvert = NULL;
- int i;
-
- int tot = gps->totpoints; /* number of points in new buffer */
-
- /* first pass: count points to remove */
- switch (mode) {
- case GP_DISSOLVE_POINTS:
- /* Count how many points are selected (i.e. how many to remove) */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* selected point - one of the points to remove */
- tot--;
- }
- }
- break;
- case GP_DISSOLVE_BETWEEN:
- /* need to find first and last point selected */
- first = -1;
- last = 0;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- if (first < 0) {
- first = i;
- }
- last = i;
- }
- }
- /* count unselected points in the range */
- for (i = first, pt = gps->points + first; i < last; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- tot--;
- }
- }
- break;
- case GP_DISSOLVE_UNSELECT:
- /* count number of unselected points */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- tot--;
- }
- }
- break;
- default:
- return false;
- break;
- }
-
- /* if no points are left, we simply delete the entire stroke */
- if (tot <= 0) {
- /* remove the entire stroke */
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
- if (gps->triangles) {
- MEM_freeN(gps->triangles);
- }
- BLI_freelinkN(&gpf->strokes, gps);
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- }
- else {
- /* just copy all points to keep into a smaller buffer */
- bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot, "new gp stroke points copy");
- bGPDspoint *npt = new_points;
-
- MDeformVert *new_dvert = NULL;
- MDeformVert *ndvert = NULL;
-
- if (gps->dvert != NULL) {
- new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
- ndvert = new_dvert;
- }
-
- switch (mode) {
- case GP_DISSOLVE_POINTS:
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if ((pt->flag & GP_SPOINT_SELECT) == 0) {
- *npt = *pt;
- npt++;
-
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- dvert++;
- }
- }
- }
- break;
- case GP_DISSOLVE_BETWEEN:
- /* copy first segment */
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < first; i++, pt++) {
- *npt = *pt;
- npt++;
-
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- dvert++;
- }
- }
- /* copy segment (selected points) */
- (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
- for (i = first, pt = gps->points + first; i < last; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- *npt = *pt;
- npt++;
-
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- dvert++;
- }
- }
- }
- /* copy last segment */
- (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
- for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
- *npt = *pt;
- npt++;
-
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- dvert++;
- }
- }
-
- break;
- case GP_DISSOLVE_UNSELECT:
- /* copy any selected point */
- (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- *npt = *pt;
- npt++;
-
- if (gps->dvert != NULL) {
- *ndvert = *dvert;
- ndvert->dw = MEM_dupallocN(dvert->dw);
- ndvert++;
- dvert++;
- }
- }
- }
- break;
- }
-
- /* free the old buffer */
- if (gps->points) {
- MEM_freeN(gps->points);
- }
- if (gps->dvert) {
- BKE_gpencil_free_stroke_weights(gps);
- MEM_freeN(gps->dvert);
- }
-
- /* save the new buffer */
- gps->points = new_points;
- gps->dvert = new_dvert;
- gps->totpoints = tot;
-
- /* triangles cache needs to be recalculated */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps->tot_triangles = 0;
-
- /* deselect the stroke, since none of its selected points will still be selected */
- gps->flag &= ~GP_STROKE_SELECT;
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
- }
-
- changed = true;
- }
- }
- }
- }
- }
- CTX_DATA_END;
-
- if (changed) {
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ bool changed = false;
+ int first = 0;
+ int last = 0;
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = gpl->actframe;
+ if (is_multiedit) {
+ init_gpf = gpl->frames.first;
+ }
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+
+ bGPDstroke *gps, *gpsn;
+
+ if (gpf == NULL)
+ continue;
+
+ /* simply delete points from selected strokes
+ * NOTE: we may still have to remove the stroke if it ends up having no points!
+ */
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = 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_color_use(ob, gpl, gps) == false)
+ continue;
+
+ /* the stroke must have at least one point selected for any operator */
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ MDeformVert *dvert = NULL;
+ int i;
+
+ int tot = gps->totpoints; /* number of points in new buffer */
+
+ /* first pass: count points to remove */
+ switch (mode) {
+ case GP_DISSOLVE_POINTS:
+ /* Count how many points are selected (i.e. how many to remove) */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* selected point - one of the points to remove */
+ tot--;
+ }
+ }
+ break;
+ case GP_DISSOLVE_BETWEEN:
+ /* need to find first and last point selected */
+ first = -1;
+ last = 0;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (first < 0) {
+ first = i;
+ }
+ last = i;
+ }
+ }
+ /* count unselected points in the range */
+ for (i = first, pt = gps->points + first; i < last; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ tot--;
+ }
+ }
+ break;
+ case GP_DISSOLVE_UNSELECT:
+ /* count number of unselected points */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ tot--;
+ }
+ }
+ break;
+ default:
+ return false;
+ break;
+ }
+
+ /* if no points are left, we simply delete the entire stroke */
+ if (tot <= 0) {
+ /* remove the entire stroke */
+ if (gps->points) {
+ MEM_freeN(gps->points);
+ }
+ if (gps->dvert) {
+ BKE_gpencil_free_stroke_weights(gps);
+ MEM_freeN(gps->dvert);
+ }
+ if (gps->triangles) {
+ MEM_freeN(gps->triangles);
+ }
+ BLI_freelinkN(&gpf->strokes, gps);
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ }
+ else {
+ /* just copy all points to keep into a smaller buffer */
+ bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot,
+ "new gp stroke points copy");
+ bGPDspoint *npt = new_points;
+
+ MDeformVert *new_dvert = NULL;
+ MDeformVert *ndvert = NULL;
+
+ if (gps->dvert != NULL) {
+ new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
+ ndvert = new_dvert;
+ }
+
+ switch (mode) {
+ case GP_DISSOLVE_POINTS:
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if ((pt->flag & GP_SPOINT_SELECT) == 0) {
+ *npt = *pt;
+ npt++;
+
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
+ }
+ }
+ }
+ break;
+ case GP_DISSOLVE_BETWEEN:
+ /* copy first segment */
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < first; i++, pt++) {
+ *npt = *pt;
+ npt++;
+
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
+ }
+ }
+ /* copy segment (selected points) */
+ (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
+ for (i = first, pt = gps->points + first; i < last; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ *npt = *pt;
+ npt++;
+
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
+ }
+ }
+ }
+ /* copy last segment */
+ (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
+ for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
+ *npt = *pt;
+ npt++;
+
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
+ }
+ }
+
+ break;
+ case GP_DISSOLVE_UNSELECT:
+ /* copy any selected point */
+ (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ *npt = *pt;
+ npt++;
+
+ if (gps->dvert != NULL) {
+ *ndvert = *dvert;
+ ndvert->dw = MEM_dupallocN(dvert->dw);
+ ndvert++;
+ dvert++;
+ }
+ }
+ }
+ break;
+ }
+
+ /* free the old buffer */
+ if (gps->points) {
+ MEM_freeN(gps->points);
+ }
+ if (gps->dvert) {
+ BKE_gpencil_free_stroke_weights(gps);
+ MEM_freeN(gps->dvert);
+ }
+
+ /* save the new buffer */
+ gps->points = new_points;
+ gps->dvert = new_dvert;
+ gps->totpoints = tot;
+
+ /* triangles cache needs to be recalculated */
+ gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps->tot_triangles = 0;
+
+ /* deselect the stroke, since none of its selected points will still be selected */
+ gps->flag &= ~GP_STROKE_SELECT;
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+ }
+
+ changed = true;
+ }
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
}
/* ----------------------------------- */
@@ -2039,94 +2044,93 @@ static int gp_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
* gp_stroke_delete_tagged_points()
*/
typedef struct tGPDeleteIsland {
- int start_idx;
- int end_idx;
+ int start_idx;
+ int end_idx;
} tGPDeleteIsland;
static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDstroke *gps_last)
{
- bGPDspoint *pt = NULL;
- bGPDspoint *pt_final = NULL;
- const int totpoints = gps_first->totpoints + gps_last->totpoints;
-
- /* create new stroke */
- bGPDstroke *join_stroke = MEM_dupallocN(gps_first);
-
- join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
- join_stroke->totpoints = totpoints;
- join_stroke->flag &= ~GP_STROKE_CYCLIC;
-
- /* copy points (last before) */
- int e1 = 0;
- int e2 = 0;
- float delta = 0.0f;
-
- for (int i = 0; i < totpoints; i++) {
- pt_final = &join_stroke->points[i];
- if (i < gps_last->totpoints) {
- pt = &gps_last->points[e1];
- e1++;
- }
- else {
- pt = &gps_first->points[e2];
- e2++;
- }
-
- /* copy current point */
- copy_v3_v3(&pt_final->x, &pt->x);
- pt_final->pressure = pt->pressure;
- pt_final->strength = pt->strength;
- pt_final->time = delta;
- pt_final->flag = pt->flag;
-
- /* retiming with fixed time interval (we cannot determine real time) */
- delta += 0.01f;
- }
-
- /* Copy over vertex weight data (if available) */
- if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) {
- join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__);
- MDeformVert *dvert_src = NULL;
- MDeformVert *dvert_dst = NULL;
-
- /* Copy weights (last before)*/
- e1 = 0;
- e2 = 0;
- for (int i = 0; i < totpoints; i++) {
- dvert_dst = &join_stroke->dvert[i];
- dvert_src = NULL;
- if (i < gps_last->totpoints) {
- if (gps_last->dvert) {
- dvert_src = &gps_last->dvert[e1];
- e1++;
- }
- }
- else {
- if (gps_first->dvert) {
- dvert_src = &gps_first->dvert[e2];
- e2++;
- }
- }
-
- if ((dvert_src) && (dvert_src->dw)) {
- dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
- }
- }
- }
-
- /* add new stroke at head */
- BLI_addhead(&gpf->strokes, join_stroke);
-
- /* remove first stroke */
- BLI_remlink(&gpf->strokes, gps_first);
- BKE_gpencil_free_stroke(gps_first);
-
- /* remove last stroke */
- BLI_remlink(&gpf->strokes, gps_last);
- BKE_gpencil_free_stroke(gps_last);
+ bGPDspoint *pt = NULL;
+ bGPDspoint *pt_final = NULL;
+ const int totpoints = gps_first->totpoints + gps_last->totpoints;
+
+ /* create new stroke */
+ bGPDstroke *join_stroke = MEM_dupallocN(gps_first);
+
+ join_stroke->points = MEM_callocN(sizeof(bGPDspoint) * totpoints, __func__);
+ join_stroke->totpoints = totpoints;
+ join_stroke->flag &= ~GP_STROKE_CYCLIC;
+
+ /* copy points (last before) */
+ int e1 = 0;
+ int e2 = 0;
+ float delta = 0.0f;
+
+ for (int i = 0; i < totpoints; i++) {
+ pt_final = &join_stroke->points[i];
+ if (i < gps_last->totpoints) {
+ pt = &gps_last->points[e1];
+ e1++;
+ }
+ else {
+ pt = &gps_first->points[e2];
+ e2++;
+ }
+
+ /* copy current point */
+ copy_v3_v3(&pt_final->x, &pt->x);
+ pt_final->pressure = pt->pressure;
+ pt_final->strength = pt->strength;
+ pt_final->time = delta;
+ pt_final->flag = pt->flag;
+
+ /* retiming with fixed time interval (we cannot determine real time) */
+ delta += 0.01f;
+ }
+
+ /* Copy over vertex weight data (if available) */
+ if ((gps_first->dvert != NULL) || (gps_last->dvert != NULL)) {
+ join_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * totpoints, __func__);
+ MDeformVert *dvert_src = NULL;
+ MDeformVert *dvert_dst = NULL;
+
+ /* Copy weights (last before)*/
+ e1 = 0;
+ e2 = 0;
+ for (int i = 0; i < totpoints; i++) {
+ dvert_dst = &join_stroke->dvert[i];
+ dvert_src = NULL;
+ if (i < gps_last->totpoints) {
+ if (gps_last->dvert) {
+ dvert_src = &gps_last->dvert[e1];
+ e1++;
+ }
+ }
+ else {
+ if (gps_first->dvert) {
+ dvert_src = &gps_first->dvert[e2];
+ e2++;
+ }
+ }
+
+ if ((dvert_src) && (dvert_src->dw)) {
+ dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ }
+ }
+ }
+
+ /* add new stroke at head */
+ BLI_addhead(&gpf->strokes, join_stroke);
+
+ /* remove first stroke */
+ BLI_remlink(&gpf->strokes, gps_first);
+ BKE_gpencil_free_stroke(gps_first);
+
+ /* remove last stroke */
+ BLI_remlink(&gpf->strokes, gps_last);
+ BKE_gpencil_free_stroke(gps_last);
}
-
/* Split the given stroke into several new strokes, partitioning
* it based on whether the stroke points have a particular flag
* is set (e.g. "GP_SPOINT_SELECT" in most cases, but not always)
@@ -2141,290 +2145,315 @@ static void gp_stroke_join_islands(bGPDframe *gpf, bGPDstroke *gps_first, bGPDst
* 2) Each island gets converted to a new stroke
* If the number of points is <= limit, the stroke is deleted
*/
-void gp_stroke_delete_tagged_points(bGPDframe *gpf, bGPDstroke *gps, bGPDstroke *next_stroke,
- int tag_flags, bool select, int limit)
+void gp_stroke_delete_tagged_points(bGPDframe *gpf,
+ bGPDstroke *gps,
+ bGPDstroke *next_stroke,
+ int tag_flags,
+ bool select,
+ int limit)
{
- tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2, "gp_point_islands");
- bool in_island = false;
- int num_islands = 0;
-
- bGPDstroke *gps_first = NULL;
- const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC);
-
- /* First Pass: Identify start/end of islands */
- bGPDspoint *pt = gps->points;
- for (int i = 0; i < gps->totpoints; i++, pt++) {
- if (pt->flag & tag_flags) {
- /* selected - stop accumulating to island */
- in_island = false;
- }
- else {
- /* unselected - start of a new island? */
- int idx;
-
- if (in_island) {
- /* extend existing island */
- idx = num_islands - 1;
- islands[idx].end_idx = i;
- }
- else {
- /* start of new island */
- in_island = true;
- num_islands++;
-
- idx = num_islands - 1;
- islands[idx].start_idx = islands[idx].end_idx = i;
- }
- }
- }
-
- /* Watch out for special case where No islands = All points selected = Delete Stroke only */
- if (num_islands) {
- /* there are islands, so create a series of new strokes, adding them before the "next" stroke */
- int idx;
- bGPDstroke *new_stroke = NULL;
-
- /* Create each new stroke... */
- for (idx = 0; idx < num_islands; idx++) {
- tGPDeleteIsland *island = &islands[idx];
- new_stroke = MEM_dupallocN(gps);
-
- /* if cyclic and first stroke, save to join later */
- if ((is_cyclic) && (gps_first == NULL)) {
- gps_first = new_stroke;
- }
-
- /* initialize triangle memory - to be calculated on next redraw */
- new_stroke->triangles = NULL;
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
- new_stroke->flag &= ~GP_STROKE_CYCLIC;
- new_stroke->tot_triangles = 0;
-
- /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
- new_stroke->totpoints = island->end_idx - island->start_idx + 1;
-
- /* Copy over the relevant point data */
- new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints, "gp delete stroke fragment");
- memcpy(new_stroke->points, gps->points + island->start_idx, sizeof(bGPDspoint) * new_stroke->totpoints);
-
- /* Copy over vertex weight data (if available) */
- if (gps->dvert != NULL) {
- /* Copy over the relevant vertex-weight points */
- new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints, "gp delete stroke fragment weight");
- memcpy(new_stroke->dvert, gps->dvert + island->start_idx, sizeof(MDeformVert) * new_stroke->totpoints);
-
- /* Copy weights */
- int e = island->start_idx;
- for (int i = 0; i < new_stroke->totpoints; i++) {
- MDeformVert *dvert_src = &gps->dvert[e];
- MDeformVert *dvert_dst = &new_stroke->dvert[i];
- if (dvert_src->dw) {
- dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
- }
- e++;
- }
- }
- /* Each island corresponds to a new stroke. We must adjust the
- * timings of these new strokes:
- *
- * Each point's timing data is a delta from stroke's inittime, so as we erase some points from
- * the start of the stroke, we have to offset this inittime and all remaining points' delta values.
- * This way we get a new stroke with exactly the same timing as if user had started drawing from
- * the first non-removed point...
- */
- {
- bGPDspoint *pts;
- float delta = gps->points[island->start_idx].time;
- int j;
-
- new_stroke->inittime += (double)delta;
-
- pts = new_stroke->points;
- for (j = 0; j < new_stroke->totpoints; j++, pts++) {
- pts->time -= delta;
- /* set flag for select again later */
- if (select == true) {
- pts->flag &= ~GP_SPOINT_SELECT;
- pts->flag |= GP_SPOINT_TAG;
- }
- }
- }
-
- /* Add new stroke to the frame or delete if below limit */
- if ((limit > 0) && (new_stroke->totpoints <= limit)) {
- BKE_gpencil_free_stroke(new_stroke);
- }
- else {
- if (next_stroke) {
- BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
- }
- else {
- BLI_addtail(&gpf->strokes, new_stroke);
- }
- }
- }
- /* if cyclic, need to join last stroke with first stroke */
- if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) {
- gp_stroke_join_islands(gpf, gps_first, new_stroke);
- }
-
- }
-
- /* free islands */
- MEM_freeN(islands);
-
- /* Delete the old stroke */
- BLI_remlink(&gpf->strokes, gps);
- BKE_gpencil_free_stroke(gps);
+ tGPDeleteIsland *islands = MEM_callocN(sizeof(tGPDeleteIsland) * (gps->totpoints + 1) / 2,
+ "gp_point_islands");
+ bool in_island = false;
+ int num_islands = 0;
+
+ bGPDstroke *gps_first = NULL;
+ const bool is_cyclic = (bool)(gps->flag & GP_STROKE_CYCLIC);
+
+ /* First Pass: Identify start/end of islands */
+ bGPDspoint *pt = gps->points;
+ for (int i = 0; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & tag_flags) {
+ /* selected - stop accumulating to island */
+ in_island = false;
+ }
+ else {
+ /* unselected - start of a new island? */
+ int idx;
+
+ if (in_island) {
+ /* extend existing island */
+ idx = num_islands - 1;
+ islands[idx].end_idx = i;
+ }
+ else {
+ /* start of new island */
+ in_island = true;
+ num_islands++;
+
+ idx = num_islands - 1;
+ islands[idx].start_idx = islands[idx].end_idx = i;
+ }
+ }
+ }
+
+ /* Watch out for special case where No islands = All points selected = Delete Stroke only */
+ if (num_islands) {
+ /* there are islands, so create a series of new strokes, adding them before the "next" stroke */
+ int idx;
+ bGPDstroke *new_stroke = NULL;
+
+ /* Create each new stroke... */
+ for (idx = 0; idx < num_islands; idx++) {
+ tGPDeleteIsland *island = &islands[idx];
+ new_stroke = MEM_dupallocN(gps);
+
+ /* if cyclic and first stroke, save to join later */
+ if ((is_cyclic) && (gps_first == NULL)) {
+ gps_first = new_stroke;
+ }
+
+ /* initialize triangle memory - to be calculated on next redraw */
+ new_stroke->triangles = NULL;
+ new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
+ new_stroke->flag &= ~GP_STROKE_CYCLIC;
+ new_stroke->tot_triangles = 0;
+
+ /* Compute new buffer size (+ 1 needed as the endpoint index is "inclusive") */
+ new_stroke->totpoints = island->end_idx - island->start_idx + 1;
+
+ /* Copy over the relevant point data */
+ new_stroke->points = MEM_callocN(sizeof(bGPDspoint) * new_stroke->totpoints,
+ "gp delete stroke fragment");
+ memcpy(new_stroke->points,
+ gps->points + island->start_idx,
+ sizeof(bGPDspoint) * new_stroke->totpoints);
+
+ /* Copy over vertex weight data (if available) */
+ if (gps->dvert != NULL) {
+ /* Copy over the relevant vertex-weight points */
+ new_stroke->dvert = MEM_callocN(sizeof(MDeformVert) * new_stroke->totpoints,
+ "gp delete stroke fragment weight");
+ memcpy(new_stroke->dvert,
+ gps->dvert + island->start_idx,
+ sizeof(MDeformVert) * new_stroke->totpoints);
+
+ /* Copy weights */
+ int e = island->start_idx;
+ for (int i = 0; i < new_stroke->totpoints; i++) {
+ MDeformVert *dvert_src = &gps->dvert[e];
+ MDeformVert *dvert_dst = &new_stroke->dvert[i];
+ if (dvert_src->dw) {
+ dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
+ }
+ e++;
+ }
+ }
+ /* Each island corresponds to a new stroke. We must adjust the
+ * timings of these new strokes:
+ *
+ * Each point's timing data is a delta from stroke's inittime, so as we erase some points from
+ * the start of the stroke, we have to offset this inittime and all remaining points' delta values.
+ * This way we get a new stroke with exactly the same timing as if user had started drawing from
+ * the first non-removed point...
+ */
+ {
+ bGPDspoint *pts;
+ float delta = gps->points[island->start_idx].time;
+ int j;
+
+ new_stroke->inittime += (double)delta;
+
+ pts = new_stroke->points;
+ for (j = 0; j < new_stroke->totpoints; j++, pts++) {
+ pts->time -= delta;
+ /* set flag for select again later */
+ if (select == true) {
+ pts->flag &= ~GP_SPOINT_SELECT;
+ pts->flag |= GP_SPOINT_TAG;
+ }
+ }
+ }
+
+ /* Add new stroke to the frame or delete if below limit */
+ if ((limit > 0) && (new_stroke->totpoints <= limit)) {
+ BKE_gpencil_free_stroke(new_stroke);
+ }
+ else {
+ if (next_stroke) {
+ BLI_insertlinkbefore(&gpf->strokes, next_stroke, new_stroke);
+ }
+ else {
+ BLI_addtail(&gpf->strokes, new_stroke);
+ }
+ }
+ }
+ /* if cyclic, need to join last stroke with first stroke */
+ if ((is_cyclic) && (gps_first != NULL) && (gps_first != new_stroke)) {
+ gp_stroke_join_islands(gpf, gps_first, new_stroke);
+ }
+ }
+
+ /* free islands */
+ MEM_freeN(islands);
+
+ /* Delete the old stroke */
+ BLI_remlink(&gpf->strokes, gps);
+ BKE_gpencil_free_stroke(gps);
}
/* Split selected strokes into segments, splitting on selected points */
static int gp_delete_selected_points(bContext *C)
{
- Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
- bool changed = false;
-
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
-
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
-
- if (gpf == NULL)
- continue;
-
- /* simply delete strokes which are selected */
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = 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_color_use(ob, gpl, gps) == false)
- continue;
-
-
- if (gps->flag & GP_STROKE_SELECT) {
- /* deselect old stroke, since it will be used as template for the new strokes */
- gps->flag &= ~GP_STROKE_SELECT;
-
- /* delete unwanted points by splitting stroke into several smaller ones */
- gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
-
- changed = true;
- }
- }
- }
- }
- }
- CTX_DATA_END;
-
- if (changed) {
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
- }
- else {
- return OPERATOR_CANCELLED;
- }
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+ bool changed = false;
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = gpl->actframe;
+ if (is_multiedit) {
+ init_gpf = gpl->frames.first;
+ }
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ bGPDstroke *gps, *gpsn;
+
+ if (gpf == NULL)
+ continue;
+
+ /* simply delete strokes which are selected */
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = 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_color_use(ob, gpl, gps) == false)
+ continue;
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* deselect old stroke, since it will be used as template for the new strokes */
+ gps->flag &= ~GP_STROKE_SELECT;
+
+ /* delete unwanted points by splitting stroke into several smaller ones */
+ gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
+
+ changed = true;
+ }
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
}
/* simple wrapper to external call */
int gp_delete_selected_point_wrap(bContext *C)
{
- return gp_delete_selected_points(C);
+ return gp_delete_selected_points(C);
}
/* ----------------------------------- */
static int gp_delete_exec(bContext *C, wmOperator *op)
{
- eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
- int result = OPERATOR_CANCELLED;
+ eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
+ int result = OPERATOR_CANCELLED;
- switch (mode) {
- case GP_DELETEOP_STROKES: /* selected strokes */
- result = gp_delete_selected_strokes(C);
- break;
+ switch (mode) {
+ case GP_DELETEOP_STROKES: /* selected strokes */
+ result = gp_delete_selected_strokes(C);
+ break;
- case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */
- result = gp_delete_selected_points(C);
- break;
+ case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */
+ result = gp_delete_selected_points(C);
+ break;
- case GP_DELETEOP_FRAME: /* active frame */
- result = gp_actframe_delete_exec(C, op);
- break;
- }
+ case GP_DELETEOP_FRAME: /* active frame */
+ result = gp_actframe_delete_exec(C, op);
+ break;
+ }
- return result;
+ return result;
}
void GPENCIL_OT_delete(wmOperatorType *ot)
{
- static const EnumPropertyItem prop_gpencil_delete_types[] = {
- {GP_DELETEOP_POINTS, "POINTS", 0, "Points", "Delete selected points and split strokes into segments"},
- {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
- {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
- {0, NULL, 0, NULL, NULL},
- };
-
- /* identifiers */
- ot->name = "Delete";
- ot->idname = "GPENCIL_OT_delete";
- ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
-
- /* callbacks */
- ot->invoke = WM_menu_invoke;
- ot->exec = gp_delete_exec;
- ot->poll = gp_stroke_edit_poll;
-
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
-
- /* props */
- ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_delete_types, 0, "Type", "Method used for deleting Grease Pencil data");
+ static const EnumPropertyItem prop_gpencil_delete_types[] = {
+ {GP_DELETEOP_POINTS,
+ "POINTS",
+ 0,
+ "Points",
+ "Delete selected points and split strokes into segments"},
+ {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
+ {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Delete";
+ ot->idname = "GPENCIL_OT_delete";
+ ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = gp_delete_exec;
+ ot->poll = gp_stroke_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+
+ /* props */
+ ot->prop = RNA_def_enum(ot->srna,
+ "type",
+ prop_gpencil_delete_types,
+ 0,
+ "Type",
+ "Method used for deleting Grease Pencil data");
}
static int gp_dissolve_exec(bContext *C, wmOperator *op)
{
- eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
+ eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
- return gp_dissolve_selected_points(C, mode);
+ return gp_dissolve_selected_points(C, mode);
}
void GPENCIL_OT_dissolve(wmOperatorType *ot)
{
- static EnumPropertyItem prop_gpencil_dissolve_types[] = {
- {GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points"},
- {GP_DISSOLVE_BETWEEN, "BETWEEN", 0, "Dissolve Between", "Dissolve points between selected points"},
- {GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points"},
- {0, NULL, 0, NULL, NULL},
- };
-
- /* identifiers */
- ot->name = "Dissolve";
- ot->idname = "GPENCIL_OT_dissolve";
- ot->description = "Delete selected points without splitting strokes";
-
- /* callbacks */
- ot->invoke = WM_menu_invoke;
- ot->exec = gp_dissolve_exec;
- ot->poll = gp_stroke_edit_poll;
-
- /* flags */
- ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
-
- /* props */
- ot->prop = RNA_def_enum(ot->srna, "type", prop_gpencil_dissolve_types, 0,
- "Type", "Method used for dissolving Stroke points");
+ static EnumPropertyItem prop_gpencil_dissolve_types[] = {
+ {GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points"},
+ {GP_DISSOLVE_BETWEEN,
+ "BETWEEN",
+ 0,
+ "Dissolve Between",
+ "Dissolve points between selected points"},
+ {GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Dissolve";
+ ot->idname = "GPENCIL_OT_dissolve";
+ ot->description = "Delete selected points without splitting strokes";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = gp_dissolve_exec;
+ ot->poll = gp_stroke_edit_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_UNDO | OPTYPE_REGISTER;
+
+ /* props */
+ ot->prop = RNA_def_enum(ot->srna,
+ "type",
+ prop_gpencil_dissolve_types,
+ 0,
+ "Type",
+ "Method used for dissolving Stroke points");
}
/* ****************** Snapping - Strokes <-> Cursor ************************ */
@@ -2435,370 +2464,369 @@ void GPENCIL_OT_dissolve(wmOperatorType *ot)
*/
static bool gp_snap_poll(bContext *C)
{
- bGPdata *gpd = CTX_data_gpencil_data(C);
- ScrArea *sa = CTX_wm_area(C);
+ bGPdata *gpd = CTX_data_gpencil_data(C);
+ ScrArea *sa = CTX_wm_area(C);
- return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D));
+ return (gpd != NULL) && ((sa != NULL) && (sa->spacetype == SPACE_VIEW3D));
}
/* --------------------------------- */
static int gp_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- RegionView3D *rv3d = CTX_wm_region_data(C);
- View3D *v3d = CTX_wm_view3d(C);
- Scene *scene = CTX_data_scene(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- Object *obact = CTX_data_active_object(C);
- const float gridf = ED_view3d_grid_view_scale(scene, v3d, rv3d, NULL);
-
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- /* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
- bGPDframe *gpf = gpl->actframe;
- float diff_mat[4][4];
-
- /* calculate difference matrix object */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
-
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
- bGPDspoint *pt;
- int i;
-
- /* 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(obact, gpl, gps) == false)
- continue;
-
- // TODO: if entire stroke is selected, offset entire stroke by same amount?
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- /* only if point is selected */
- if (pt->flag & GP_SPOINT_SELECT) {
- /* apply parent transformations */
- float fpt[3];
- mul_v3_m4v3(fpt, diff_mat, &pt->x);
-
- fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
- fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
- fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
-
- /* return data */
- copy_v3_v3(&pt->x, fpt);
- gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
- }
- }
- }
- }
- }
-
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ RegionView3D *rv3d = CTX_wm_region_data(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ Object *obact = CTX_data_active_object(C);
+ const float gridf = ED_view3d_grid_view_scale(scene, v3d, rv3d, NULL);
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ bGPDframe *gpf = gpl->actframe;
+ float diff_mat[4][4];
+
+ /* calculate difference matrix object */
+ ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ bGPDspoint *pt;
+ int i;
+
+ /* 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(obact, gpl, gps) == false)
+ continue;
+
+ // TODO: if entire stroke is selected, offset entire stroke by same amount?
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ /* only if point is selected */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* apply parent transformations */
+ float fpt[3];
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
+ fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
+ fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
+ fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
+
+ /* return data */
+ copy_v3_v3(&pt->x, fpt);
+ gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
+ }
+ }
+ }
+ }
+ }
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Snap Selection to Grid";
- ot->idname = "GPENCIL_OT_snap_to_grid";
- ot->description = "Snap selected points to the nearest grid points";
+ /* identifiers */
+ ot->name = "Snap Selection to Grid";
+ ot->idname = "GPENCIL_OT_snap_to_grid";
+ ot->description = "Snap selected points to the nearest grid points";
- /* callbacks */
- ot->exec = gp_snap_to_grid;
- ot->poll = gp_snap_poll;
+ /* callbacks */
+ ot->exec = gp_snap_to_grid;
+ ot->poll = gp_snap_poll;
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ------------------------------- */
static int gp_snap_to_cursor(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
-
- Scene *scene = CTX_data_scene(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- Object *obact = CTX_data_active_object(C);
-
- const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
- const float *cursor_global = scene->cursor.location;
-
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- /* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
- bGPDframe *gpf = gpl->actframe;
- float diff_mat[4][4];
-
- /* calculate difference matrix */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
-
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
- bGPDspoint *pt;
- int i;
-
- /* 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(obact, gpl, gps) == false)
- continue;
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0)
- continue;
-
- if (use_offset) {
- float offset[3];
-
- /* compute offset from first point of stroke to cursor */
- /* TODO: Allow using midpoint instead? */
- sub_v3_v3v3(offset, cursor_global, &gps->points->x);
-
- /* apply offset to all points in the stroke */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- add_v3_v3(&pt->x, offset);
- }
- }
- else {
- /* affect each selected point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- copy_v3_v3(&pt->x, cursor_global);
- gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
- }
- }
- }
- }
-
- }
- }
-
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ Scene *scene = CTX_data_scene(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ Object *obact = CTX_data_active_object(C);
+
+ const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
+ const float *cursor_global = scene->cursor.location;
+
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ bGPDframe *gpf = gpl->actframe;
+ float diff_mat[4][4];
+
+ /* calculate difference matrix */
+ ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ bGPDspoint *pt;
+ int i;
+
+ /* 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(obact, gpl, gps) == false)
+ continue;
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0)
+ continue;
+
+ if (use_offset) {
+ float offset[3];
+
+ /* compute offset from first point of stroke to cursor */
+ /* TODO: Allow using midpoint instead? */
+ sub_v3_v3v3(offset, cursor_global, &gps->points->x);
+
+ /* apply offset to all points in the stroke */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ add_v3_v3(&pt->x, offset);
+ }
+ }
+ else {
+ /* affect each selected point */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ copy_v3_v3(&pt->x, cursor_global);
+ gp_apply_parent_point(depsgraph, obact, gpd, gpl, pt);
+ }
+ }
+ }
+ }
+ }
+ }
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(&obact->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Snap Selection to Cursor";
- ot->idname = "GPENCIL_OT_snap_to_cursor";
- ot->description = "Snap selected points/strokes to the cursor";
-
- /* callbacks */
- ot->exec = gp_snap_to_cursor;
- ot->poll = gp_snap_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* props */
- ot->prop = RNA_def_boolean(
- ot->srna, "use_offset", true, "With Offset",
- "Offset the entire stroke instead of selected points only");
+ /* identifiers */
+ ot->name = "Snap Selection to Cursor";
+ ot->idname = "GPENCIL_OT_snap_to_cursor";
+ ot->description = "Snap selected points/strokes to the cursor";
+
+ /* callbacks */
+ ot->exec = gp_snap_to_cursor;
+ ot->poll = gp_snap_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* props */
+ ot->prop = RNA_def_boolean(ot->srna,
+ "use_offset",
+ true,
+ "With Offset",
+ "Offset the entire stroke instead of selected points only");
}
/* ------------------------------- */
static int gp_snap_cursor_to_sel(bContext *C, wmOperator *UNUSED(op))
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
-
- Scene *scene = CTX_data_scene(C);
- View3D *v3d = CTX_wm_view3d(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- Object *obact = CTX_data_active_object(C);
-
- float *cursor = scene->cursor.location;
- float centroid[3] = {0.0f};
- float min[3], max[3];
- size_t count = 0;
-
- INIT_MINMAX(min, max);
-
- /* calculate midpoints from selected points */
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- /* only editable and visible layers are considered */
- if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
- bGPDframe *gpf = gpl->actframe;
- float diff_mat[4][4];
-
- /* calculate difference matrix */
- ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
-
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
- bGPDspoint *pt;
- int i;
-
- /* 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(obact, gpl, gps) == false)
- continue;
- /* only continue if this stroke is selected (editable doesn't guarantee this)... */
- if ((gps->flag & GP_STROKE_SELECT) == 0)
- continue;
-
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- /* apply parent transformations */
- float fpt[3];
- mul_v3_m4v3(fpt, diff_mat, &pt->x);
-
- add_v3_v3(centroid, fpt);
- minmax_v3v3_v3(min, max, fpt);
-
- count++;
- }
- }
-
- }
- }
- }
-
- if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_MEDIAN && count) {
- mul_v3_fl(centroid, 1.0f / (float)count);
- copy_v3_v3(cursor, centroid);
- }
- else {
- mid_v3_v3v3(cursor, min, max);
- }
-
-
- DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ Scene *scene = CTX_data_scene(C);
+ View3D *v3d = CTX_wm_view3d(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ Object *obact = CTX_data_active_object(C);
+
+ float *cursor = scene->cursor.location;
+ float centroid[3] = {0.0f};
+ float min[3], max[3];
+ size_t count = 0;
+
+ INIT_MINMAX(min, max);
+
+ /* calculate midpoints from selected points */
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ /* only editable and visible layers are considered */
+ if (gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
+ bGPDframe *gpf = gpl->actframe;
+ float diff_mat[4][4];
+
+ /* calculate difference matrix */
+ ED_gpencil_parent_location(depsgraph, obact, gpd, gpl, diff_mat);
+
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
+ bGPDspoint *pt;
+ int i;
+
+ /* 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(obact, gpl, gps) == false)
+ continue;
+ /* only continue if this stroke is selected (editable doesn't guarantee this)... */
+ if ((gps->flag & GP_STROKE_SELECT) == 0)
+ continue;
+
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ /* apply parent transformations */
+ float fpt[3];
+ mul_v3_m4v3(fpt, diff_mat, &pt->x);
+
+ add_v3_v3(centroid, fpt);
+ minmax_v3v3_v3(min, max, fpt);
+
+ count++;
+ }
+ }
+ }
+ }
+ }
+
+ if (scene->toolsettings->transform_pivot_point == V3D_AROUND_CENTER_MEDIAN && count) {
+ mul_v3_fl(centroid, 1.0f / (float)count);
+ copy_v3_v3(cursor, centroid);
+ }
+ else {
+ mid_v3_v3v3(cursor, min, max);
+ }
+
+ DEG_id_tag_update(&scene->id, ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_SPACE | ND_SPACE_VIEW3D, v3d);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Snap Cursor to Selected Points";
- ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
- ot->description = "Snap cursor to center of selected points";
+ /* identifiers */
+ ot->name = "Snap Cursor to Selected Points";
+ ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
+ ot->description = "Snap cursor to center of selected points";
- /* callbacks */
- ot->exec = gp_snap_cursor_to_sel;
- ot->poll = gp_snap_poll;
+ /* callbacks */
+ ot->exec = gp_snap_cursor_to_sel;
+ ot->poll = gp_snap_poll;
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* flags */
+ 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 */
- if ((gps->thickness == 0) && (gpl->line_change == 0)) {
- gps->thickness = gpl->thickness;
- }
- else {
- gps->thickness = gps->thickness + gpl->line_change;
- }
- }
- }
- /* clear value */
- gpl->thickness = 0.0f;
- gpl->line_change = 0;
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ 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 */
+ if ((gps->thickness == 0) && (gpl->line_change == 0)) {
+ gps->thickness = gpl->thickness;
+ }
+ else {
+ gps->thickness = gps->thickness + gpl->line_change;
+ }
+ }
+ }
+ /* clear value */
+ gpl->thickness = 0.0f;
+ gpl->line_change = 0;
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ 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;
+ /* 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,
+ 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);
- Object *ob = CTX_data_active_object(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)
- {
- if (gpl->actframe == NULL)
- continue;
-
- for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
- MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
-
- /* 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 (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) || (gp_style->flag & GP_STYLE_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 */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ Object *ob = CTX_data_active_object(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) {
+ if (gpl->actframe == NULL)
+ continue;
+
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
+
+ /* 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 (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) ||
+ (gp_style->flag & GP_STYLE_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 */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
/**
@@ -2807,101 +2835,92 @@ static int gp_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
*/
void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
{
- static const 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;
-
- /* properties */
- ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
+ static const 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;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
}
/* ******************* Flat Stroke Caps ************************** */
enum {
- GP_STROKE_CAPS_TOGGLE_BOTH = 0,
- GP_STROKE_CAPS_TOGGLE_START = 1,
- GP_STROKE_CAPS_TOGGLE_END = 2,
- GP_STROKE_CAPS_TOGGLE_DEFAULT = 3,
+ GP_STROKE_CAPS_TOGGLE_BOTH = 0,
+ GP_STROKE_CAPS_TOGGLE_START = 1,
+ GP_STROKE_CAPS_TOGGLE_END = 2,
+ GP_STROKE_CAPS_TOGGLE_DEFAULT = 3,
};
static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- Object *ob = CTX_data_active_object(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)
- {
- if (gpl->actframe == NULL)
- continue;
-
- for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
- MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
-
- /* 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 (!gp_style ||
- (gp_style->flag & GP_STYLE_COLOR_HIDE) ||
- (gp_style->flag & GP_STYLE_COLOR_LOCKED))
- {
- continue;
- }
-
- if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) ||
- (type == GP_STROKE_CAPS_TOGGLE_START))
- {
- ++gps->caps[0];
- if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
- gps->caps[0] = GP_STROKE_CAP_ROUND;
- }
- }
- if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) ||
- (type == GP_STROKE_CAPS_TOGGLE_END))
- {
- ++gps->caps[1];
- if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
- gps->caps[1] = GP_STROKE_CAP_ROUND;
- }
- }
- if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
- gps->caps[0] = GP_STROKE_CAP_ROUND;
- gps->caps[1] = GP_STROKE_CAP_ROUND;
- }
- }
- }
- CTX_DATA_END;
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ Object *ob = CTX_data_active_object(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) {
+ if (gpl->actframe == NULL)
+ continue;
+
+ for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
+ MaterialGPencilStyle *gp_style = BKE_material_gpencil_settings_get(ob, gps->mat_nr + 1);
+
+ /* 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 (!gp_style || (gp_style->flag & GP_STYLE_COLOR_HIDE) ||
+ (gp_style->flag & GP_STYLE_COLOR_LOCKED)) {
+ continue;
+ }
+
+ if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) || (type == GP_STROKE_CAPS_TOGGLE_START)) {
+ ++gps->caps[0];
+ if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
+ gps->caps[0] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ if ((type == GP_STROKE_CAPS_TOGGLE_BOTH) || (type == GP_STROKE_CAPS_TOGGLE_END)) {
+ ++gps->caps[1];
+ if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
+ gps->caps[1] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ if (type == GP_STROKE_CAPS_TOGGLE_DEFAULT) {
+ gps->caps[0] = GP_STROKE_CAP_ROUND;
+ gps->caps[1] = GP_STROKE_CAP_ROUND;
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
/**
@@ -2909,28 +2928,28 @@ static int gp_stroke_caps_set_exec(bContext *C, wmOperator *op)
*/
void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
{
- static const EnumPropertyItem toggle_type[] = {
- {GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
- {GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
- {GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
- {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"},
- {0, NULL, 0, NULL, NULL},
- };
-
- /* identifiers */
- ot->name = "Set Caps Mode";
- ot->idname = "GPENCIL_OT_stroke_caps_set";
- ot->description = "Change Stroke caps mode (rounded or flat)";
-
- /* api callbacks */
- ot->exec = gp_stroke_caps_set_exec;
- ot->poll = gp_active_layer_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", "");
+ static const EnumPropertyItem toggle_type[] = {
+ {GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
+ {GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
+ {GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
+ {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Set Caps Mode";
+ ot->idname = "GPENCIL_OT_stroke_caps_set";
+ ot->description = "Change Stroke caps mode (rounded or flat)";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_caps_set_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", "");
}
/* ******************* Stroke join ************************** */
@@ -2938,1523 +2957,1524 @@ void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
/* Helper: flip stroke */
static void gpencil_flip_stroke(bGPDstroke *gps)
{
- int end = gps->totpoints - 1;
-
- for (int i = 0; i < gps->totpoints / 2; i++) {
- bGPDspoint *point, *point2;
- bGPDspoint pt;
-
- /* 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--;
- }
+ int end = gps->totpoints - 1;
+
+ for (int i = 0; i < gps->totpoints / 2; i++) {
+ bGPDspoint *point, *point2;
+ bGPDspoint pt;
+
+ /* 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, int idx, float delta[3],
- float pressure, float strength, float deltatime)
+static void gpencil_stroke_copy_point(bGPDstroke *gps,
+ bGPDspoint *point,
+ int idx,
+ float delta[3],
+ float pressure,
+ float strength,
+ float deltatime)
{
- bGPDspoint *newpoint;
-
- gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
- if (gps->dvert != NULL) {
- gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (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;
-
- if (gps->dvert != NULL) {
- MDeformVert *dvert = &gps->dvert[idx];
- MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1];
-
- newdvert->totweight = dvert->totweight;
- newdvert->dw = MEM_dupallocN(dvert->dw);
- }
+ bGPDspoint *newpoint;
+
+ gps->points = MEM_reallocN(gps->points, sizeof(bGPDspoint) * (gps->totpoints + 1));
+ if (gps->dvert != NULL) {
+ gps->dvert = MEM_reallocN(gps->dvert, sizeof(MDeformVert) * (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;
+
+ if (gps->dvert != NULL) {
+ MDeformVert *dvert = &gps->dvert[idx];
+ MDeformVert *newdvert = &gps->dvert[gps->totpoints - 1];
+
+ newdvert->totweight = dvert->totweight;
+ newdvert->dw = MEM_dupallocN(dvert->dw);
+ }
}
/* 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)
+static void gpencil_stroke_join_strokes(bGPDstroke *gps_a,
+ bGPDstroke *gps_b,
+ const bool leave_gaps)
{
- bGPDspoint point;
- bGPDspoint *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, gps_a->totpoints - 1, 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, 0, 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, i, delta, pt->pressure, pt->strength, deltatime);
- }
- }
+ bGPDspoint point;
+ bGPDspoint *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, gps_a->totpoints - 1, 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, 0, 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, i, 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;
- Object *ob = CTX_data_active_object(C);
-
- 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;
- if (gpf == NULL)
- continue;
-
- 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(ob, 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);
- if (stroke_a->dvert != NULL) {
- new_stroke->dvert = MEM_dupallocN(stroke_a->dvert);
- BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke);
- }
- new_stroke->triangles = NULL;
- new_stroke->tot_triangles = 0;
- new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
-
- /* if new, set current color */
- if (type == GP_STROKE_JOINCOPY) {
- new_stroke->mat_nr = stroke_a->mat_nr;
- }
- }
-
- /* 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 */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDlayer *activegpl = BKE_gpencil_layer_getactive(gpd);
+ bGPDstroke *gps, *gpsn;
+ Object *ob = CTX_data_active_object(C);
+
+ 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;
+ if (gpf == NULL)
+ continue;
+
+ 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(ob, 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);
+ if (stroke_a->dvert != NULL) {
+ new_stroke->dvert = MEM_dupallocN(stroke_a->dvert);
+ BKE_gpencil_stroke_weights_duplicate(stroke_a, new_stroke);
+ }
+ new_stroke->triangles = NULL;
+ new_stroke->tot_triangles = 0;
+ new_stroke->flag |= GP_STROKE_RECALC_GEOMETRY;
+
+ /* if new, set current color */
+ if (type == GP_STROKE_JOINCOPY) {
+ new_stroke->mat_nr = stroke_a->mat_nr;
+ }
+ }
+
+ /* 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 */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_join(wmOperatorType *ot)
{
- static const 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;
-
- /* properties */
- 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");
+ static const 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;
+
+ /* properties */
+ 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);
- Object *ob = CTX_data_active_object(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;
- if (gpf == NULL)
- continue;
-
- 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(ob, gpl, gps) == false) {
- continue;
- }
-
- /* flip stroke */
- gpencil_flip_stroke(gps);
- }
- }
- }
- CTX_DATA_END;
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ Object *ob = CTX_data_active_object(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;
+ if (gpf == NULL)
+ continue;
+
+ 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(ob, gpl, gps) == false) {
+ continue;
+ }
+
+ /* flip stroke */
+ gpencil_flip_stroke(gps);
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ 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";
+ /* 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;
+ /* api callbacks */
+ ot->exec = gp_stroke_flip_exec;
+ ot->poll = gp_active_layer_poll;
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ***************** Reproject Strokes ********************** */
typedef enum eGP_ReprojectModes {
- /* Axis */
- GP_REPROJECT_FRONT = 0,
- GP_REPROJECT_SIDE,
- GP_REPROJECT_TOP,
- /* On same plane, parallel to viewplane */
- GP_REPROJECT_VIEW,
- /* Reprojected on to the scene geometry */
- GP_REPROJECT_SURFACE,
- /* Reprojected on 3D cursor orientation */
- GP_REPROJECT_CURSOR,
+ /* Axis */
+ GP_REPROJECT_FRONT = 0,
+ GP_REPROJECT_SIDE,
+ GP_REPROJECT_TOP,
+ /* On same plane, parallel to viewplane */
+ GP_REPROJECT_VIEW,
+ /* Reprojected on to the scene geometry */
+ GP_REPROJECT_SURFACE,
+ /* Reprojected on 3D cursor orientation */
+ GP_REPROJECT_CURSOR,
} eGP_ReprojectModes;
static int gp_strokes_reproject_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- Scene *scene = CTX_data_scene(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- Depsgraph *depsgraph = CTX_data_depsgraph(C);
- Object *ob = CTX_data_active_object(C);
- ARegion *ar = CTX_wm_region(C);
- RegionView3D *rv3d = ar->regiondata;
-
- GP_SpaceConversion gsc = {NULL};
- eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
-
- float origin[3];
-
- /* init space conversion stuff */
- gp_point_conversion_init(C, &gsc);
-
- /* init autodist for geometry projection */
- if (mode == GP_REPROJECT_SURFACE) {
- view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar);
- ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0);
- }
-
- // TODO: For deforming geometry workflow, create new frames?
-
- /* Go through each editable + selected stroke, adjusting each of its points one by one... */
- GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
- {
- if (gps->flag & GP_STROKE_SELECT) {
- bGPDspoint *pt;
- int i;
- float inverse_diff_mat[4][4];
-
- /* Compute inverse matrix for unapplying parenting once instead of doing per-point */
- /* TODO: add this bit to the iteration macro? */
- invert_m4_m4(inverse_diff_mat, gpstroke_iter.diff_mat);
-
- /* Adjust each point */
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- float xy[2];
-
- /* 3D to Screenspace */
- /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace
- * coordinates, resulting in lost precision, which in turn causes stairstepping
- * artifacts in the final points.
- */
- bGPDspoint pt2;
- gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
- gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
-
- /* Project stroke in one axis */
- if (ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE,
- GP_REPROJECT_TOP, GP_REPROJECT_CURSOR))
- {
- if (mode != GP_REPROJECT_CURSOR) {
- ED_gp_get_drawing_reference(
- scene, ob, gpl,
- ts->gpencil_v3d_align, origin);
- }
- else {
- copy_v3_v3(origin, scene->cursor.location);
- }
-
- int axis = 0;
- switch (mode) {
- case GP_REPROJECT_FRONT:
- {
- axis = 1;
- break;
- }
- case GP_REPROJECT_SIDE:
- {
- axis = 0;
- break;
- }
- case GP_REPROJECT_TOP:
- {
- axis = 2;
- break;
- }
- case GP_REPROJECT_CURSOR:
- {
- axis = 3;
- break;
- }
- default:
- {
- axis = 1;
- break;
- }
- }
-
- ED_gp_project_point_to_plane(
- scene, ob, rv3d, origin,
- axis, &pt2);
-
- copy_v3_v3(&pt->x, &pt2.x);
-
- /* apply parent again */
- gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt);
- }
- /* Project screenspace back to 3D space (from current perspective)
- * so that all points have been treated the same way
- */
- else if (mode == GP_REPROJECT_VIEW) {
- /* Planar - All on same plane parallel to the viewplane */
- gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
- }
- else {
- /* Geometry - Snap to surfaces of visible geometry */
- /* XXX: There will be precision loss (possible stairstep artifacts)
- * from this conversion to satisfy the API's */
- const int screen_co[2] = {(int)xy[0], (int)xy[1]};
-
- int depth_margin = 0; // XXX: 4 for strokes, 0 for normal
- float depth;
-
- /* XXX: The proper procedure computes the depths into an array,
- * to have smooth transitions when all else fails... */
- if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) {
- ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth);
- }
- else {
- /* Default to planar */
- gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
- }
- }
-
- /* Unapply parent corrections */
- if (!ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP)) {
- mul_m4_v3(inverse_diff_mat, &pt->x);
- }
- }
- }
- }
- GP_EDITABLE_STROKES_END(gpstroke_iter);
-
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ Scene *scene = CTX_data_scene(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ Depsgraph *depsgraph = CTX_data_depsgraph(C);
+ Object *ob = CTX_data_active_object(C);
+ ARegion *ar = CTX_wm_region(C);
+ RegionView3D *rv3d = ar->regiondata;
+
+ GP_SpaceConversion gsc = {NULL};
+ eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
+
+ float origin[3];
+
+ /* init space conversion stuff */
+ gp_point_conversion_init(C, &gsc);
+
+ /* init autodist for geometry projection */
+ if (mode == GP_REPROJECT_SURFACE) {
+ view3d_region_operator_needs_opengl(CTX_wm_window(C), gsc.ar);
+ ED_view3d_autodist_init(depsgraph, gsc.ar, CTX_wm_view3d(C), 0);
+ }
+
+ // TODO: For deforming geometry workflow, create new frames?
+
+ /* Go through each editable + selected stroke, adjusting each of its points one by one... */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ bGPDspoint *pt;
+ int i;
+ float inverse_diff_mat[4][4];
+
+ /* Compute inverse matrix for unapplying parenting once instead of doing per-point */
+ /* TODO: add this bit to the iteration macro? */
+ invert_m4_m4(inverse_diff_mat, gpstroke_iter.diff_mat);
+
+ /* Adjust each point */
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ float xy[2];
+
+ /* 3D to Screenspace */
+ /* Note: We can't use gp_point_to_xy() here because that uses ints for the screenspace
+ * coordinates, resulting in lost precision, which in turn causes stairstepping
+ * artifacts in the final points.
+ */
+ bGPDspoint pt2;
+ gp_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
+ gp_point_to_xy_fl(&gsc, gps, &pt2, &xy[0], &xy[1]);
+
+ /* Project stroke in one axis */
+ if (ELEM(mode,
+ GP_REPROJECT_FRONT,
+ GP_REPROJECT_SIDE,
+ GP_REPROJECT_TOP,
+ GP_REPROJECT_CURSOR)) {
+ if (mode != GP_REPROJECT_CURSOR) {
+ ED_gp_get_drawing_reference(scene, ob, gpl, ts->gpencil_v3d_align, origin);
+ }
+ else {
+ copy_v3_v3(origin, scene->cursor.location);
+ }
+
+ int axis = 0;
+ switch (mode) {
+ case GP_REPROJECT_FRONT: {
+ axis = 1;
+ break;
+ }
+ case GP_REPROJECT_SIDE: {
+ axis = 0;
+ break;
+ }
+ case GP_REPROJECT_TOP: {
+ axis = 2;
+ break;
+ }
+ case GP_REPROJECT_CURSOR: {
+ axis = 3;
+ break;
+ }
+ default: {
+ axis = 1;
+ break;
+ }
+ }
+
+ ED_gp_project_point_to_plane(scene, ob, rv3d, origin, axis, &pt2);
+
+ copy_v3_v3(&pt->x, &pt2.x);
+
+ /* apply parent again */
+ gp_apply_parent_point(depsgraph, ob, gpd, gpl, pt);
+ }
+ /* Project screenspace back to 3D space (from current perspective)
+ * so that all points have been treated the same way
+ */
+ else if (mode == GP_REPROJECT_VIEW) {
+ /* Planar - All on same plane parallel to the viewplane */
+ gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
+ }
+ else {
+ /* Geometry - Snap to surfaces of visible geometry */
+ /* XXX: There will be precision loss (possible stairstep artifacts)
+ * from this conversion to satisfy the API's */
+ const int screen_co[2] = {(int)xy[0], (int)xy[1]};
+
+ int depth_margin = 0; // XXX: 4 for strokes, 0 for normal
+ float depth;
+
+ /* XXX: The proper procedure computes the depths into an array,
+ * to have smooth transitions when all else fails... */
+ if (ED_view3d_autodist_depth(gsc.ar, screen_co, depth_margin, &depth)) {
+ ED_view3d_autodist_simple(gsc.ar, screen_co, &pt->x, 0, &depth);
+ }
+ else {
+ /* Default to planar */
+ gp_point_xy_to_3d(&gsc, scene, xy, &pt->x);
+ }
+ }
+
+ /* Unapply parent corrections */
+ if (!ELEM(mode, GP_REPROJECT_FRONT, GP_REPROJECT_SIDE, GP_REPROJECT_TOP)) {
+ mul_m4_v3(inverse_diff_mat, &pt->x);
+ }
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_reproject(wmOperatorType *ot)
{
- static const EnumPropertyItem reproject_type[] = {
- {GP_REPROJECT_FRONT, "FRONT", 0, "Front",
- "Reproject the strokes using the X-Z plane"},
- {GP_REPROJECT_SIDE, "SIDE", 0, "Side",
- "Reproject the strokes using the Y-Z plane"},
- {GP_REPROJECT_TOP, "TOP", 0, "Top",
- "Reproject the strokes using the X-Y plane"},
- {GP_REPROJECT_VIEW, "VIEW", 0, "View",
- "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
- "using 'Cursor' Stroke Placement"},
- {GP_REPROJECT_SURFACE, "SURFACE", 0, "Surface",
- "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"},
- {GP_REPROJECT_CURSOR, "CURSOR", 0, "Cursor",
- "Reproject the strokes using the orienation of 3D cursor"},
- {0, NULL, 0, NULL, NULL},
- };
-
- /* identifiers */
- ot->name = "Reproject Strokes";
- ot->idname = "GPENCIL_OT_reproject";
- ot->description = "Reproject the selected strokes from the current viewpoint as if they had been newly drawn "
- "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
- "or for matching deforming geometry)";
-
- /* callbacks */
- ot->invoke = WM_menu_invoke;
- ot->exec = gp_strokes_reproject_exec;
- ot->poll = gp_strokes_edit3d_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
-
- /* properties */
- ot->prop = RNA_def_enum(ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
+ static const EnumPropertyItem reproject_type[] = {
+ {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
+ {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
+ {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
+ {GP_REPROJECT_VIEW,
+ "VIEW",
+ 0,
+ "View",
+ "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
+ "using 'Cursor' Stroke Placement"},
+ {GP_REPROJECT_SURFACE,
+ "SURFACE",
+ 0,
+ "Surface",
+ "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"},
+ {GP_REPROJECT_CURSOR,
+ "CURSOR",
+ 0,
+ "Cursor",
+ "Reproject the strokes using the orienation of 3D cursor"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Reproject Strokes";
+ ot->idname = "GPENCIL_OT_reproject";
+ ot->description =
+ "Reproject the selected strokes from the current viewpoint as if they had been newly drawn "
+ "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
+ "or for matching deforming geometry)";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = gp_strokes_reproject_exec;
+ ot->poll = gp_strokes_edit3d_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
+
+ /* properties */
+ ot->prop = RNA_def_enum(
+ ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
}
/* ******************* Stroke subdivide ************************** */
/* helper to smooth */
static void gp_smooth_stroke(bContext *C, wmOperator *op)
{
- const int repeat = RNA_int_get(op->ptr, "repeat");
- float factor = RNA_float_get(op->ptr, "factor");
- const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
- const bool smooth_position = RNA_boolean_get(op->ptr, "smooth_position");
- const bool smooth_thickness = RNA_boolean_get(op->ptr, "smooth_thickness");
- const bool smooth_strength = RNA_boolean_get(op->ptr, "smooth_strength");
- const bool smooth_uv = RNA_boolean_get(op->ptr, "smooth_uv");
-
- if (factor == 0.0f) {
- return;
- }
-
- GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
- {
- if (gps->flag & GP_STROKE_SELECT) {
- for (int r = 0; r < repeat; r++) {
- for (int i = 0; i < gps->totpoints; i++) {
- bGPDspoint *pt = &gps->points[i];
- if ((only_selected) && ((pt->flag & GP_SPOINT_SELECT) == 0)) {
- continue;
- }
-
- /* perform smoothing */
- if (smooth_position) {
- BKE_gpencil_smooth_stroke(gps, i, factor);
- }
- if (smooth_strength) {
- BKE_gpencil_smooth_stroke_strength(gps, i, factor);
- }
- if (smooth_thickness) {
- /* thickness need to repeat process several times */
- for (int r2 = 0; r2 < r * 10; r2++) {
- BKE_gpencil_smooth_stroke_thickness(gps, i, factor);
- }
- }
- if (smooth_uv) {
- BKE_gpencil_smooth_stroke_uv(gps, i, factor);
- }
- }
- }
- }
- }
- GP_EDITABLE_STROKES_END(gpstroke_iter);
+ const int repeat = RNA_int_get(op->ptr, "repeat");
+ float factor = RNA_float_get(op->ptr, "factor");
+ const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
+ const bool smooth_position = RNA_boolean_get(op->ptr, "smooth_position");
+ const bool smooth_thickness = RNA_boolean_get(op->ptr, "smooth_thickness");
+ const bool smooth_strength = RNA_boolean_get(op->ptr, "smooth_strength");
+ const bool smooth_uv = RNA_boolean_get(op->ptr, "smooth_uv");
+
+ if (factor == 0.0f) {
+ return;
+ }
+
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ for (int r = 0; r < repeat; r++) {
+ for (int i = 0; i < gps->totpoints; i++) {
+ bGPDspoint *pt = &gps->points[i];
+ if ((only_selected) && ((pt->flag & GP_SPOINT_SELECT) == 0)) {
+ continue;
+ }
+
+ /* perform smoothing */
+ if (smooth_position) {
+ BKE_gpencil_smooth_stroke(gps, i, factor);
+ }
+ if (smooth_strength) {
+ BKE_gpencil_smooth_stroke_strength(gps, i, factor);
+ }
+ if (smooth_thickness) {
+ /* thickness need to repeat process several times */
+ for (int r2 = 0; r2 < r * 10; r2++) {
+ BKE_gpencil_smooth_stroke_thickness(gps, i, factor);
+ }
+ }
+ if (smooth_uv) {
+ BKE_gpencil_smooth_stroke_uv(gps, i, factor);
+ }
+ }
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
}
/* helper: Count how many points need to be inserted */
static int gp_count_subdivision_cuts(bGPDstroke *gps)
{
- bGPDspoint *pt;
- int i;
- int totnewpoints = 0;
- for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- if (i + 1 < gps->totpoints) {
- if (gps->points[i + 1].flag & GP_SPOINT_SELECT) {
- totnewpoints++;
- }
- }
- }
- }
-
- return totnewpoints;
+ bGPDspoint *pt;
+ int i;
+ int totnewpoints = 0;
+ for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (i + 1 < gps->totpoints) {
+ if (gps->points[i + 1].flag & GP_SPOINT_SELECT) {
+ totnewpoints++;
+ }
+ }
+ }
+ }
+
+ return totnewpoints;
}
static int gp_stroke_subdivide_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDspoint *temp_points;
- const int cuts = RNA_int_get(op->ptr, "number_cuts");
-
- int totnewpoints, oldtotpoints;
- int i2;
-
- /* sanity checks */
- if (ELEM(NULL, gpd))
- return OPERATOR_CANCELLED;
-
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
- {
- if (gps->flag & GP_STROKE_SELECT) {
- /* loop as many times as cuts */
- for (int s = 0; s < cuts; s++) {
- totnewpoints = gp_count_subdivision_cuts(gps);
- if (totnewpoints == 0) {
- continue;
- }
- /* duplicate points in a temp area */
- temp_points = MEM_dupallocN(gps->points);
- oldtotpoints = gps->totpoints;
-
- MDeformVert *temp_dverts = NULL;
- MDeformVert *dvert_final = NULL;
- MDeformVert *dvert = NULL;
- MDeformVert *dvert_next = NULL;
- if (gps->dvert != NULL) {
- temp_dverts = MEM_dupallocN(gps->dvert);
- }
-
- /* resize the points arrays */
- gps->totpoints += totnewpoints;
- gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
- if (gps->dvert != NULL) {
- gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
- }
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
-
- /* loop and interpolate */
- i2 = 0;
- for (int i = 0; i < oldtotpoints; i++) {
- bGPDspoint *pt = &temp_points[i];
- bGPDspoint *pt_final = &gps->points[i2];
-
- /* copy current point */
- copy_v3_v3(&pt_final->x, &pt->x);
- pt_final->pressure = pt->pressure;
- pt_final->strength = pt->strength;
- pt_final->time = pt->time;
- pt_final->flag = pt->flag;
-
- if (gps->dvert != NULL) {
- dvert = &temp_dverts[i];
- dvert_final = &gps->dvert[i2];
- dvert_final->totweight = dvert->totweight;
- dvert_final->dw = dvert->dw;
- }
- i2++;
-
- /* if next point is selected add a half way point */
- if (pt->flag & GP_SPOINT_SELECT) {
- if (i + 1 < oldtotpoints) {
- if (temp_points[i + 1].flag & GP_SPOINT_SELECT) {
- pt_final = &gps->points[i2];
- if (gps->dvert != NULL) {
- dvert_final = &gps->dvert[i2];
- }
- /* Interpolate all values */
- bGPDspoint *next = &temp_points[i + 1];
- interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
- pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
- pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
- CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
- pt_final->time = interpf(pt->time, next->time, 0.5f);
- pt_final->flag |= GP_SPOINT_SELECT;
-
- /* interpolate weights */
- if (gps->dvert != NULL) {
- dvert = &temp_dverts[i];
- dvert_next = &temp_dverts[i + 1];
- dvert_final = &gps->dvert[i2];
-
- dvert_final->totweight = dvert->totweight;
- dvert_final->dw = MEM_dupallocN(dvert->dw);
-
- /* interpolate weight values */
- for (int d = 0; d < dvert->totweight; d++) {
- MDeformWeight *dw_a = &dvert->dw[d];
- if (dvert_next->totweight > d) {
- MDeformWeight *dw_b = &dvert_next->dw[d];
- MDeformWeight *dw_final = &dvert_final->dw[d];
- dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
- }
- }
- }
-
- i2++;
- }
- }
- }
- }
- /* free temp memory */
- MEM_SAFE_FREE(temp_points);
- MEM_SAFE_FREE(temp_dverts);
- }
-
- /* triangles cache needs to be recalculated */
- gps->flag |= GP_STROKE_RECALC_GEOMETRY;
- gps->tot_triangles = 0;
- }
- }
- GP_EDITABLE_STROKES_END(gpstroke_iter);
-
- /* smooth stroke */
- gp_smooth_stroke(C, op);
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDspoint *temp_points;
+ const int cuts = RNA_int_get(op->ptr, "number_cuts");
+
+ int totnewpoints, oldtotpoints;
+ int i2;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* loop as many times as cuts */
+ for (int s = 0; s < cuts; s++) {
+ totnewpoints = gp_count_subdivision_cuts(gps);
+ if (totnewpoints == 0) {
+ continue;
+ }
+ /* duplicate points in a temp area */
+ temp_points = MEM_dupallocN(gps->points);
+ oldtotpoints = gps->totpoints;
+
+ MDeformVert *temp_dverts = NULL;
+ MDeformVert *dvert_final = NULL;
+ MDeformVert *dvert = NULL;
+ MDeformVert *dvert_next = NULL;
+ if (gps->dvert != NULL) {
+ temp_dverts = MEM_dupallocN(gps->dvert);
+ }
+
+ /* resize the points arrays */
+ gps->totpoints += totnewpoints;
+ gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
+ if (gps->dvert != NULL) {
+ gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
+ }
+ gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+
+ /* loop and interpolate */
+ i2 = 0;
+ for (int i = 0; i < oldtotpoints; i++) {
+ bGPDspoint *pt = &temp_points[i];
+ bGPDspoint *pt_final = &gps->points[i2];
+
+ /* copy current point */
+ copy_v3_v3(&pt_final->x, &pt->x);
+ pt_final->pressure = pt->pressure;
+ pt_final->strength = pt->strength;
+ pt_final->time = pt->time;
+ pt_final->flag = pt->flag;
+
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[i];
+ dvert_final = &gps->dvert[i2];
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = dvert->dw;
+ }
+ i2++;
+
+ /* if next point is selected add a half way point */
+ if (pt->flag & GP_SPOINT_SELECT) {
+ if (i + 1 < oldtotpoints) {
+ if (temp_points[i + 1].flag & GP_SPOINT_SELECT) {
+ pt_final = &gps->points[i2];
+ if (gps->dvert != NULL) {
+ dvert_final = &gps->dvert[i2];
+ }
+ /* Interpolate all values */
+ bGPDspoint *next = &temp_points[i + 1];
+ interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
+ pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
+ pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
+ CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
+ pt_final->time = interpf(pt->time, next->time, 0.5f);
+ pt_final->flag |= GP_SPOINT_SELECT;
+
+ /* interpolate weights */
+ if (gps->dvert != NULL) {
+ dvert = &temp_dverts[i];
+ dvert_next = &temp_dverts[i + 1];
+ dvert_final = &gps->dvert[i2];
+
+ dvert_final->totweight = dvert->totweight;
+ dvert_final->dw = MEM_dupallocN(dvert->dw);
+
+ /* interpolate weight values */
+ for (int d = 0; d < dvert->totweight; d++) {
+ MDeformWeight *dw_a = &dvert->dw[d];
+ if (dvert_next->totweight > d) {
+ MDeformWeight *dw_b = &dvert_next->dw[d];
+ MDeformWeight *dw_final = &dvert_final->dw[d];
+ dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
+ }
+ }
+ }
+
+ i2++;
+ }
+ }
+ }
+ }
+ /* free temp memory */
+ MEM_SAFE_FREE(temp_points);
+ MEM_SAFE_FREE(temp_dverts);
+ }
+
+ /* triangles cache needs to be recalculated */
+ gps->flag |= GP_STROKE_RECALC_GEOMETRY;
+ gps->tot_triangles = 0;
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* smooth stroke */
+ gp_smooth_stroke(C, op);
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Subdivide Stroke";
- ot->idname = "GPENCIL_OT_stroke_subdivide";
- ot->description = "Subdivide between continuous selected points of the stroke adding a point half way between them";
-
- /* api callbacks */
- ot->exec = gp_stroke_subdivide_exec;
- ot->poll = gp_active_layer_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5);
- /* avoid re-using last var because it can cause _very_ high value and annoy users */
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-
- /* Smooth parameters */
- RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 2.0f, "Smooth", "", 0.0f, 2.0f);
- prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
- RNA_def_boolean(
- ot->srna, "only_selected", true, "Selected Points",
- "Smooth only selected points in the stroke");
- RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
- RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", "");
- RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
- RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
-
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Subdivide Stroke";
+ ot->idname = "GPENCIL_OT_stroke_subdivide";
+ ot->description =
+ "Subdivide between continuous selected points of the stroke adding a point half way between "
+ "them";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_subdivide_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5);
+ /* avoid re-using last var because it can cause _very_ high value and annoy users */
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ /* Smooth parameters */
+ RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 2.0f, "Smooth", "", 0.0f, 2.0f);
+ prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ RNA_def_boolean(ot->srna,
+ "only_selected",
+ true,
+ "Selected Points",
+ "Smooth only selected points in the stroke");
+ RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
+ RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", "");
+ RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
+ RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
}
/* ** simplify stroke *** */
static int gp_stroke_simplify_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- float factor = RNA_float_get(op->ptr, "factor");
-
- /* sanity checks */
- if (ELEM(NULL, gpd))
- return OPERATOR_CANCELLED;
-
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
- {
- if (gps->flag & GP_STROKE_SELECT) {
- /* simplify stroke using Ramer-Douglas-Peucker algorithm */
- BKE_gpencil_simplify_stroke(gps, factor);
- }
- }
- GP_EDITABLE_STROKES_END(gpstroke_iter);
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ float factor = RNA_float_get(op->ptr, "factor");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* simplify stroke using Ramer-Douglas-Peucker algorithm */
+ BKE_gpencil_simplify_stroke(gps, factor);
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_simplify(wmOperatorType *ot)
{
- PropertyRNA *prop;
+ PropertyRNA *prop;
- /* identifiers */
- ot->name = "Simplify Stroke";
- ot->idname = "GPENCIL_OT_stroke_simplify";
- ot->description = "Simplify selected stroked reducing number of points";
+ /* identifiers */
+ ot->name = "Simplify Stroke";
+ ot->idname = "GPENCIL_OT_stroke_simplify";
+ ot->description = "Simplify selected stroked reducing number of points";
- /* api callbacks */
- ot->exec = gp_stroke_simplify_exec;
- ot->poll = gp_active_layer_poll;
+ /* api callbacks */
+ ot->exec = gp_stroke_simplify_exec;
+ ot->poll = gp_active_layer_poll;
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- /* properties */
- prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f);
- /* avoid re-using last var */
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ /* properties */
+ prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f);
+ /* avoid re-using last var */
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ** simplify stroke using fixed algorithm *** */
static int gp_stroke_simplify_fixed_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- int steps = RNA_int_get(op->ptr, "step");
-
- /* sanity checks */
- if (ELEM(NULL, gpd))
- return OPERATOR_CANCELLED;
-
- /* Go through each editable + selected stroke */
- GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
- {
- if (gps->flag & GP_STROKE_SELECT) {
- for (int i = 0; i < steps; i++) {
- BKE_gpencil_simplify_fixed(gps);
- }
- }
- }
- GP_EDITABLE_STROKES_END(gpstroke_iter);
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ int steps = RNA_int_get(op->ptr, "step");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* Go through each editable + selected stroke */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ if (gps->flag & GP_STROKE_SELECT) {
+ for (int i = 0; i < steps; i++) {
+ BKE_gpencil_simplify_fixed(gps);
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot)
{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Simplify Fixed Stroke";
- ot->idname = "GPENCIL_OT_stroke_simplify_fixed";
- ot->description = "Simplify selected stroked reducing number of points using fixed algorithm";
+ PropertyRNA *prop;
- /* api callbacks */
- ot->exec = gp_stroke_simplify_fixed_exec;
- ot->poll = gp_active_layer_poll;
+ /* identifiers */
+ ot->name = "Simplify Fixed Stroke";
+ ot->idname = "GPENCIL_OT_stroke_simplify_fixed";
+ ot->description = "Simplify selected stroked reducing number of points using fixed algorithm";
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* api callbacks */
+ ot->exec = gp_stroke_simplify_fixed_exec;
+ ot->poll = gp_active_layer_poll;
- /* properties */
- prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Steps", "Number of simplify steps", 1, 10);
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
- /* avoid re-using last var */
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+ /* properties */
+ prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Steps", "Number of simplify steps", 1, 10);
+ /* avoid re-using last var */
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
}
/* ******************* Stroke trim ************************** */
static int gp_stroke_trim_exec(bContext *C, wmOperator *UNUSED(op))
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
-
- /* sanity checks */
- if (ELEM(NULL, gpd))
- return OPERATOR_CANCELLED;
-
- /* Go through each editable + selected stroke */
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
-
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
-
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
-
- if (gpf == NULL)
- continue;
-
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
-
- /* skip strokes that are invalid for current view */
- if (ED_gpencil_stroke_can_use(C, gps) == false)
- continue;
-
- if (gps->flag & GP_STROKE_SELECT) {
- BKE_gpencil_trim_stroke(gps);
- }
- }
- /* if not multiedit, exit loop*/
- if (!is_multiedit) {
- break;
- }
- }
- }
- }
- CTX_DATA_END;
-
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
+
+ /* Go through each editable + selected stroke */
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = gpl->actframe;
+ if (is_multiedit) {
+ init_gpf = gpl->frames.first;
+ }
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ bGPDstroke *gps, *gpsn;
+
+ if (gpf == NULL)
+ continue;
+
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+
+ /* skip strokes that are invalid for current view */
+ if (ED_gpencil_stroke_can_use(C, gps) == false)
+ continue;
+
+ if (gps->flag & GP_STROKE_SELECT) {
+ BKE_gpencil_trim_stroke(gps);
+ }
+ }
+ /* if not multiedit, exit loop*/
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_trim(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Trim Stroke";
- ot->idname = "GPENCIL_OT_stroke_trim";
- ot->description = "Trim selected stroke to first loop or intersection";
+ /* identifiers */
+ ot->name = "Trim Stroke";
+ ot->idname = "GPENCIL_OT_stroke_trim";
+ ot->description = "Trim selected stroke to first loop or intersection";
- /* api callbacks */
- ot->exec = gp_stroke_trim_exec;
- ot->poll = gp_active_layer_poll;
+ /* api callbacks */
+ ot->exec = gp_stroke_trim_exec;
+ ot->poll = gp_active_layer_poll;
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/* ***************** Separate Strokes ********************** */
typedef enum eGP_SeparateModes {
- /* Points */
- GP_SEPARATE_POINT = 0,
- /* Selected Strokes */
- GP_SEPARATE_STROKE,
- /* Current Layer */
- GP_SEPARATE_LAYER,
+ /* Points */
+ GP_SEPARATE_POINT = 0,
+ /* Selected Strokes */
+ GP_SEPARATE_STROKE,
+ /* Current Layer */
+ GP_SEPARATE_LAYER,
} eGP_SeparateModes;
static int gp_stroke_separate_exec(bContext *C, wmOperator *op)
{
- Base *base_new;
- Main *bmain = CTX_data_main(C);
- Scene *scene = CTX_data_scene(C);
- ViewLayer *view_layer = CTX_data_view_layer(C);
- Base *base_old = CTX_data_active_base(C);
- bGPdata *gpd_src = ED_gpencil_data_get_active(C);
- Object *ob = CTX_data_active_object(C);
-
- Object *ob_dst = NULL;
- bGPdata *gpd_dst = NULL;
- bGPDlayer *gpl_dst = NULL;
- bGPDframe *gpf_dst = NULL;
- bGPDspoint *pt;
- Material *ma = NULL;
- int i, idx;
-
- eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
-
- /* sanity checks */
- if (ELEM(NULL, gpd_src)) {
- return OPERATOR_CANCELLED;
- }
-
- if ((mode == GP_SEPARATE_LAYER) && (BLI_listbase_count(&gpd_src->layers) == 1)) {
- BKE_report(op->reports, RPT_ERROR, "Cannot separate an object with one layer only");
- return OPERATOR_CANCELLED;
- }
-
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
-
- /* create a new object */
- base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, 0);
- ob_dst = base_new->object;
- ob_dst->mode = OB_MODE_OBJECT;
- /* create new grease pencil datablock */
- gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2);
- ob_dst->data = (bGPdata *)gpd_dst;
-
- /* loop old datablock and separate parts */
- if ((mode == GP_SEPARATE_POINT) || (mode == GP_SEPARATE_STROKE)) {
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- gpl_dst = NULL;
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
-
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
-
- if (gpf == NULL) {
- continue;
- }
-
- gpf_dst = NULL;
-
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = 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_color_use(ob, gpl, gps) == false) {
- continue;
- }
- /* separate selected strokes */
- if (gps->flag & GP_STROKE_SELECT) {
- /* add layer if not created before */
- if (gpl_dst == NULL) {
- gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false);
- }
-
- /* add frame if not created before */
- if (gpf_dst == NULL) {
- gpf_dst = BKE_gpencil_layer_getframe(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
- }
-
- /* add duplicate materials */
- ma = give_current_material(ob, gps->mat_nr + 1); /* XXX same material can be in multiple slots */
- idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
-
- /* selected points mode */
- if (mode == GP_SEPARATE_POINT) {
- /* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps);
-
- /* reasign material */
- gps_dst->mat_nr = idx;
-
- /* link to destination frame */
- BLI_addtail(&gpf_dst->strokes, gps_dst);
-
- /* Invert selection status of all points in destination stroke */
- for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
- pt->flag ^= GP_SPOINT_SELECT;
- }
-
- /* delete selected points from destination stroke */
- gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
-
- /* delete selected points from origin stroke */
- gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
- }
- /* selected strokes mode */
- else if (mode == GP_SEPARATE_STROKE) {
- /* deselect old stroke */
- gps->flag &= ~GP_STROKE_SELECT;
- /* unlink from source frame */
- BLI_remlink(&gpf->strokes, gps);
- gps->prev = gps->next = NULL;
- /* relink to destination frame */
- BLI_addtail(&gpf_dst->strokes, gps);
- /* reasign material */
- gps->mat_nr = idx;
- }
- }
- }
- }
-
- /* if not multiedit, exit loop*/
- if (!is_multiedit) {
- break;
- }
- }
- }
- CTX_DATA_END;
- }
- else if (mode == GP_SEPARATE_LAYER) {
- bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
- if (gpl) {
- /* try to set a new active layer in source datablock */
- if (gpl->prev) {
- BKE_gpencil_layer_setactive(gpd_src, gpl->prev);
- }
- else if (gpl->next) {
- BKE_gpencil_layer_setactive(gpd_src, gpl->next);
- }
- /* unlink from source datablock */
- BLI_remlink(&gpd_src->layers, gpl);
- gpl->prev = gpl->next = NULL;
- /* relink to destination datablock */
- BLI_addtail(&gpd_dst->layers, gpl);
-
- /* add duplicate materials */
- for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
- for (bGPDstroke *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;
- }
- ma = give_current_material(ob, gps->mat_nr + 1);
- gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
- }
- }
- }
- }
-
- DEG_id_tag_update(&gpd_src->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- DEG_id_tag_update(&gpd_dst->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
-
- DEG_relations_tag_update(bmain);
- WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ Base *base_new;
+ Main *bmain = CTX_data_main(C);
+ Scene *scene = CTX_data_scene(C);
+ ViewLayer *view_layer = CTX_data_view_layer(C);
+ Base *base_old = CTX_data_active_base(C);
+ bGPdata *gpd_src = ED_gpencil_data_get_active(C);
+ Object *ob = CTX_data_active_object(C);
+
+ Object *ob_dst = NULL;
+ bGPdata *gpd_dst = NULL;
+ bGPDlayer *gpl_dst = NULL;
+ bGPDframe *gpf_dst = NULL;
+ bGPDspoint *pt;
+ Material *ma = NULL;
+ int i, idx;
+
+ eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd_src)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if ((mode == GP_SEPARATE_LAYER) && (BLI_listbase_count(&gpd_src->layers) == 1)) {
+ BKE_report(op->reports, RPT_ERROR, "Cannot separate an object with one layer only");
+ return OPERATOR_CANCELLED;
+ }
+
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
+
+ /* create a new object */
+ base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_old, 0);
+ ob_dst = base_new->object;
+ ob_dst->mode = OB_MODE_OBJECT;
+ /* create new grease pencil datablock */
+ gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2);
+ ob_dst->data = (bGPdata *)gpd_dst;
+
+ /* loop old datablock and separate parts */
+ if ((mode == GP_SEPARATE_POINT) || (mode == GP_SEPARATE_STROKE)) {
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ gpl_dst = NULL;
+ bGPDframe *init_gpf = gpl->actframe;
+ if (is_multiedit) {
+ init_gpf = gpl->frames.first;
+ }
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ bGPDstroke *gps, *gpsn;
+
+ if (gpf == NULL) {
+ continue;
+ }
+
+ gpf_dst = NULL;
+
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = 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_color_use(ob, gpl, gps) == false) {
+ continue;
+ }
+ /* separate selected strokes */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* add layer if not created before */
+ if (gpl_dst == NULL) {
+ gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false);
+ }
+
+ /* add frame if not created before */
+ if (gpf_dst == NULL) {
+ gpf_dst = BKE_gpencil_layer_getframe(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
+ }
+
+ /* add duplicate materials */
+ ma = give_current_material(
+ ob, gps->mat_nr + 1); /* XXX same material can be in multiple slots */
+ idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
+
+ /* selected points mode */
+ if (mode == GP_SEPARATE_POINT) {
+ /* make copy of source stroke */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps);
+
+ /* reasign material */
+ gps_dst->mat_nr = idx;
+
+ /* link to destination frame */
+ BLI_addtail(&gpf_dst->strokes, gps_dst);
+
+ /* Invert selection status of all points in destination stroke */
+ for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
+ pt->flag ^= GP_SPOINT_SELECT;
+ }
+
+ /* delete selected points from destination stroke */
+ gp_stroke_delete_tagged_points(gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, 0);
+
+ /* delete selected points from origin stroke */
+ gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
+ }
+ /* selected strokes mode */
+ else if (mode == GP_SEPARATE_STROKE) {
+ /* deselect old stroke */
+ gps->flag &= ~GP_STROKE_SELECT;
+ /* unlink from source frame */
+ BLI_remlink(&gpf->strokes, gps);
+ gps->prev = gps->next = NULL;
+ /* relink to destination frame */
+ BLI_addtail(&gpf_dst->strokes, gps);
+ /* reasign material */
+ gps->mat_nr = idx;
+ }
+ }
+ }
+ }
+
+ /* if not multiedit, exit loop*/
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ CTX_DATA_END;
+ }
+ else if (mode == GP_SEPARATE_LAYER) {
+ bGPDlayer *gpl = CTX_data_active_gpencil_layer(C);
+ if (gpl) {
+ /* try to set a new active layer in source datablock */
+ if (gpl->prev) {
+ BKE_gpencil_layer_setactive(gpd_src, gpl->prev);
+ }
+ else if (gpl->next) {
+ BKE_gpencil_layer_setactive(gpd_src, gpl->next);
+ }
+ /* unlink from source datablock */
+ BLI_remlink(&gpd_src->layers, gpl);
+ gpl->prev = gpl->next = NULL;
+ /* relink to destination datablock */
+ BLI_addtail(&gpd_dst->layers, gpl);
+
+ /* add duplicate materials */
+ for (bGPDframe *gpf = gpl->frames.first; gpf; gpf = gpf->next) {
+ for (bGPDstroke *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;
+ }
+ ma = give_current_material(ob, gps->mat_nr + 1);
+ gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
+ }
+ }
+ }
+ }
+
+ DEG_id_tag_update(&gpd_src->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ DEG_id_tag_update(&gpd_dst->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+ DEG_relations_tag_update(bmain);
+ WM_event_add_notifier(C, NC_OBJECT | ND_DRAW, NULL);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
{
- static const EnumPropertyItem separate_type[] = {
- {GP_SEPARATE_POINT, "POINT", 0, "Selected Points", "Separate the selected points"},
- {GP_SEPARATE_STROKE, "STROKE", 0, "Selected Strokes", "Separate the selected strokes"},
- {GP_SEPARATE_LAYER, "LAYER", 0, "Active Layer", "Separate the strokes of the current layer"},
- {0, NULL, 0, NULL, NULL},
- };
-
- /* identifiers */
- ot->name = "Separate Strokes";
- ot->idname = "GPENCIL_OT_stroke_separate";
- ot->description = "Separate the selected strokes or layer in a new grease pencil object";
-
- /* callbacks */
- ot->invoke = WM_menu_invoke;
- ot->exec = gp_stroke_separate_exec;
- ot->poll = gp_strokes_edit3d_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", "");
+ static const EnumPropertyItem separate_type[] = {
+ {GP_SEPARATE_POINT, "POINT", 0, "Selected Points", "Separate the selected points"},
+ {GP_SEPARATE_STROKE, "STROKE", 0, "Selected Strokes", "Separate the selected strokes"},
+ {GP_SEPARATE_LAYER, "LAYER", 0, "Active Layer", "Separate the strokes of the current layer"},
+ {0, NULL, 0, NULL, NULL},
+ };
+
+ /* identifiers */
+ ot->name = "Separate Strokes";
+ ot->idname = "GPENCIL_OT_stroke_separate";
+ ot->description = "Separate the selected strokes or layer in a new grease pencil object";
+
+ /* callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = gp_stroke_separate_exec;
+ ot->poll = gp_strokes_edit3d_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", "");
}
/* ***************** Split Strokes ********************** */
static int gp_stroke_split_exec(bContext *C, wmOperator *UNUSED(op))
{
- Object *ob = CTX_data_active_object(C);
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- bGPDspoint *pt;
- int i;
-
- /* sanity checks */
- if (ELEM(NULL, gpd)) {
- return OPERATOR_CANCELLED;
- }
- const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
-
- /* loop strokes and split parts */
- CTX_DATA_BEGIN(C, bGPDlayer *, gpl, editable_gpencil_layers)
- {
- bGPDframe *init_gpf = gpl->actframe;
- if (is_multiedit) {
- init_gpf = gpl->frames.first;
- }
-
- for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
- if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
- bGPDstroke *gps, *gpsn;
-
- if (gpf == NULL) {
- continue;
- }
-
- for (gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = 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_color_use(ob, gpl, gps) == false) {
- continue;
- }
- /* split selected strokes */
- if (gps->flag & GP_STROKE_SELECT) {
- /* make copy of source stroke */
- bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps);
-
- /* link to same frame */
- BLI_addtail(&gpf->strokes, gps_dst);
-
- /* invert selection status of all points in destination stroke */
- for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
- pt->flag ^= GP_SPOINT_SELECT;
- }
-
- /* delete selected points from destination stroke */
- gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
-
- /* delete selected points from origin stroke */
- gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
- }
- }
- /* select again tagged points */
- for (gps = gpf->strokes.first; gps; gps = gps->next) {
- bGPDspoint *ptn = gps->points;
- for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) {
- if (ptn->flag & GP_SPOINT_TAG) {
- ptn->flag |= GP_SPOINT_SELECT;
- ptn->flag &= ~GP_SPOINT_TAG;
- }
- }
- }
- }
-
- /* if not multiedit, exit loop*/
- if (!is_multiedit) {
- break;
- }
- }
- }
- CTX_DATA_END;
-
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
-
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
-
- return OPERATOR_FINISHED;
+ Object *ob = CTX_data_active_object(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPDspoint *pt;
+ int i;
+
+ /* sanity checks */
+ if (ELEM(NULL, gpd)) {
+ return OPERATOR_CANCELLED;
+ }
+ const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
+
+ /* loop strokes and split parts */
+ CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
+ bGPDframe *init_gpf = gpl->actframe;
+ if (is_multiedit) {
+ init_gpf = gpl->frames.first;
+ }
+
+ for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
+ if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
+ bGPDstroke *gps, *gpsn;
+
+ if (gpf == NULL) {
+ continue;
+ }
+
+ for (gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = 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_color_use(ob, gpl, gps) == false) {
+ continue;
+ }
+ /* split selected strokes */
+ if (gps->flag & GP_STROKE_SELECT) {
+ /* make copy of source stroke */
+ bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps);
+
+ /* link to same frame */
+ BLI_addtail(&gpf->strokes, gps_dst);
+
+ /* invert selection status of all points in destination stroke */
+ for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
+ pt->flag ^= GP_SPOINT_SELECT;
+ }
+
+ /* delete selected points from destination stroke */
+ gp_stroke_delete_tagged_points(gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, 0);
+
+ /* delete selected points from origin stroke */
+ gp_stroke_delete_tagged_points(gpf, gps, gpsn, GP_SPOINT_SELECT, false, 0);
+ }
+ }
+ /* select again tagged points */
+ for (gps = gpf->strokes.first; gps; gps = gps->next) {
+ bGPDspoint *ptn = gps->points;
+ for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) {
+ if (ptn->flag & GP_SPOINT_TAG) {
+ ptn->flag |= GP_SPOINT_SELECT;
+ ptn->flag &= ~GP_SPOINT_TAG;
+ }
+ }
+ }
+ }
+
+ /* if not multiedit, exit loop*/
+ if (!is_multiedit) {
+ break;
+ }
+ }
+ }
+ CTX_DATA_END;
+
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_split(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Split Strokes";
- ot->idname = "GPENCIL_OT_stroke_split";
- ot->description = "Split selected points as new stroke on same frame";
+ /* identifiers */
+ ot->name = "Split Strokes";
+ ot->idname = "GPENCIL_OT_stroke_split";
+ ot->description = "Split selected points as new stroke on same frame";
- /* callbacks */
- ot->exec = gp_stroke_split_exec;
- ot->poll = gp_strokes_edit3d_poll;
+ /* callbacks */
+ ot->exec = gp_stroke_split_exec;
+ ot->poll = gp_strokes_edit3d_poll;
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int gp_stroke_smooth_exec(bContext *C, wmOperator *op)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
- /* sanity checks */
- if (ELEM(NULL, gpd))
- return OPERATOR_CANCELLED;
+ /* sanity checks */
+ if (ELEM(NULL, gpd))
+ return OPERATOR_CANCELLED;
- gp_smooth_stroke(C, op);
+ gp_smooth_stroke(C, op);
- /* notifiers */
- DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
- WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
+ /* notifiers */
+ DEG_id_tag_update(&gpd->id, ID_RECALC_TRANSFORM | ID_RECALC_GEOMETRY);
+ WM_event_add_notifier(C, NC_GPENCIL | ND_DATA | NA_EDITED, NULL);
- return OPERATOR_FINISHED;
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
{
- PropertyRNA *prop;
-
- /* identifiers */
- ot->name = "Smooth Stroke";
- ot->idname = "GPENCIL_OT_stroke_smooth";
- ot->description = "Smooth selected strokes";
-
- /* api callbacks */
- ot->exec = gp_stroke_smooth_exec;
- ot->poll = gp_active_layer_poll;
-
- /* flags */
- ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
-
- /* properties */
- prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5);
- RNA_def_property_flag(prop, PROP_SKIP_SAVE);
-
- RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f);
- RNA_def_boolean(
- ot->srna, "only_selected", true, "Selected Points",
- "Smooth only selected points in the stroke");
- RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
- RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", "");
- RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
- RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
+ PropertyRNA *prop;
+
+ /* identifiers */
+ ot->name = "Smooth Stroke";
+ ot->idname = "GPENCIL_OT_stroke_smooth";
+ ot->description = "Smooth selected strokes";
+
+ /* api callbacks */
+ ot->exec = gp_stroke_smooth_exec;
+ ot->poll = gp_active_layer_poll;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5);
+ RNA_def_property_flag(prop, PROP_SKIP_SAVE);
+
+ RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f);
+ RNA_def_boolean(ot->srna,
+ "only_selected",
+ true,
+ "Selected Points",
+ "Smooth only selected points in the stroke");
+ RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
+ RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", "");
+ RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
+ RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
}
/* smart stroke cutter for trimming stroke ends */
struct GP_SelectLassoUserData {
- rcti rect;
- const int(*mcords)[2];
- int mcords_len;
+ rcti rect;
+ const int (*mcords)[2];
+ int mcords_len;
};
-static bool gpencil_test_lasso(
- bGPDstroke *gps, bGPDspoint *pt,
- const GP_SpaceConversion *gsc, const float diff_mat[4][4],
- void *user_data)
+static bool gpencil_test_lasso(bGPDstroke *gps,
+ bGPDspoint *pt,
+ const GP_SpaceConversion *gsc,
+ const float diff_mat[4][4],
+ void *user_data)
{
- const struct GP_SelectLassoUserData *data = user_data;
- bGPDspoint pt2;
- int x0, y0;
- gp_point_to_parent_space(pt, diff_mat, &pt2);
- gp_point_to_xy(gsc, gps, &pt2, &x0, &y0);
- /* test if in lasso */
- return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) &&
- BLI_rcti_isect_pt(&data->rect, x0, y0) &&
- BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX));
+ const struct GP_SelectLassoUserData *data = user_data;
+ bGPDspoint pt2;
+ int x0, y0;
+ gp_point_to_parent_space(pt, diff_mat, &pt2);
+ gp_point_to_xy(gsc, gps, &pt2, &x0, &y0);
+ /* test if in lasso */
+ return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) &&
+ BLI_lasso_is_point_inside(data->mcords, data->mcords_len, x0, y0, INT_MAX));
}
-typedef bool(*GPencilTestFn)(
- bGPDstroke *gps, bGPDspoint *pt,
- const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data);
+typedef bool (*GPencilTestFn)(bGPDstroke *gps,
+ bGPDspoint *pt,
+ const GP_SpaceConversion *gsc,
+ const float diff_mat[4][4],
+ void *user_data);
static void gpencil_cutter_dissolve(bGPDlayer *hit_layer, bGPDstroke *hit_stroke)
{
- bGPDspoint *pt = NULL;
- bGPDspoint *pt1 = NULL;
- int i;
-
- bGPDstroke *gpsn = hit_stroke->next;
-
- int totselect = 0;
- for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- totselect++;
- }
- }
-
- /* if all points selected delete or only 2 points and 1 selected */
- if (((totselect == 1) && (hit_stroke->totpoints == 2)) ||
- (hit_stroke->totpoints == totselect))
- {
- BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
- BKE_gpencil_free_stroke(hit_stroke);
- hit_stroke = NULL;
- }
-
- /* if very small distance delete */
- if ((hit_stroke) && (hit_stroke->totpoints == 2)) {
- pt = &hit_stroke->points[0];
- pt1 = &hit_stroke->points[1];
- if (len_v3v3(&pt->x, &pt1->x) < 0.001f) {
- BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
- BKE_gpencil_free_stroke(hit_stroke);
- hit_stroke = NULL;
- }
- }
-
- if (hit_stroke) {
- /* tag and dissolve (untag new points) */
- for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
- if (pt->flag & GP_SPOINT_SELECT) {
- pt->flag &= ~GP_SPOINT_SELECT;
- pt->flag |= GP_SPOINT_TAG;
- }
- else if (pt->flag & GP_SPOINT_TAG) {
- pt->flag &= ~GP_SPOINT_TAG;
- }
- }
- gp_stroke_delete_tagged_points(
- hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1);
- }
+ bGPDspoint *pt = NULL;
+ bGPDspoint *pt1 = NULL;
+ int i;
+
+ bGPDstroke *gpsn = hit_stroke->next;
+
+ int totselect = 0;
+ for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ totselect++;
+ }
+ }
+
+ /* if all points selected delete or only 2 points and 1 selected */
+ if (((totselect == 1) && (hit_stroke->totpoints == 2)) || (hit_stroke->totpoints == totselect)) {
+ BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
+ BKE_gpencil_free_stroke(hit_stroke);
+ hit_stroke = NULL;
+ }
+
+ /* if very small distance delete */
+ if ((hit_stroke) && (hit_stroke->totpoints == 2)) {
+ pt = &hit_stroke->points[0];
+ pt1 = &hit_stroke->points[1];
+ if (len_v3v3(&pt->x, &pt1->x) < 0.001f) {
+ BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
+ BKE_gpencil_free_stroke(hit_stroke);
+ hit_stroke = NULL;
+ }
+ }
+
+ if (hit_stroke) {
+ /* tag and dissolve (untag new points) */
+ for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
+ if (pt->flag & GP_SPOINT_SELECT) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ pt->flag |= GP_SPOINT_TAG;
+ }
+ else if (pt->flag & GP_SPOINT_TAG) {
+ pt->flag &= ~GP_SPOINT_TAG;
+ }
+ }
+ gp_stroke_delete_tagged_points(hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, 1);
+ }
}
-static int gpencil_cutter_lasso_select(
- bContext *C, wmOperator *op,
- GPencilTestFn is_inside_fn, void *user_data)
+static int gpencil_cutter_lasso_select(bContext *C,
+ wmOperator *op,
+ GPencilTestFn is_inside_fn,
+ void *user_data)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
- ScrArea *sa = CTX_wm_area(C);
- ToolSettings *ts = CTX_data_tool_settings(C);
- const float scale = ts->gp_sculpt.isect_threshold;
-
- bGPDspoint *pt;
- int i;
- GP_SpaceConversion gsc = { NULL };
-
- bool changed = false;
-
- /* sanity checks */
- if (sa == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No active area");
- return OPERATOR_CANCELLED;
- }
-
- /* init space conversion stuff */
- gp_point_conversion_init(C, &gsc);
-
- /* deselect all strokes first */
- CTX_DATA_BEGIN(C, bGPDstroke *, gps, editable_gpencil_strokes)
- {
- for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
- pt->flag &= ~GP_SPOINT_SELECT;
- }
-
- gps->flag &= ~GP_STROKE_SELECT;
- }
- CTX_DATA_END;
-
- /* select points */
- GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
- {
- int tot_inside = 0;
- const int oldtot = gps->totpoints;
- for (i = 0; i < gps->totpoints; i++) {
- pt = &gps->points[i];
- if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) {
- continue;
- }
- /* convert point coords to screenspace */
- const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data);
- if (is_inside) {
- tot_inside++;
- changed = true;
- pt->flag |= GP_SPOINT_SELECT;
- gps->flag |= GP_STROKE_SELECT;
- float r_hita[3], r_hitb[3];
- if (gps->totpoints > 1) {
- ED_gpencil_select_stroke_segment(
- gpl, gps, pt, true, true, scale, r_hita, r_hitb);
- }
- /* avoid infinite loops */
- if (gps->totpoints > oldtot) {
- break;
- }
- }
- }
- /* if mark all points inside lasso set to remove all stroke */
- if ((tot_inside == oldtot) ||
- ((tot_inside == 1) && (oldtot == 2)))
- {
- for (i = 0; i < gps->totpoints; i++) {
- pt = &gps->points[i];
- pt->flag |= GP_SPOINT_SELECT;
- }
- }
- }
- GP_EDITABLE_STROKES_END(gpstroke_iter);
-
- /* dissolve selected points */
- bGPDstroke *gpsn;
- for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
- bGPDframe *gpf = gpl->actframe;
- if (gpf == NULL) {
- continue;
- }
- for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gpsn) {
- gpsn = gps->next;
- if (gps->flag & GP_STROKE_SELECT) {
- gpencil_cutter_dissolve(gpl, gps);
- }
- }
- }
-
- /* updates */
- if (changed) {
- DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
- WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
- WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
- }
-
- return OPERATOR_FINISHED;
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
+ ScrArea *sa = CTX_wm_area(C);
+ ToolSettings *ts = CTX_data_tool_settings(C);
+ const float scale = ts->gp_sculpt.isect_threshold;
+
+ bGPDspoint *pt;
+ int i;
+ GP_SpaceConversion gsc = {NULL};
+
+ bool changed = false;
+
+ /* sanity checks */
+ if (sa == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No active area");
+ return OPERATOR_CANCELLED;
+ }
+
+ /* init space conversion stuff */
+ gp_point_conversion_init(C, &gsc);
+
+ /* deselect all strokes first */
+ CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
+ for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
+ pt->flag &= ~GP_SPOINT_SELECT;
+ }
+
+ gps->flag &= ~GP_STROKE_SELECT;
+ }
+ CTX_DATA_END;
+
+ /* select points */
+ GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
+ int tot_inside = 0;
+ const int oldtot = gps->totpoints;
+ for (i = 0; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) {
+ continue;
+ }
+ /* convert point coords to screenspace */
+ const bool is_inside = is_inside_fn(gps, pt, &gsc, gpstroke_iter.diff_mat, user_data);
+ if (is_inside) {
+ tot_inside++;
+ changed = true;
+ pt->flag |= GP_SPOINT_SELECT;
+ gps->flag |= GP_STROKE_SELECT;
+ float r_hita[3], r_hitb[3];
+ if (gps->totpoints > 1) {
+ ED_gpencil_select_stroke_segment(gpl, gps, pt, true, true, scale, r_hita, r_hitb);
+ }
+ /* avoid infinite loops */
+ if (gps->totpoints > oldtot) {
+ break;
+ }
+ }
+ }
+ /* if mark all points inside lasso set to remove all stroke */
+ if ((tot_inside == oldtot) || ((tot_inside == 1) && (oldtot == 2))) {
+ for (i = 0; i < gps->totpoints; i++) {
+ pt = &gps->points[i];
+ pt->flag |= GP_SPOINT_SELECT;
+ }
+ }
+ }
+ GP_EDITABLE_STROKES_END(gpstroke_iter);
+
+ /* dissolve selected points */
+ bGPDstroke *gpsn;
+ for (bGPDlayer *gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+ bGPDframe *gpf = gpl->actframe;
+ if (gpf == NULL) {
+ continue;
+ }
+ for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gpsn) {
+ gpsn = gps->next;
+ if (gps->flag & GP_STROKE_SELECT) {
+ gpencil_cutter_dissolve(gpl, gps);
+ }
+ }
+ }
+
+ /* updates */
+ if (changed) {
+ DEG_id_tag_update(&gpd->id, ID_RECALC_GEOMETRY | ID_RECALC_COPY_ON_WRITE);
+ WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, NULL);
+ }
+
+ return OPERATOR_FINISHED;
}
static bool gpencil_cutter_poll(bContext *C)
{
- bGPdata *gpd = ED_gpencil_data_get_active(C);
+ bGPdata *gpd = ED_gpencil_data_get_active(C);
- if (GPENCIL_PAINT_MODE(gpd)) {
- if (gpd->layers.first)
- return true;
- }
+ if (GPENCIL_PAINT_MODE(gpd)) {
+ if (gpd->layers.first)
+ return true;
+ }
- return false;
+ return false;
}
static int gpencil_cutter_exec(bContext *C, wmOperator *op)
{
- ScrArea *sa = CTX_wm_area(C);
- /* sanity checks */
- if (sa == NULL) {
- BKE_report(op->reports, RPT_ERROR, "No active area");
- return OPERATOR_CANCELLED;
- }
+ ScrArea *sa = CTX_wm_area(C);
+ /* sanity checks */
+ if (sa == NULL) {
+ BKE_report(op->reports, RPT_ERROR, "No active area");
+ return OPERATOR_CANCELLED;
+ }
- struct GP_SelectLassoUserData data = { 0 };
- data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len);
+ struct GP_SelectLassoUserData data = {0};
+ data.mcords = WM_gesture_lasso_path_to_array(C, op, &data.mcords_len);
- /* Sanity check. */
- if (data.mcords == NULL) {
- return OPERATOR_PASS_THROUGH;
- }
+ /* Sanity check. */
+ if (data.mcords == NULL) {
+ return OPERATOR_PASS_THROUGH;
+ }
- /* Compute boundbox of lasso (for faster testing later). */
- BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len);
+ /* Compute boundbox of lasso (for faster testing later). */
+ BLI_lasso_boundbox(&data.rect, data.mcords, data.mcords_len);
- gpencil_cutter_lasso_select(C, op, gpencil_test_lasso, &data);
+ gpencil_cutter_lasso_select(C, op, gpencil_test_lasso, &data);
- MEM_freeN((void *)data.mcords);
+ MEM_freeN((void *)data.mcords);
- return OPERATOR_FINISHED;
+ return OPERATOR_FINISHED;
}
void GPENCIL_OT_stroke_cutter(wmOperatorType *ot)
{
- /* identifiers */
- ot->name = "Stroke Cutter";
- ot->description = "Select section and cut";
- ot->idname = "GPENCIL_OT_stroke_cutter";
-
- /* callbacks */
- ot->invoke = WM_gesture_lasso_invoke;
- ot->modal = WM_gesture_lasso_modal;
- ot->exec = gpencil_cutter_exec;
- ot->poll = gpencil_cutter_poll;
- ot->cancel = WM_gesture_lasso_cancel;
-
- /* flag */
- ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
-
- /* properties */
- WM_operator_properties_gesture_lasso(ot);
+ /* identifiers */
+ ot->name = "Stroke Cutter";
+ ot->description = "Select section and cut";
+ ot->idname = "GPENCIL_OT_stroke_cutter";
+
+ /* callbacks */
+ ot->invoke = WM_gesture_lasso_invoke;
+ ot->modal = WM_gesture_lasso_modal;
+ ot->exec = gpencil_cutter_exec;
+ ot->poll = gpencil_cutter_poll;
+ ot->cancel = WM_gesture_lasso_cancel;
+
+ /* flag */
+ ot->flag = OPTYPE_UNDO | OPTYPE_USE_EVAL_DATA;
+
+ /* properties */
+ WM_operator_properties_gesture_lasso(ot);
}