Blender  V2.93
gpencil_edit.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2008, Blender Foundation
17  * This is a new part of Blender
18  * Operators for editing Grease Pencil strokes
19  */
20 
25 #include <math.h>
26 #include <stddef.h>
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <string.h>
30 
31 #include "MEM_guardedalloc.h"
32 
33 #include "BLI_blenlib.h"
34 #include "BLI_ghash.h"
35 #include "BLI_lasso_2d.h"
36 #include "BLI_math.h"
37 #include "BLI_string.h"
38 #include "BLI_utildefines.h"
39 
40 #include "BLT_translation.h"
41 
43 #include "DNA_gpencil_types.h"
44 #include "DNA_material_types.h"
45 #include "DNA_meshdata_types.h"
46 #include "DNA_object_types.h"
47 #include "DNA_scene_types.h"
48 #include "DNA_screen_types.h"
49 #include "DNA_space_types.h"
50 #include "DNA_view3d_types.h"
51 
52 #include "BKE_brush.h"
53 #include "BKE_context.h"
54 #include "BKE_global.h"
55 #include "BKE_gpencil.h"
56 #include "BKE_gpencil_curve.h"
57 #include "BKE_gpencil_geom.h"
58 #include "BKE_layer.h"
59 #include "BKE_lib_id.h"
60 #include "BKE_library.h"
61 #include "BKE_main.h"
62 #include "BKE_material.h"
63 #include "BKE_object.h"
64 #include "BKE_paint.h"
65 #include "BKE_report.h"
66 #include "BKE_scene.h"
67 #include "BKE_workspace.h"
68 
69 #include "UI_interface.h"
70 #include "UI_resources.h"
71 
72 #include "WM_api.h"
73 #include "WM_message.h"
74 #include "WM_toolsystem.h"
75 #include "WM_types.h"
76 
77 #include "RNA_access.h"
78 #include "RNA_define.h"
79 #include "RNA_enum_types.h"
80 
81 #include "UI_view2d.h"
82 
83 #include "ED_armature.h"
84 #include "ED_gpencil.h"
85 #include "ED_object.h"
86 #include "ED_outliner.h"
87 #include "ED_screen.h"
88 #include "ED_select_utils.h"
89 #include "ED_space_api.h"
91 #include "ED_view3d.h"
92 
93 #include "DEG_depsgraph.h"
94 #include "DEG_depsgraph_build.h"
95 #include "DEG_depsgraph_query.h"
96 
97 #include "gpencil_intern.h"
98 
99 /* -------------------------------------------------------------------- */
103 /* poll callback for all stroke editing operators */
105 {
106  /* edit only supported with grease pencil objects */
108  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
109  return false;
110  }
111 
112  /* NOTE: this is a bit slower, but is the most accurate... */
113  return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
114 }
115 
116 /* poll callback to verify edit mode in 3D view only */
118 {
119  /* edit only supported with grease pencil objects */
121  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
122  return false;
123  }
124 
125  /* 2 Requirements:
126  * - 1) Editable GP data
127  * - 2) 3D View only
128  */
130 }
131 
133 {
134  /* edit only supported with grease pencil objects */
136  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
137  return false;
138  }
139 
140  /* if using gpencil object, use this gpd */
141  if (ob->type == OB_GPENCIL) {
142  return ob->data != NULL;
143  }
144 
145  return ED_gpencil_data_get_active(C) != NULL;
146 }
147 
149 {
151  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
152  return false;
153  }
154  bGPdata *gpd = (bGPdata *)ob->data;
156 
157  return (gpl != NULL && !GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd));
158 }
159 
162 /* -------------------------------------------------------------------- */
167 {
168  const int back = RNA_boolean_get(op->ptr, "back");
169 
170  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
172  bool is_object = false;
173  short mode;
174  /* if using a gpencil object, use this datablock */
176  if ((ob) && (ob->type == OB_GPENCIL)) {
177  gpd = ob->data;
178  is_object = true;
179  }
180 
181  if (gpd == NULL) {
182  BKE_report(op->reports, RPT_ERROR, "No active GP data");
183  return OPERATOR_CANCELLED;
184  }
185 
186  /* Just toggle editmode flag... */
188  /* recalculate parent matrix */
189  if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
192  }
193  /* set mode */
194  if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
195  mode = OB_MODE_EDIT_GPENCIL;
196  }
197  else {
198  mode = OB_MODE_OBJECT;
199  }
200 
201  if (is_object) {
202  /* try to back previous mode */
203  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) {
204  mode = ob->restore_mode;
205  }
206  ob->restore_mode = ob->mode;
207  ob->mode = mode;
208  }
209 
210  /* Recalculate editcurves for strokes where the geometry/vertex colors have changed */
212  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
213  {
214  if (gpc->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
216  /* Update the selection from the stroke to the curve. */
217  BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
218 
219  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
221  }
222  }
223  GP_EDITABLE_CURVES_END(gps_iter);
224  }
225 
226  /* setup other modes */
227  ED_gpencil_setup_modes(C, gpd, mode);
228  /* set cache as dirty */
230 
234 
235  if (is_object) {
236  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
237  }
238  if (G.background == false) {
240  }
241 
242  return OPERATOR_FINISHED;
243 }
244 
246 {
247  PropertyRNA *prop;
248 
249  /* identifiers */
250  ot->name = "Strokes Edit Mode Toggle";
251  ot->idname = "GPENCIL_OT_editmode_toggle";
252  ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
253 
254  /* callbacks */
257 
258  /* flags */
260 
261  /* properties */
262  prop = RNA_def_boolean(
263  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
265 }
266 
269 /* -------------------------------------------------------------------- */
273 /* set select mode */
275 {
276  /* edit only supported with grease pencil objects */
278  if ((ob == NULL) || (ob->type != OB_GPENCIL) || (ob->mode != OB_MODE_EDIT_GPENCIL)) {
279  return false;
280  }
281 
283 }
284 
286 {
290  const int mode = RNA_int_get(op->ptr, "mode");
291  bool changed = false;
292 
293  if (ts->gpencil_selectmode_edit == mode) {
294  return OPERATOR_FINISHED;
295  }
296 
297  /* Just set mode */
298  ts->gpencil_selectmode_edit = mode;
299 
300  /* If the mode is Stroke, extend selection. */
301  if ((ob) && (ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE)) {
302  bGPdata *gpd = (bGPdata *)ob->data;
303  /* Extend selection to all points in all selected strokes. */
304  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
305  if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
306  changed = true;
307  bGPDspoint *pt;
308  for (int i = 0; i < gps->totpoints; i++) {
309  pt = &gps->points[i];
310  pt->flag |= GP_SPOINT_SELECT;
311  }
312  }
313  }
314  CTX_DATA_END;
315  if (changed) {
317  }
318  }
319 
323 
324  return OPERATOR_FINISHED;
325 }
326 
328 {
329  PropertyRNA *prop;
330 
331  /* identifiers */
332  ot->name = "Select Mode Toggle";
333  ot->idname = "GPENCIL_OT_selectmode_toggle";
334  ot->description = "Set selection mode for Grease Pencil strokes";
335 
336  /* callbacks */
339 
340  /* flags */
342 
343  /* properties */
344  prop = RNA_def_int(ot->srna, "mode", 0, 0, 2, "Select Mode", "Select mode", 0, 2);
346 }
347 
350 /* -------------------------------------------------------------------- */
355 {
356  /* if using gpencil object, use this gpd */
358  if ((ob) && (ob->type == OB_GPENCIL)) {
359  return ob->data != NULL;
360  }
361  return ED_gpencil_data_get_active(C) != NULL;
362 }
363 
365 {
366  const bool back = RNA_boolean_get(op->ptr, "back");
367 
368  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
369  Main *bmain = CTX_data_main(C);
372 
373  bool is_object = false;
374  short mode;
375  /* if using a gpencil object, use this datablock */
377  if ((ob) && (ob->type == OB_GPENCIL)) {
378  gpd = ob->data;
379  is_object = true;
380  }
381 
382  if (gpd == NULL) {
383  return OPERATOR_CANCELLED;
384  }
385 
386  /* Just toggle paintmode flag... */
388  /* set mode */
389  if (gpd->flag & GP_DATA_STROKE_PAINTMODE) {
390  mode = OB_MODE_PAINT_GPENCIL;
391  }
392  else {
393  mode = OB_MODE_OBJECT;
394  }
395 
396  if (is_object) {
397  /* try to back previous mode */
398  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) {
399  mode = ob->restore_mode;
400  }
401  ob->restore_mode = ob->mode;
402  ob->mode = mode;
403  }
404 
405  if (mode == OB_MODE_PAINT_GPENCIL) {
406  /* Be sure we have brushes and Paint settings.
407  * Need Draw and Vertex (used for Tint). */
408  BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
409  BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint);
410 
411  BKE_brush_gpencil_paint_presets(bmain, ts, false);
412 
413  /* Ensure Palette by default. */
415 
416  Paint *paint = &ts->gp_paint->paint;
417  /* if not exist, create a new one */
418  if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
419  BKE_brush_gpencil_paint_presets(bmain, ts, true);
420  }
422  }
423 
424  /* setup other modes */
425  ED_gpencil_setup_modes(C, gpd, mode);
426  /* set cache as dirty */
428 
431 
432  if (is_object) {
433  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
434  }
435  if (G.background == false) {
437  }
438 
439  return OPERATOR_FINISHED;
440 }
441 
443 {
444  PropertyRNA *prop;
445 
446  /* identifiers */
447  ot->name = "Strokes Paint Mode Toggle";
448  ot->idname = "GPENCIL_OT_paintmode_toggle";
449  ot->description = "Enter/Exit paint mode for Grease Pencil strokes";
450 
451  /* callbacks */
454 
455  /* flags */
457 
458  /* properties */
459  prop = RNA_def_boolean(
460  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
462 }
463 
466 /* -------------------------------------------------------------------- */
471 {
472  /* if using gpencil object, use this gpd */
474  if ((ob) && (ob->type == OB_GPENCIL)) {
475  return ob->data != NULL;
476  }
477  return ED_gpencil_data_get_active(C) != NULL;
478 }
479 
481 {
482  Main *bmain = CTX_data_main(C);
484 
485  const bool back = RNA_boolean_get(op->ptr, "back");
486 
487  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
489  bool is_object = false;
490  short mode;
491  /* if using a gpencil object, use this datablock */
493  if ((ob) && (ob->type == OB_GPENCIL)) {
494  gpd = ob->data;
495  is_object = true;
496  }
497 
498  if (gpd == NULL) {
499  return OPERATOR_CANCELLED;
500  }
501 
502  /* Just toggle sculptmode flag... */
504  /* set mode */
505  if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) {
506  mode = OB_MODE_SCULPT_GPENCIL;
507  }
508  else {
509  mode = OB_MODE_OBJECT;
510  }
511 
512  if (is_object) {
513  /* try to back previous mode */
514  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) {
515  mode = ob->restore_mode;
516  }
517  ob->restore_mode = ob->mode;
518  ob->mode = mode;
519  }
520 
521  if (mode == OB_MODE_SCULPT_GPENCIL) {
522  /* Be sure we have brushes. */
523  BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint);
524 
525  const bool reset_mode = (ts->gp_sculptpaint->paint.brush == NULL);
526  BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode);
527 
529  }
530 
531  /* setup other modes */
532  ED_gpencil_setup_modes(C, gpd, mode);
533  /* set cache as dirty */
535 
538 
539  if (is_object) {
540  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
541  }
542  if (G.background == false) {
544  }
545 
546  return OPERATOR_FINISHED;
547 }
548 
551 /* -------------------------------------------------------------------- */
556 {
557  PropertyRNA *prop;
558 
559  /* identifiers */
560  ot->name = "Strokes Sculpt Mode Toggle";
561  ot->idname = "GPENCIL_OT_sculptmode_toggle";
562  ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes";
563 
564  /* callbacks */
567 
568  /* flags */
570 
571  /* properties */
572  prop = RNA_def_boolean(
573  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
575 }
576 
577 /* Stroke Weight Paint Mode Management */
578 
580 {
581  /* if using gpencil object, use this gpd */
583  if ((ob) && (ob->type == OB_GPENCIL)) {
584  return ob->data != NULL;
585  }
586  return ED_gpencil_data_get_active(C) != NULL;
587 }
588 
590 {
591  Main *bmain = CTX_data_main(C);
593 
594  const bool back = RNA_boolean_get(op->ptr, "back");
595 
596  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
598  bool is_object = false;
599  short mode;
600  /* if using a gpencil object, use this datablock */
602  if ((ob) && (ob->type == OB_GPENCIL)) {
603  gpd = ob->data;
604  is_object = true;
605  }
606  const int mode_flag = OB_MODE_WEIGHT_GPENCIL;
607  const bool is_mode_set = (ob->mode & mode_flag) != 0;
608 
609  if (gpd == NULL) {
610  return OPERATOR_CANCELLED;
611  }
612 
613  /* Just toggle weightmode flag... */
615  /* set mode */
616  if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) {
617  mode = OB_MODE_WEIGHT_GPENCIL;
618  }
619  else {
620  mode = OB_MODE_OBJECT;
621  }
622 
623  if (is_object) {
624  /* try to back previous mode */
625  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) {
626  mode = ob->restore_mode;
627  }
628  ob->restore_mode = ob->mode;
629  ob->mode = mode;
630 
631  /* Prepare armature posemode. */
632  ED_object_posemode_set_for_weight_paint(C, bmain, ob, is_mode_set);
633  }
634 
635  if (mode == OB_MODE_WEIGHT_GPENCIL) {
636  /* Be sure we have brushes. */
637  BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint);
638 
639  const bool reset_mode = (ts->gp_weightpaint->paint.brush == NULL);
640  BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode);
641 
643  }
644 
645  /* setup other modes */
646  ED_gpencil_setup_modes(C, gpd, mode);
647  /* set cache as dirty */
649 
652 
653  if (is_object) {
654  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
655  }
656  if (G.background == false) {
658  }
659 
660  return OPERATOR_FINISHED;
661 }
662 
664 {
665  PropertyRNA *prop;
666 
667  /* identifiers */
668  ot->name = "Strokes Weight Mode Toggle";
669  ot->idname = "GPENCIL_OT_weightmode_toggle";
670  ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes";
671 
672  /* callbacks */
675 
676  /* flags */
678 
679  /* properties */
680  prop = RNA_def_boolean(
681  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
683 }
684 
687 /* -------------------------------------------------------------------- */
692 {
693  /* if using gpencil object, use this gpd */
695  if ((ob) && (ob->type == OB_GPENCIL)) {
696  return ob->data != NULL;
697  }
698  return ED_gpencil_data_get_active(C) != NULL;
699 }
701 {
702  const bool back = RNA_boolean_get(op->ptr, "back");
703 
704  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
705  Main *bmain = CTX_data_main(C);
708 
709  bool is_object = false;
710  short mode;
711  /* if using a gpencil object, use this datablock */
713  if ((ob) && (ob->type == OB_GPENCIL)) {
714  gpd = ob->data;
715  is_object = true;
716  }
717 
718  if (gpd == NULL) {
719  return OPERATOR_CANCELLED;
720  }
721 
722  /* Just toggle paintmode flag... */
724  /* set mode */
725  if (gpd->flag & GP_DATA_STROKE_VERTEXMODE) {
726  mode = OB_MODE_VERTEX_GPENCIL;
727  }
728  else {
729  mode = OB_MODE_OBJECT;
730  }
731 
732  if (is_object) {
733  /* try to back previous mode */
734  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_VERTEXMODE) == 0) && (back == 1)) {
735  mode = ob->restore_mode;
736  }
737  ob->restore_mode = ob->mode;
738  ob->mode = mode;
739  }
740 
741  if (mode == OB_MODE_VERTEX_GPENCIL) {
742  /* Be sure we have brushes. */
743  BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint);
744 
745  const bool reset_mode = (ts->gp_vertexpaint->paint.brush == NULL);
746  BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode);
747 
749 
750  /* Ensure Palette by default. */
752  }
753 
754  /* setup other modes */
755  ED_gpencil_setup_modes(C, gpd, mode);
756  /* set cache as dirty */
758 
761 
762  if (is_object) {
763  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
764  }
765  if (G.background == false) {
767  }
768 
769  return OPERATOR_FINISHED;
770 }
771 
773 {
774  PropertyRNA *prop;
775 
776  /* identifiers */
777  ot->name = "Strokes Vertex Mode Toggle";
778  ot->idname = "GPENCIL_OT_vertexmode_toggle";
779  ot->description = "Enter/Exit vertex paint mode for Grease Pencil strokes";
780 
781  /* callbacks */
784 
785  /* flags */
787 
788  /* properties */
789  prop = RNA_def_boolean(
790  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
792 }
793 
796 /* -------------------------------------------------------------------- */
801 {
802  View3D *v3d = CTX_wm_view3d(C);
803  if (v3d == NULL) {
804  return OPERATOR_CANCELLED;
805  }
806 
807  /* Just toggle alpha... */
808  if (v3d->vertex_opacity > 0.0f) {
809  v3d->vertex_opacity = 0.0f;
810  }
811  else {
812  v3d->vertex_opacity = 1.0f;
813  }
814 
818 
819  return OPERATOR_FINISHED;
820 }
821 
823 {
824  /* identifiers */
825  ot->name = "Hide Selected";
826  ot->idname = "GPENCIL_OT_selection_opacity_toggle";
827  ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
828 
829  /* callbacks */
832 
833  /* flags */
835 }
836 
839 /* -------------------------------------------------------------------- */
843 /* Make copies of selected point segments in a selected stroke */
845  const bGPDstroke *gps,
846  ListBase *new_strokes,
847  const char *layername)
848 {
849  bGPDspoint *pt;
850  int i;
851 
852  int start_idx = -1;
853 
854  /* Step through the original stroke's points:
855  * - We accumulate selected points (from start_idx to current index)
856  * and then convert that to a new stroke
857  */
858  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
859  /* searching for start, are waiting for end? */
860  if (start_idx == -1) {
861  /* is this the first selected point for a new island? */
862  if (pt->flag & GP_SPOINT_SELECT) {
863  start_idx = i;
864  }
865  }
866  else {
867  size_t len = 0;
868 
869  /* is this the end of current island yet?
870  * 1) Point i-1 was the last one that was selected
871  * 2) Point i is the last in the array
872  */
873  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
874  len = i - start_idx;
875  }
876  else if (i == gps->totpoints - 1) {
877  len = i - start_idx + 1;
878  }
879 
880  /* make copies of the relevant data */
881  if (len) {
882  bGPDstroke *gpsd;
883 
884  /* make a stupid copy first of the entire stroke (to get the flags too) */
885  gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, true);
886 
887  /* saves original layer name */
888  BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
889 
890  /* now, make a new points array, and copy of the relevant parts */
891  gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
892  memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
893  gpsd->totpoints = len;
894 
895  if (gps->dvert != NULL) {
896  gpsd->dvert = MEM_mallocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
897  memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
898 
899  /* Copy weights */
900  int e = start_idx;
901  for (int j = 0; j < gpsd->totpoints; j++) {
902  MDeformVert *dvert_dst = &gps->dvert[e];
903  MDeformVert *dvert_src = &gps->dvert[j];
904  dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
905  e++;
906  }
907  }
908 
910 
911  /* add to temp buffer */
912  gpsd->next = gpsd->prev = NULL;
913 
914  BLI_addtail(new_strokes, gpsd);
915 
916  /* cleanup + reset for next */
917  start_idx = -1;
918  }
919  }
920  }
921 }
922 
924 {
926  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
927 
928  if (gpd == NULL) {
929  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
930  return OPERATOR_CANCELLED;
931  }
932 
934  BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
935  return OPERATOR_CANCELLED;
936  }
937 
938  bool changed = false;
939  if (is_curve_edit) {
940  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
941  }
942  else {
943  /* for each visible (and editable) layer's selected strokes,
944  * copy the strokes into a temporary buffer, then append
945  * once all done
946  */
947  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
948  ListBase new_strokes = {NULL, NULL};
949  bGPDframe *gpf = gpl->actframe;
950  bGPDstroke *gps;
951 
952  if (gpf == NULL) {
953  continue;
954  }
955 
956  /* make copies of selected strokes, and deselect these once we're done */
957  for (gps = gpf->strokes.first; gps; gps = gps->next) {
958  /* skip strokes that are invalid for current view */
959  if (ED_gpencil_stroke_can_use(C, gps) == false) {
960  continue;
961  }
962 
963  if (gps->flag & GP_STROKE_SELECT) {
964  if (gps->totpoints == 1) {
965  /* Special Case: If there's just a single point in this stroke... */
966  bGPDstroke *gpsd;
967 
968  /* make direct copies of the stroke and its points */
969  gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
970 
971  BLI_strncpy(
972  gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
973 
974  /* Initialize triangle information. */
976 
977  /* add to temp buffer */
978  gpsd->next = gpsd->prev = NULL;
979  BLI_addtail(&new_strokes, gpsd);
980  }
981  else {
982  /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
983  gpencil_duplicate_points(gpd, gps, &new_strokes, gpl->info);
984  }
985 
986  /* deselect original stroke, or else the originals get moved too
987  * (when using the copy + move macro)
988  */
989  bGPDspoint *pt;
990  int i;
991  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
992  pt->flag &= ~GP_SPOINT_SELECT;
993  }
994  gps->flag &= ~GP_STROKE_SELECT;
996 
997  changed = true;
998  }
999  }
1000 
1001  /* add all new strokes in temp buffer to the frame (preventing double-copies) */
1002  BLI_movelisttolist(&gpf->strokes, &new_strokes);
1003  BLI_assert(new_strokes.first == NULL);
1004  }
1005  CTX_DATA_END;
1006  }
1007 
1008  if (changed) {
1009  /* updates */
1012  }
1013 
1014  return OPERATOR_FINISHED;
1015 }
1016 
1018 {
1019  /* identifiers */
1020  ot->name = "Duplicate Strokes";
1021  ot->idname = "GPENCIL_OT_duplicate";
1022  ot->description = "Duplicate the selected Grease Pencil strokes";
1023 
1024  /* callbacks */
1027 
1028  /* flags */
1030 }
1031 
1034 /* -------------------------------------------------------------------- */
1038 /* helper to copy a point to temp area */
1040  bGPDspoint *temp_points,
1041  MDeformVert *temp_dverts,
1042  int from_idx,
1043  int to_idx,
1044  const bool copy)
1045 {
1046  bGPDspoint *pt = &temp_points[from_idx];
1047  bGPDspoint *pt_final = &gps->points[to_idx];
1048 
1049  copy_v3_v3(&pt_final->x, &pt->x);
1050  pt_final->pressure = pt->pressure;
1051  pt_final->strength = pt->strength;
1052  pt_final->time = pt->time;
1053  pt_final->flag = pt->flag;
1054  pt_final->uv_fac = pt->uv_fac;
1055  pt_final->uv_rot = pt->uv_rot;
1056  copy_v4_v4(pt_final->vert_color, pt->vert_color);
1057 
1058  if (gps->dvert != NULL) {
1059  MDeformVert *dvert = &temp_dverts[from_idx];
1060  MDeformVert *dvert_final = &gps->dvert[to_idx];
1061 
1062  dvert_final->totweight = dvert->totweight;
1063  /* if copy, duplicate memory, otherwise move only the pointer */
1064  if (copy) {
1065  dvert_final->dw = MEM_dupallocN(dvert->dw);
1066  }
1067  else {
1068  dvert_final->dw = dvert->dw;
1069  }
1070  }
1071 }
1072 
1074 {
1075  bGPDspoint *temp_points = NULL;
1076  MDeformVert *temp_dverts = NULL;
1077  bGPDspoint *pt = NULL;
1078  const bGPDspoint *pt_start = &gps->points[0];
1079  const bGPDspoint *pt_last = &gps->points[gps->totpoints - 1];
1080  const bool do_first = (pt_start->flag & GP_SPOINT_SELECT);
1081  const bool do_last = ((pt_last->flag & GP_SPOINT_SELECT) && (pt_start != pt_last));
1082  const bool do_stroke = (do_first || do_last);
1083 
1084  /* review points in the middle of stroke to create new strokes */
1085  for (int i = 0; i < gps->totpoints; i++) {
1086  /* skip first and last point */
1087  if (ELEM(i, 0, gps->totpoints - 1)) {
1088  continue;
1089  }
1090 
1091  pt = &gps->points[i];
1092  if (pt->flag == GP_SPOINT_SELECT) {
1093  /* duplicate original stroke data */
1094  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false, true);
1095  gps_new->prev = gps_new->next = NULL;
1096 
1097  /* add new points array */
1098  gps_new->totpoints = 1;
1099  gps_new->points = MEM_callocN(sizeof(bGPDspoint), __func__);
1100  gps_new->dvert = NULL;
1101 
1102  if (gps->dvert != NULL) {
1103  gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__);
1104  }
1105 
1106  BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
1107 
1108  /* copy selected point data to new stroke */
1109  gpencil_copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
1110 
1111  /* Calc geometry data. */
1113  BKE_gpencil_stroke_geometry_update(gpd, gps_new);
1114 
1115  /* Deselect original point. */
1116  pt->flag &= ~GP_SPOINT_SELECT;
1117  }
1118  }
1119 
1120  /* review first and last point to reuse same stroke */
1121  int i2 = 0;
1122  int totnewpoints, oldtotpoints;
1123  /* if first or last, reuse stroke and resize */
1124  if ((do_first) || (do_last)) {
1125  totnewpoints = gps->totpoints;
1126  if (do_first) {
1127  totnewpoints++;
1128  }
1129  if (do_last) {
1130  totnewpoints++;
1131  }
1132 
1133  /* duplicate points in a temp area */
1134  temp_points = MEM_dupallocN(gps->points);
1135  oldtotpoints = gps->totpoints;
1136  if (gps->dvert != NULL) {
1137  temp_dverts = MEM_dupallocN(gps->dvert);
1138  }
1139 
1140  /* if first point, need move all one position */
1141  if (do_first) {
1142  i2 = 1;
1143  }
1144 
1145  /* resize the points arrays */
1146  gps->totpoints = totnewpoints;
1147  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
1148  if (gps->dvert != NULL) {
1149  gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
1150  }
1151 
1152  /* move points to new position */
1153  for (int i = 0; i < oldtotpoints; i++) {
1154  gpencil_copy_move_point(gps, temp_points, temp_dverts, i, i2, false);
1155  i2++;
1156  }
1157 
1158  /* If first point, add new point at the beginning. */
1159  if (do_first) {
1160  gpencil_copy_move_point(gps, temp_points, temp_dverts, 0, 0, true);
1161  /* deselect old */
1162  pt = &gps->points[1];
1163  pt->flag &= ~GP_SPOINT_SELECT;
1164  /* select new */
1165  pt = &gps->points[0];
1166  pt->flag |= GP_SPOINT_SELECT;
1167  }
1168 
1169  /* if last point, add new point at the end */
1170  if (do_last) {
1172  gps, temp_points, temp_dverts, oldtotpoints - 1, gps->totpoints - 1, true);
1173 
1174  /* deselect old */
1175  pt = &gps->points[gps->totpoints - 2];
1176  pt->flag &= ~GP_SPOINT_SELECT;
1177  /* select new */
1178  pt = &gps->points[gps->totpoints - 1];
1179  pt->flag |= GP_SPOINT_SELECT;
1180  }
1181 
1182  /* Flip stroke if it was only one point to consider extrude point as last point. */
1183  if (gps->totpoints == 2) {
1185  }
1186 
1187  /* Calc geometry data. */
1189 
1190  MEM_SAFE_FREE(temp_points);
1191  MEM_SAFE_FREE(temp_dverts);
1192  }
1193 
1194  /* if the stroke is not reused, deselect */
1195  if (!do_stroke) {
1196  gps->flag &= ~GP_STROKE_SELECT;
1198  }
1199 }
1200 
1202  bGPDframe *gpf,
1203  bGPDstroke *gps,
1204  bGPDcurve *gpc)
1205 {
1206  const int old_num_points = gpc->tot_curve_points;
1207  const bool first_select = gpc->curve_points[0].flag & GP_CURVE_POINT_SELECT;
1208  bool last_select = gpc->curve_points[old_num_points - 1].flag & GP_CURVE_POINT_SELECT;
1209 
1210  /* iterate over middle points */
1211  for (int i = 1; i < gpc->tot_curve_points - 1; i++) {
1212  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1213 
1214  /* Create new stroke if selected point */
1215  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1216  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false, false);
1217  gps_new->points = NULL;
1218  gps_new->flag &= ~GP_STROKE_CYCLIC;
1219  gps_new->prev = gps_new->next = NULL;
1220 
1222  bGPDcurve *new_gpc = gps_new->editcurve;
1223  for (int j = 0; j < new_gpc->tot_curve_points; j++) {
1224  bGPDcurve_point *gpc_pt_new = &new_gpc->curve_points[j];
1225  memcpy(gpc_pt_new, gpc_pt, sizeof(bGPDcurve_point));
1226  gpc_pt_new->flag &= ~GP_CURVE_POINT_SELECT;
1227  BEZT_DESEL_ALL(&gpc_pt_new->bezt);
1228  }
1229 
1230  /* select last point */
1231  bGPDcurve_point *gpc_pt_last = &new_gpc->curve_points[1];
1232  gpc_pt_last->flag |= GP_CURVE_POINT_SELECT;
1233  BEZT_SEL_IDX(&gpc_pt_last->bezt, 1);
1234  gps_new->editcurve->flag |= GP_CURVE_SELECT;
1235 
1236  BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
1237 
1238  gps_new->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
1239  BKE_gpencil_stroke_geometry_update(gpd, gps_new);
1240 
1241  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1242  BEZT_DESEL_ALL(&gpc_pt->bezt);
1243  }
1244  }
1245 
1246  /* Edge-case for single curve point. */
1247  if (gpc->tot_curve_points == 1) {
1248  last_select = false;
1249  }
1250 
1251  if (first_select || last_select) {
1252  int new_num_points = old_num_points;
1253 
1254  if (first_select) {
1255  new_num_points++;
1256  }
1257  if (last_select) {
1258  new_num_points++;
1259  }
1260 
1261  /* Grow the array */
1262  gpc->tot_curve_points = new_num_points;
1263  gpc->curve_points = MEM_recallocN(gpc->curve_points, sizeof(bGPDcurve_point) * new_num_points);
1264 
1265  if (first_select) {
1266  /* shift points by one */
1267  memmove(
1268  &gpc->curve_points[1], &gpc->curve_points[0], sizeof(bGPDcurve_point) * old_num_points);
1269 
1270  bGPDcurve_point *old_first = &gpc->curve_points[1];
1271 
1272  old_first->flag &= ~GP_CURVE_POINT_SELECT;
1273  BEZT_DESEL_ALL(&old_first->bezt);
1274  }
1275 
1276  if (last_select) {
1277  bGPDcurve_point *old_last = &gpc->curve_points[gpc->tot_curve_points - 2];
1278  bGPDcurve_point *new_last = &gpc->curve_points[gpc->tot_curve_points - 1];
1279  memcpy(new_last, old_last, sizeof(bGPDcurve_point));
1280 
1281  old_last->flag &= ~GP_CURVE_POINT_SELECT;
1282  BEZT_DESEL_ALL(&old_last->bezt);
1283  }
1284 
1287  }
1288 }
1289 
1291 {
1292  Object *obact = CTX_data_active_object(C);
1293  bGPdata *gpd = (bGPdata *)obact->data;
1294  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1295  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1296  bGPDstroke *gps = NULL;
1297 
1298  if (gpd == NULL) {
1299  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
1300  return OPERATOR_CANCELLED;
1301  }
1302 
1303  bool changed = false;
1304  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1305  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
1306 
1307  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1308  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1309  if (gpf == NULL) {
1310  continue;
1311  }
1312 
1313  for (gps = gpf->strokes.first; gps; gps = gps->next) {
1314  /* skip strokes that are invalid for current view */
1315  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1316  continue;
1317  }
1318 
1319  if (is_curve_edit) {
1320  if (gps->editcurve == NULL) {
1321  continue;
1322  }
1323  bGPDcurve *gpc = gps->editcurve;
1324  if (gpc->flag & GP_CURVE_SELECT) {
1325  gpencil_curve_extrude_points(gpd, gpf, gps, gpc);
1326  }
1327  }
1328  else {
1329  if (gps->flag & GP_STROKE_SELECT) {
1330  gpencil_add_move_points(gpd, gpf, gps);
1331  }
1332  }
1333 
1334  changed = true;
1335  }
1336  /* If not multi-edit, exit loop. */
1337  if (!is_multiedit) {
1338  break;
1339  }
1340  }
1341  }
1342  }
1343  CTX_DATA_END;
1344 
1345  if (changed) {
1346  /* updates */
1347  DEG_id_tag_update(&gpd->id,
1351  }
1352 
1353  return OPERATOR_FINISHED;
1354 }
1355 
1357 {
1358  /* identifiers */
1359  ot->name = "Extrude Stroke Points";
1360  ot->idname = "GPENCIL_OT_extrude";
1361  ot->description = "Extrude the selected Grease Pencil points";
1362 
1363  /* callbacks */
1366 
1367  /* flags */
1369 }
1370 
1373 /* -------------------------------------------------------------------- */
1390 
1391 /* Hash for hanging on to all the colors used by strokes in the buffer
1392  *
1393  * This is needed to prevent dangling and unsafe pointers when pasting across data-blocks,
1394  * or after a color used by a stroke in the buffer gets deleted (via user action or undo).
1395  */
1397 
1399 {
1400  GHash *ma_to_name = BLI_ghash_ptr_new(__func__);
1401 
1402  for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) {
1403  char *name = BKE_id_to_unique_string_key(&ma->id);
1404  BLI_ghash_insert(ma_to_name, ma, name);
1405  }
1406 
1407  return ma_to_name;
1408 }
1409 
1411 {
1412  BLI_ghash_free(ma_to_name, NULL, MEM_freeN);
1413 }
1414 
1416 {
1417  GHash *name_to_ma = BLI_ghash_str_new(__func__);
1418 
1419  for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) {
1420  char *name = BKE_id_to_unique_string_key(&ma->id);
1421  BLI_ghash_insert(name_to_ma, name, ma);
1422  }
1423 
1424  return name_to_ma;
1425 }
1426 
1428 {
1429  BLI_ghash_free(name_to_ma, MEM_freeN, NULL);
1430 }
1431 
1432 /* Free copy/paste buffer data */
1434 {
1435  bGPDstroke *gps, *gpsn;
1436 
1437  /* Free the colors buffer
1438  * NOTE: This is done before the strokes so that the ptrs are still safe
1439  */
1443  }
1444 
1445  /* Free the stroke buffer */
1446  for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gpsn) {
1447  gpsn = gps->next;
1448 
1449  if (gps->points) {
1450  MEM_freeN(gps->points);
1451  }
1452  if (gps->dvert) {
1454  MEM_freeN(gps->dvert);
1455  }
1456 
1457  MEM_SAFE_FREE(gps->triangles);
1458 
1460  }
1461 
1463 }
1464 
1470 {
1471  Main *bmain = CTX_data_main(C);
1473  GHash *new_colors = BLI_ghash_int_new("GPencil Paste Dst Colors");
1474  GHashIterator gh_iter;
1475 
1476  /* For each color, check if exist and add if not */
1478 
1480  int *key = BLI_ghashIterator_getKey(&gh_iter);
1481  char *ma_name = BLI_ghashIterator_getValue(&gh_iter);
1482  Material *ma = BLI_ghash_lookup(name_to_ma, ma_name);
1483 
1484  BKE_gpencil_object_material_ensure(bmain, ob, ma);
1485 
1486  /* Store this mapping (for use later when pasting) */
1487  if (!BLI_ghash_haskey(new_colors, POINTER_FROM_INT(*key))) {
1488  BLI_ghash_insert(new_colors, POINTER_FROM_INT(*key), ma);
1489  }
1490  }
1491 
1493 
1494  return new_colors;
1495 }
1496 
1499 /* -------------------------------------------------------------------- */
1504 {
1505  Main *bmain = CTX_data_main(C);
1508  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1509 
1510  if (gpd == NULL) {
1511  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
1512  return OPERATOR_CANCELLED;
1513  }
1514 
1515  if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1516  BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1517  return OPERATOR_CANCELLED;
1518  }
1519 
1520  /* clear the buffer first */
1522 
1523  if (is_curve_edit) {
1524  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
1525  }
1526  else {
1527  /* for each visible (and editable) layer's selected strokes,
1528  * copy the strokes into a temporary buffer, then append
1529  * once all done
1530  */
1531  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1532  bGPDframe *gpf = gpl->actframe;
1533  bGPDstroke *gps;
1534 
1535  if (gpf == NULL) {
1536  continue;
1537  }
1538 
1539  /* make copies of selected strokes, and deselect these once we're done */
1540  for (gps = gpf->strokes.first; gps; gps = gps->next) {
1541  /* skip strokes that are invalid for current view */
1542  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1543  continue;
1544  }
1545 
1546  if (gps->flag & GP_STROKE_SELECT) {
1547  if (gps->totpoints == 1) {
1548  /* Special Case: If there's just a single point in this stroke... */
1549  bGPDstroke *gpsd;
1550 
1551  /* make direct copies of the stroke and its points */
1552  gpsd = BKE_gpencil_stroke_duplicate(gps, false, true);
1553 
1554  /* saves original layer name */
1555  BLI_strncpy(
1556  gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
1557  gpsd->points = MEM_dupallocN(gps->points);
1558  if (gps->dvert != NULL) {
1559  gpsd->dvert = MEM_dupallocN(gps->dvert);
1561  }
1562 
1563  /* Calc geometry data. */
1565 
1566  /* add to temp buffer */
1567  gpsd->next = gpsd->prev = NULL;
1569  }
1570  else {
1571  /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
1573  }
1574  }
1575  }
1576  }
1577  CTX_DATA_END;
1578  }
1579 
1580  /* Build up hash of material colors used in these strokes */
1582  gpencil_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors");
1585  if (ED_gpencil_stroke_can_use(C, gps)) {
1586  Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1);
1587  /* Avoid default material. */
1588  if (ma == NULL) {
1589  continue;
1590  }
1591 
1592  char **ma_name_val;
1593  if (!BLI_ghash_ensure_p(
1594  gpencil_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
1595  char *ma_name = BLI_ghash_lookup(ma_to_name, ma);
1596  *ma_name_val = MEM_dupallocN(ma_name);
1597  }
1598  }
1599  }
1601  }
1602 
1603  /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */
1604  WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); /* XXX? */
1605 
1606  /* done */
1607  return OPERATOR_FINISHED;
1608 }
1609 
1611 {
1612  /* identifiers */
1613  ot->name = "Copy Strokes";
1614  ot->idname = "GPENCIL_OT_copy";
1615  ot->description = "Copy selected Grease Pencil points and strokes";
1616 
1617  /* callbacks */
1620 
1621  /* flags */
1622  // ot->flag = OPTYPE_REGISTER;
1623 }
1624 
1627 /* -------------------------------------------------------------------- */
1632 {
1633  ScrArea *area = CTX_wm_area(C);
1634  if (!((area != NULL) && (area->spacetype == SPACE_VIEW3D))) {
1635  return false;
1636  }
1637  /* 1) Must have GP datablock to paste to
1638  * - We don't need to have an active layer though, as that can easily get added
1639  * - If the active layer is locked, we can't paste there,
1640  * but that should prompt a warning instead.
1641  * 2) Copy buffer must at least have something (though it may be the wrong sort...).
1642  */
1643  return (ED_gpencil_data_get_active(C) != NULL) &&
1645 }
1646 
1647 typedef enum eGP_PasteMode {
1651 
1653 {
1655  bGPdata *gpd = (bGPdata *)ob->data;
1656  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1657  bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* only use active for copy merge */
1659  bGPDframe *gpf;
1660 
1661  eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
1662  const bool on_back = RNA_boolean_get(op->ptr, "paste_back");
1663  GHash *new_colors;
1664 
1665  /* Check for various error conditions. */
1666  if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1667  BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1668  return OPERATOR_CANCELLED;
1669  }
1670 
1672  BKE_report(op->reports,
1673  RPT_ERROR,
1674  "No strokes to paste, select and copy some points before trying again");
1675  return OPERATOR_CANCELLED;
1676  }
1677 
1678  if (gpl == NULL) {
1679  /* no active layer - let's just create one */
1680  gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true);
1681  }
1682  else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
1683  BKE_report(
1684  op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
1685  return OPERATOR_CANCELLED;
1686  }
1687  else {
1688  /* Check that some of the strokes in the buffer can be used */
1689  bGPDstroke *gps;
1690  bool ok = false;
1691 
1692  for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
1693  if (ED_gpencil_stroke_can_use(C, gps)) {
1694  ok = true;
1695  break;
1696  }
1697  }
1698 
1699  if (ok == false) {
1700  return OPERATOR_CANCELLED;
1701  }
1702  }
1703 
1704  /* Deselect all strokes first */
1705  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
1706  bGPDspoint *pt;
1707  int i;
1708 
1709  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1710  pt->flag &= ~GP_SPOINT_SELECT;
1711  }
1712 
1713  gps->flag &= ~GP_STROKE_SELECT;
1715  }
1716  CTX_DATA_END;
1717 
1718  /* Ensure that all the necessary colors exist */
1719  new_colors = gpencil_copybuf_validate_colormap(C);
1720 
1721  if (is_curve_edit) {
1722  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
1723  }
1724  else {
1725  /* Copy over the strokes from the buffer (and adjust the colors) */
1726  bGPDstroke *gps_init = (!on_back) ? gpencil_strokes_copypastebuf.first :
1728  for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) {
1729  if (ED_gpencil_stroke_can_use(C, gps)) {
1730  /* Need to verify if layer exists */
1731  if (type != GP_COPY_TO_ACTIVE) {
1732  gpl = BLI_findstring(
1733  &gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
1734  if (gpl == NULL) {
1735  /* no layer - use active (only if layer deleted before paste) */
1736  gpl = BKE_gpencil_layer_active_get(gpd);
1737  }
1738  }
1739 
1740  /* Ensure we have a frame to draw into
1741  * NOTE: Since this is an op which creates strokes,
1742  * we are obliged to add a new frame if one
1743  * doesn't exist already
1744  */
1746  if (gpf) {
1747  /* Create new stroke */
1748  bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
1749  new_stroke->runtime.tmp_layerinfo[0] = '\0';
1750  new_stroke->next = new_stroke->prev = NULL;
1751 
1752  /* Calc geometry data. */
1753  BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
1754 
1755  if (on_back) {
1756  BLI_addhead(&gpf->strokes, new_stroke);
1757  }
1758  else {
1759  BLI_addtail(&gpf->strokes, new_stroke);
1760  }
1761 
1762  /* Remap material */
1763  Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
1764  new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
1765  CLAMP_MIN(new_stroke->mat_nr, 0);
1766  }
1767  }
1768  }
1769  }
1770 
1771  /* free temp data */
1772  BLI_ghash_free(new_colors, NULL, NULL);
1773 
1774  /* updates */
1777 
1778  return OPERATOR_FINISHED;
1779 }
1780 
1782 {
1783  PropertyRNA *prop;
1784 
1785  static const EnumPropertyItem copy_type[] = {
1786  {GP_COPY_TO_ACTIVE, "ACTIVE", 0, "Paste to Active", ""},
1787  {GP_COPY_BY_LAYER, "LAYER", 0, "Paste by Layer", ""},
1788  {0, NULL, 0, NULL, NULL},
1789  };
1790 
1791  /* identifiers */
1792  ot->name = "Paste Strokes";
1793  ot->idname = "GPENCIL_OT_paste";
1794  ot->description = "Paste previously copied strokes to active layer or to original layer";
1795 
1796  /* callbacks */
1799 
1800  /* flags */
1802 
1803  /* properties */
1804  ot->prop = RNA_def_enum(ot->srna, "type", copy_type, GP_COPY_TO_ACTIVE, "Type", "");
1805 
1806  prop = RNA_def_boolean(
1807  ot->srna, "paste_back", 0, "Paste on Back", "Add pasted strokes behind all strokes");
1809 }
1810 
1813 /* -------------------------------------------------------------------- */
1818 {
1820  bGPdata *gpd = (bGPdata *)ob->data;
1822  bGPDlayer *target_layer = NULL;
1823  ListBase strokes = {NULL, NULL};
1824  int layer_num = RNA_int_get(op->ptr, "layer");
1825  const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
1826 
1827  if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1828  BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1829  return OPERATOR_CANCELLED;
1830  }
1831 
1832  /* if autolock enabled, disabled now */
1833  if (use_autolock) {
1834  gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
1835  }
1836 
1837  /* Try to get layer */
1838  if (layer_num > -1) {
1839  target_layer = BLI_findlink(&gpd->layers, layer_num);
1840  }
1841  else {
1842  /* Create a new layer. */
1843  target_layer = BKE_gpencil_layer_addnew(gpd, "GP_Layer", true);
1844  }
1845 
1846  if (target_layer == NULL) {
1847  /* back autolock status */
1848  if (use_autolock) {
1849  gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
1850  }
1851  BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
1852  return OPERATOR_CANCELLED;
1853  }
1854 
1855  /* Extract all strokes to move to this layer
1856  * NOTE: We need to do this in a two-pass system to avoid conflicts with strokes
1857  * getting repeatedly moved
1858  */
1859  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1860  bGPDframe *gpf = gpl->actframe;
1861 
1862  /* skip if no frame with strokes, or if this is the layer we're moving strokes to */
1863  if ((gpl == target_layer) || (gpf == NULL)) {
1864  continue;
1865  }
1866 
1867  /* make copies of selected strokes, and deselect these once we're done */
1868  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
1869 
1870  /* skip strokes that are invalid for current view */
1871  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1872  continue;
1873  }
1874 
1875  /* Check if the color is editable. */
1876  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
1877  continue;
1878  }
1879 
1880  /* TODO: Don't just move entire strokes - instead, only copy the selected portions... */
1881  if (gps->flag & GP_STROKE_SELECT) {
1882  BLI_remlink(&gpf->strokes, gps);
1883  BLI_addtail(&strokes, gps);
1884  }
1885  }
1886 
1887  /* if new layer and autolock, lock old layer */
1888  if ((layer_num == -1) && (use_autolock)) {
1889  gpl->flag |= GP_LAYER_LOCKED;
1890  }
1891  }
1892  CTX_DATA_END;
1893 
1894  /* Paste them all in one go */
1895  if (strokes.first) {
1897 
1898  BLI_movelisttolist(&gpf->strokes, &strokes);
1899  BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
1900  }
1901 
1902  /* back autolock status */
1903  if (use_autolock) {
1904  gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
1905  }
1906 
1907  /* updates */
1910 
1911  return OPERATOR_FINISHED;
1912 }
1913 
1915 {
1916  /* identifiers */
1917  ot->name = "Move Strokes to Layer";
1918  ot->idname = "GPENCIL_OT_move_to_layer";
1919  ot->description =
1920  "Move selected strokes to another layer"; /* XXX: allow moving individual points too? */
1921 
1922  /* callbacks */
1924  ot->poll = gpencil_stroke_edit_poll; /* XXX? */
1925 
1926  /* flags */
1928 
1929  /* GPencil layer to use. */
1930  ot->prop = RNA_def_int(
1931  ot->srna, "layer", 0, -1, INT_MAX, "Grease Pencil Layer", "", -1, INT_MAX);
1933 }
1934 
1937 /* -------------------------------------------------------------------- */
1942 {
1945  int cfra = CFRA;
1946 
1947  bGPDlayer *active_gpl = BKE_gpencil_layer_active_get(gpd);
1948 
1949  const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
1950 
1951  /* Initialize data-block and an active layer if nothing exists yet. */
1952  if (ELEM(NULL, gpd, active_gpl)) {
1953  /* Let's just be lazy, and call the "Add New Layer" operator,
1954  * which sets everything up as required. */
1955  WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL);
1956  }
1957 
1958  /* Go through each layer, adding a frame after the active one
1959  * and/or shunting all the others out of the way
1960  */
1961  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1962  if ((all_layers == false) && (gpl != active_gpl)) {
1963  continue;
1964  }
1965 
1966  /* 1) Check for an existing frame on the current frame */
1967  bGPDframe *gpf = BKE_gpencil_layer_frame_find(gpl, cfra);
1968  if (gpf) {
1969  /* Shunt all frames after (and including) the existing one later by 1-frame */
1970  for (; gpf; gpf = gpf->next) {
1971  gpf->framenum += 1;
1972  }
1973  }
1974 
1975  /* 2) Now add a new frame, with nothing in it */
1976  gpl->actframe = BKE_gpencil_layer_frame_get(gpl, cfra, GP_GETFRAME_ADD_NEW);
1977  }
1978  CTX_DATA_END;
1979 
1980  /* notifiers */
1983 
1984  return OPERATOR_FINISHED;
1985 }
1986 
1988 {
1989  PropertyRNA *prop;
1990 
1991  /* identifiers */
1992  ot->name = "Insert Blank Frame";
1993  ot->idname = "GPENCIL_OT_blank_frame_add";
1994  ot->description =
1995  "Insert a blank frame on the current frame "
1996  "(all subsequently existing frames, if any, are shifted right by one frame)";
1997 
1998  /* callbacks */
2001 
2003 
2004  /* properties */
2005  prop = RNA_def_boolean(ot->srna,
2006  "all_layers",
2007  false,
2008  "All Layers",
2009  "Create blank frame in all layers, not only active");
2011 }
2012 
2015 /* -------------------------------------------------------------------- */
2020 {
2023 
2024  /* only if there's an active layer with an active frame */
2025  return (gpl && gpl->actframe);
2026 }
2027 
2029 {
2032 
2033  /* only if there's an active layer with an active frame */
2034  return (gpl && gpl->actframe);
2035 }
2036 
2037 /* delete active frame - wrapper around API calls */
2039 {
2040  const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_annotation_active_frame_delete");
2041 
2042  bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) :
2044 
2046 
2048 
2050 
2051  /* if there's no existing Grease-Pencil data there, add some */
2052  if (gpd == NULL) {
2053  BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
2054  return OPERATOR_CANCELLED;
2055  }
2056  if (ELEM(NULL, gpl, gpf)) {
2057  BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
2058  return OPERATOR_CANCELLED;
2059  }
2060 
2061  /* delete it... */
2063 
2064  /* notifiers */
2067 
2068  return OPERATOR_FINISHED;
2069 }
2070 
2072 {
2073  /* identifiers */
2074  ot->name = "Delete Active Frame";
2075  ot->idname = "GPENCIL_OT_active_frame_delete";
2076  ot->description = "Delete the active frame for the active Grease Pencil Layer";
2077 
2079 
2080  /* callbacks */
2083 }
2084 
2086 {
2087  /* identifiers */
2088  ot->name = "Delete Active Frame";
2089  ot->idname = "GPENCIL_OT_annotation_active_frame_delete";
2090  ot->description = "Delete the active frame for the active Annotation Layer";
2091 
2093 
2094  /* callbacks */
2097 }
2098 
2101 /* -------------------------------------------------------------------- */
2106 {
2108 
2109  /* 1) There must be grease pencil data
2110  * 2) Hopefully some of the layers have stuff we can use
2111  */
2112  return (gpd && gpd->layers.first);
2113 }
2114 
2116 {
2119 
2120  bool success = false;
2121 
2122  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
2123  /* try to get the "active" frame - but only if it actually occurs on this frame */
2125 
2126  if (gpf == NULL) {
2127  continue;
2128  }
2129 
2130  /* delete it... */
2132 
2133  /* we successfully modified something */
2134  success = true;
2135  }
2136  CTX_DATA_END;
2137 
2138  /* updates */
2139  if (success) {
2142  return OPERATOR_FINISHED;
2143  }
2144  BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
2145  return OPERATOR_CANCELLED;
2146 }
2147 
2149 {
2150  /* identifiers */
2151  ot->name = "Delete All Active Frames";
2152  ot->idname = "GPENCIL_OT_active_frames_delete_all";
2153  ot->description = "Delete the active frame(s) of all editable Grease Pencil layers";
2154 
2156 
2157  /* callbacks */
2160 }
2161 
2164 /* -------------------------------------------------------------------- */
2168 typedef enum eGP_DeleteMode {
2169  /* delete selected stroke points */
2171  /* delete selected strokes */
2173  /* delete active frame */
2176 
2177 typedef enum eGP_DissolveMode {
2178  /* dissolve all selected points */
2180  /* dissolve between selected points */
2182  /* dissolve unselected points */
2185 
2186 /* Delete selected strokes */
2188 {
2190  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
2191 
2192  bool changed = false;
2193  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
2194  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
2195 
2196  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
2197  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
2198 
2199  if (gpf == NULL) {
2200  continue;
2201  }
2202 
2203  /* simply delete strokes which are selected */
2204  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
2205 
2206  /* skip strokes that are invalid for current view */
2207  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2208  continue;
2209  }
2210 
2211  /* free stroke if selected */
2212  if (gps->flag & GP_STROKE_SELECT) {
2213  BLI_remlink(&gpf->strokes, gps);
2214  /* free stroke memory arrays, then stroke itself */
2216 
2217  changed = true;
2218  }
2219  }
2220  }
2221  }
2222  }
2223  CTX_DATA_END;
2224 
2225  if (changed) {
2228  return OPERATOR_FINISHED;
2229  }
2230  return OPERATOR_CANCELLED;
2231 }
2232 
2233 /* ----------------------------------- */
2234 
2236  bGPdata *gpd,
2237  eGP_DissolveMode mode)
2238 {
2239  bool changed = false;
2240  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
2241  {
2242  if (gpc->flag & GP_CURVE_SELECT) {
2243  int first = 0, last = 0;
2244  int num_points_remaining = gpc->tot_curve_points;
2245 
2246  switch (mode) {
2247  case GP_DISSOLVE_POINTS:
2248  for (int i = 0; i < gpc->tot_curve_points; i++) {
2249  bGPDcurve_point *cpt = &gpc->curve_points[i];
2250  if (cpt->flag & GP_CURVE_POINT_SELECT) {
2251  num_points_remaining--;
2252  }
2253  }
2254  break;
2255  case GP_DISSOLVE_BETWEEN:
2256  first = -1;
2257  for (int i = 0; i < gpc->tot_curve_points; i++) {
2258  bGPDcurve_point *cpt = &gpc->curve_points[i];
2259  if (cpt->flag & GP_CURVE_POINT_SELECT) {
2260  if (first < 0) {
2261  first = i;
2262  }
2263  last = i;
2264  }
2265  }
2266 
2267  for (int i = first + 1; i < last; i++) {
2268  bGPDcurve_point *cpt = &gpc->curve_points[i];
2269  if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
2270  num_points_remaining--;
2271  }
2272  }
2273  break;
2274  case GP_DISSOLVE_UNSELECT:
2275  for (int i = 0; i < gpc->tot_curve_points; i++) {
2276  bGPDcurve_point *cpt = &gpc->curve_points[i];
2277  if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
2278  num_points_remaining--;
2279  }
2280  }
2281  break;
2282  default:
2283  return false;
2284  break;
2285  }
2286 
2287  if (num_points_remaining < 1) {
2288  /* Delete stroke */
2289  BLI_remlink(&gpf_->strokes, gps);
2291  }
2292  else {
2293  bGPDcurve_point *new_points = MEM_callocN(sizeof(bGPDcurve_point) * num_points_remaining,
2294  __func__);
2295 
2296  int idx = 0;
2297  switch (mode) {
2298  case GP_DISSOLVE_POINTS:
2299  for (int i = 0; i < gpc->tot_curve_points; i++) {
2300  bGPDcurve_point *cpt = &gpc->curve_points[i];
2301  bGPDcurve_point *new_cpt = &new_points[idx];
2302  if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
2303  *new_cpt = *cpt;
2304  idx++;
2305  }
2306  }
2307  break;
2308  case GP_DISSOLVE_BETWEEN:
2309  for (int i = 0; i < first; i++) {
2310  bGPDcurve_point *cpt = &gpc->curve_points[i];
2311  bGPDcurve_point *new_cpt = &new_points[idx];
2312 
2313  *new_cpt = *cpt;
2314  idx++;
2315  }
2316 
2317  for (int i = first; i < last; i++) {
2318  bGPDcurve_point *cpt = &gpc->curve_points[i];
2319  bGPDcurve_point *new_cpt = &new_points[idx];
2320  if (cpt->flag & GP_CURVE_POINT_SELECT) {
2321  *new_cpt = *cpt;
2322  idx++;
2323  }
2324  }
2325 
2326  for (int i = last; i < gpc->tot_curve_points; i++) {
2327  bGPDcurve_point *cpt = &gpc->curve_points[i];
2328  bGPDcurve_point *new_cpt = &new_points[idx];
2329 
2330  *new_cpt = *cpt;
2331  idx++;
2332  }
2333  break;
2334  case GP_DISSOLVE_UNSELECT:
2335  for (int i = 0; i < gpc->tot_curve_points; i++) {
2336  bGPDcurve_point *cpt = &gpc->curve_points[i];
2337  bGPDcurve_point *new_cpt = &new_points[idx];
2338  if (cpt->flag & GP_CURVE_POINT_SELECT) {
2339  *new_cpt = *cpt;
2340  idx++;
2341  }
2342  }
2343  break;
2344  default:
2345  return false;
2346  break;
2347  }
2348 
2349  if (gpc->curve_points != NULL) {
2350  MEM_freeN(gpc->curve_points);
2351  }
2352 
2353  gpc->curve_points = new_points;
2354  gpc->tot_curve_points = num_points_remaining;
2355 
2357  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
2359  }
2360 
2361  changed = true;
2362  }
2363  }
2364  GP_EDITABLE_CURVES_END(gps_iter);
2365 
2366  return changed;
2367 }
2368 
2370  bGPdata *gpd,
2371  eGP_DissolveMode mode)
2372 {
2373  bool changed = false;
2374  int first = 0;
2375  int last = 0;
2376 
2377  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
2378  /* the stroke must have at least one point selected for any operator */
2379  if (gps->flag & GP_STROKE_SELECT) {
2380  bGPDspoint *pt;
2381  MDeformVert *dvert = NULL;
2382  int i;
2383 
2384  int tot = gps->totpoints; /* number of points in new buffer */
2385 
2386  /* first pass: count points to remove */
2387  switch (mode) {
2388  case GP_DISSOLVE_POINTS:
2389  /* Count how many points are selected (i.e. how many to remove) */
2390  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2391  if (pt->flag & GP_SPOINT_SELECT) {
2392  /* selected point - one of the points to remove */
2393  tot--;
2394  }
2395  }
2396  break;
2397  case GP_DISSOLVE_BETWEEN:
2398  /* need to find first and last point selected */
2399  first = -1;
2400  last = 0;
2401  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2402  if (pt->flag & GP_SPOINT_SELECT) {
2403  if (first < 0) {
2404  first = i;
2405  }
2406  last = i;
2407  }
2408  }
2409  /* count unselected points in the range */
2410  for (i = first, pt = gps->points + first; i < last; i++, pt++) {
2411  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
2412  tot--;
2413  }
2414  }
2415  break;
2416  case GP_DISSOLVE_UNSELECT:
2417  /* count number of unselected points */
2418  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2419  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
2420  tot--;
2421  }
2422  }
2423  break;
2424  default:
2425  return false;
2426  break;
2427  }
2428 
2429  /* if no points are left, we simply delete the entire stroke */
2430  if (tot <= 0) {
2431  /* remove the entire stroke */
2432  BLI_remlink(&gpf_->strokes, gps);
2434  }
2435  else {
2436  /* just copy all points to keep into a smaller buffer */
2437  bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot,
2438  "new gp stroke points copy");
2439  bGPDspoint *npt = new_points;
2440 
2441  MDeformVert *new_dvert = NULL;
2442  MDeformVert *ndvert = NULL;
2443 
2444  if (gps->dvert != NULL) {
2445  new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
2446  ndvert = new_dvert;
2447  }
2448 
2449  switch (mode) {
2450  case GP_DISSOLVE_POINTS:
2451  (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
2452  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2453  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
2454  *npt = *pt;
2455  npt++;
2456 
2457  if (gps->dvert != NULL) {
2458  *ndvert = *dvert;
2459  ndvert->dw = MEM_dupallocN(dvert->dw);
2460  ndvert++;
2461  }
2462  }
2463  if (gps->dvert != NULL) {
2464  dvert++;
2465  }
2466  }
2467  break;
2468  case GP_DISSOLVE_BETWEEN:
2469  /* copy first segment */
2470  (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
2471  for (i = 0, pt = gps->points; i < first; i++, pt++) {
2472  *npt = *pt;
2473  npt++;
2474 
2475  if (gps->dvert != NULL) {
2476  *ndvert = *dvert;
2477  ndvert->dw = MEM_dupallocN(dvert->dw);
2478  ndvert++;
2479  dvert++;
2480  }
2481  }
2482  /* copy segment (selected points) */
2483  (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
2484  for (i = first, pt = gps->points + first; i < last; i++, pt++) {
2485  if (pt->flag & GP_SPOINT_SELECT) {
2486  *npt = *pt;
2487  npt++;
2488 
2489  if (gps->dvert != NULL) {
2490  *ndvert = *dvert;
2491  ndvert->dw = MEM_dupallocN(dvert->dw);
2492  ndvert++;
2493  }
2494  }
2495  if (gps->dvert != NULL) {
2496  dvert++;
2497  }
2498  }
2499  /* copy last segment */
2500  (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
2501  for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
2502  *npt = *pt;
2503  npt++;
2504 
2505  if (gps->dvert != NULL) {
2506  *ndvert = *dvert;
2507  ndvert->dw = MEM_dupallocN(dvert->dw);
2508  ndvert++;
2509  dvert++;
2510  }
2511  }
2512 
2513  break;
2514  case GP_DISSOLVE_UNSELECT:
2515  /* copy any selected point */
2516  (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
2517  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2518  if (pt->flag & GP_SPOINT_SELECT) {
2519  *npt = *pt;
2520  npt++;
2521 
2522  if (gps->dvert != NULL) {
2523  *ndvert = *dvert;
2524  ndvert->dw = MEM_dupallocN(dvert->dw);
2525  ndvert++;
2526  }
2527  }
2528  if (gps->dvert != NULL) {
2529  dvert++;
2530  }
2531  }
2532  break;
2533  }
2534 
2535  /* free the old buffer */
2536  if (gps->points) {
2537  MEM_freeN(gps->points);
2538  }
2539  if (gps->dvert) {
2541  MEM_freeN(gps->dvert);
2542  }
2543 
2544  /* save the new buffer */
2545  gps->points = new_points;
2546  gps->dvert = new_dvert;
2547  gps->totpoints = tot;
2548 
2549  /* Calc geometry data. */
2551 
2552  /* deselect the stroke, since none of its selected points will still be selected */
2553  gps->flag &= ~GP_STROKE_SELECT;
2555  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2556  pt->flag &= ~GP_SPOINT_SELECT;
2557  }
2558  }
2559 
2560  changed = true;
2561  }
2562  }
2563  GP_EDITABLE_STROKES_END(gpstroke_iter);
2564 
2565  return changed;
2566 }
2567 
2568 /* Delete selected points but keep the stroke */
2570 {
2572  bGPdata *gpd = (bGPdata *)ob->data;
2573  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2574  bool changed = false;
2575 
2576  if (is_curve_edit) {
2577  changed = gpencil_dissolve_selected_curve_points(C, gpd, mode);
2578  }
2579  else {
2580  changed = gpencil_dissolve_selected_stroke_points(C, gpd, mode);
2581  }
2582 
2583  if (changed) {
2586  return OPERATOR_FINISHED;
2587  }
2588  return OPERATOR_CANCELLED;
2589 }
2590 
2591 /* ----------------------------------- */
2592 
2593 /* Split selected strokes into segments, splitting on selected points */
2595 {
2598  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2599  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
2600  bool changed = false;
2601 
2602  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
2603  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
2604 
2605  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
2606  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
2607 
2608  if (gpf == NULL) {
2609  continue;
2610  }
2611 
2612  /* simply delete strokes which are selected */
2613  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
2614 
2615  /* skip strokes that are invalid for current view */
2616  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2617  continue;
2618  }
2619  /* check if the color is editable */
2620  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
2621  continue;
2622  }
2623 
2624  if (gps->flag & GP_STROKE_SELECT) {
2625  /* deselect old stroke, since it will be used as template for the new strokes */
2626  gps->flag &= ~GP_STROKE_SELECT;
2628 
2629  if (is_curve_edit) {
2630  bGPDcurve *gpc = gps->editcurve;
2632  gpd, gpf, gps, gps->next, gpc, GP_CURVE_POINT_SELECT);
2633  }
2634  else {
2635  /* delete unwanted points by splitting stroke into several smaller ones */
2637  gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, false, 0);
2638  }
2639 
2640  changed = true;
2641  }
2642  }
2643  }
2644  }
2645  }
2646  CTX_DATA_END;
2647 
2648  if (changed) {
2651  return OPERATOR_FINISHED;
2652  }
2653  return OPERATOR_CANCELLED;
2654 }
2655 
2656 /* simple wrapper to external call */
2658 {
2660 }
2661 
2664 /* -------------------------------------------------------------------- */
2669 {
2670  eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
2671  int result = OPERATOR_CANCELLED;
2672 
2673  switch (mode) {
2674  case GP_DELETEOP_STROKES: /* selected strokes */
2676  break;
2677 
2678  case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */
2680  break;
2681 
2682  case GP_DELETEOP_FRAME: /* active frame */
2684  break;
2685  }
2686 
2687  return result;
2688 }
2689 
2691 {
2692  static const EnumPropertyItem prop_gpencil_delete_types[] = {
2694  "POINTS",
2695  0,
2696  "Points",
2697  "Delete selected points and split strokes into segments"},
2698  {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
2699  {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
2700  {0, NULL, 0, NULL, NULL},
2701  };
2702 
2703  /* identifiers */
2704  ot->name = "Delete";
2705  ot->idname = "GPENCIL_OT_delete";
2706  ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
2707 
2708  /* callbacks */
2712 
2713  /* flags */
2715 
2716  /* props */
2717  ot->prop = RNA_def_enum(ot->srna,
2718  "type",
2719  prop_gpencil_delete_types,
2720  0,
2721  "Type",
2722  "Method used for deleting Grease Pencil data");
2723 }
2724 
2727 /* -------------------------------------------------------------------- */
2732 {
2733  eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
2734 
2735  return gpencil_dissolve_selected_points(C, mode);
2736 }
2737 
2739 {
2740  static EnumPropertyItem prop_gpencil_dissolve_types[] = {
2741  {GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points"},
2743  "BETWEEN",
2744  0,
2745  "Dissolve Between",
2746  "Dissolve points between selected points"},
2747  {GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points"},
2748  {0, NULL, 0, NULL, NULL},
2749  };
2750 
2751  /* identifiers */
2752  ot->name = "Dissolve";
2753  ot->idname = "GPENCIL_OT_dissolve";
2754  ot->description = "Delete selected points without splitting strokes";
2755 
2756  /* callbacks */
2760 
2761  /* flags */
2763 
2764  /* props */
2765  ot->prop = RNA_def_enum(ot->srna,
2766  "type",
2767  prop_gpencil_dissolve_types,
2768  0,
2769  "Type",
2770  "Method used for dissolving stroke points");
2771 }
2772 
2775 /* -------------------------------------------------------------------- */
2779 /* Poll callback for snap operators */
2780 /* NOTE: For now, we only allow these in the 3D view, as other editors do not
2781  * define a cursor or gridstep which can be used
2782  */
2784 {
2785  ScrArea *area = CTX_wm_area(C);
2787 
2788  return (ob != NULL) && (ob->type == OB_GPENCIL) &&
2789  ((area != NULL) && (area->spacetype == SPACE_VIEW3D));
2790 }
2791 
2793 {
2795  ARegion *region = CTX_wm_region(C);
2796  View3D *v3d = CTX_wm_view3d(C);
2799  Object *obact = CTX_data_active_object(C);
2800  const float gridf = ED_view3d_grid_view_scale(scene, v3d, region, NULL);
2801  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2802 
2803  bool changed = false;
2804  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2805  /* only editable and visible layers are considered */
2806  if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2807  bGPDframe *gpf = gpl->actframe;
2808  float diff_mat[4][4];
2809 
2810  /* calculate difference matrix object */
2811  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
2812 
2813  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2814  /* skip strokes that are invalid for current view */
2815  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2816  continue;
2817  }
2818  /* check if the color is editable */
2819  if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
2820  continue;
2821  }
2822 
2823  if (is_curve_edit) {
2824  if (gps->editcurve == NULL) {
2825  continue;
2826  }
2827  float inv_diff_mat[4][4];
2828  invert_m4_m4_safe(inv_diff_mat, diff_mat);
2829 
2830  bGPDcurve *gpc = gps->editcurve;
2831  for (int i = 0; i < gpc->tot_curve_points; i++) {
2832  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
2833  BezTriple *bezt = &gpc_pt->bezt;
2834  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
2835  float tmp0[3], tmp1[3], tmp2[3], offset[3];
2836  mul_v3_m4v3(tmp0, diff_mat, bezt->vec[0]);
2837  mul_v3_m4v3(tmp1, diff_mat, bezt->vec[1]);
2838  mul_v3_m4v3(tmp2, diff_mat, bezt->vec[2]);
2839 
2840  /* calculate the offset vector */
2841  offset[0] = gridf * floorf(0.5f + tmp1[0] / gridf) - tmp1[0];
2842  offset[1] = gridf * floorf(0.5f + tmp1[1] / gridf) - tmp1[1];
2843  offset[2] = gridf * floorf(0.5f + tmp1[2] / gridf) - tmp1[2];
2844 
2845  /* shift bezTriple */
2846  add_v3_v3(bezt->vec[0], offset);
2847  add_v3_v3(bezt->vec[1], offset);
2848  add_v3_v3(bezt->vec[2], offset);
2849 
2850  mul_v3_m4v3(tmp0, inv_diff_mat, bezt->vec[0]);
2851  mul_v3_m4v3(tmp1, inv_diff_mat, bezt->vec[1]);
2852  mul_v3_m4v3(tmp2, inv_diff_mat, bezt->vec[2]);
2853  copy_v3_v3(bezt->vec[0], tmp0);
2854  copy_v3_v3(bezt->vec[1], tmp1);
2855  copy_v3_v3(bezt->vec[2], tmp2);
2856 
2857  changed = true;
2858  }
2859  }
2860 
2861  if (changed) {
2863  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
2865  }
2866  }
2867  else {
2868  /* TODO: if entire stroke is selected, offset entire stroke by same amount? */
2869  for (int i = 0; i < gps->totpoints; i++) {
2870  bGPDspoint *pt = &gps->points[i];
2871  /* only if point is selected */
2872  if (pt->flag & GP_SPOINT_SELECT) {
2873  /* apply parent transformations */
2874  float fpt[3];
2875  mul_v3_m4v3(fpt, diff_mat, &pt->x);
2876 
2877  fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
2878  fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
2879  fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
2880 
2881  /* return data */
2882  copy_v3_v3(&pt->x, fpt);
2883  gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
2884 
2885  changed = true;
2886  }
2887  }
2888  }
2889  }
2890  }
2891  }
2892 
2893  if (changed) {
2897  }
2898 
2899  return OPERATOR_FINISHED;
2900 }
2901 
2903 {
2904  /* identifiers */
2905  ot->name = "Snap Selection to Grid";
2906  ot->idname = "GPENCIL_OT_snap_to_grid";
2907  ot->description = "Snap selected points to the nearest grid points";
2908 
2909  /* callbacks */
2912 
2913  /* flags */
2915 }
2916 
2919 /* -------------------------------------------------------------------- */
2924 {
2926  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2929  Object *obact = CTX_data_active_object(C);
2930 
2931  const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
2932  const float *cursor_global = scene->cursor.location;
2933 
2934  bool changed = false;
2935  if (is_curve_edit) {
2936  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
2937  }
2938  else {
2939  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2940  /* only editable and visible layers are considered */
2941  if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2942  bGPDframe *gpf = gpl->actframe;
2943  float diff_mat[4][4];
2944 
2945  /* calculate difference matrix */
2946  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
2947 
2948  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2949  bGPDspoint *pt;
2950  int i;
2951 
2952  /* skip strokes that are invalid for current view */
2953  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2954  continue;
2955  }
2956  /* check if the color is editable */
2957  if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
2958  continue;
2959  }
2960  /* only continue if this stroke is selected (editable doesn't guarantee this)... */
2961  if ((gps->flag & GP_STROKE_SELECT) == 0) {
2962  continue;
2963  }
2964 
2965  if (use_offset) {
2966  float offset[3];
2967 
2968  /* compute offset from first point of stroke to cursor */
2969  /* TODO: Allow using midpoint instead? */
2970  sub_v3_v3v3(offset, cursor_global, &gps->points->x);
2971 
2972  /* apply offset to all points in the stroke */
2973  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2974  add_v3_v3(&pt->x, offset);
2975  }
2976 
2977  changed = true;
2978  }
2979  else {
2980  /* affect each selected point */
2981  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2982  if (pt->flag & GP_SPOINT_SELECT) {
2983  copy_v3_v3(&pt->x, cursor_global);
2984  gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
2985 
2986  changed = true;
2987  }
2988  }
2989  }
2990  }
2991  }
2992  }
2993  }
2994 
2995  if (changed) {
2999  }
3000 
3001  return OPERATOR_FINISHED;
3002 }
3003 
3005 {
3006  /* identifiers */
3007  ot->name = "Snap Selection to Cursor";
3008  ot->idname = "GPENCIL_OT_snap_to_cursor";
3009  ot->description = "Snap selected points/strokes to the cursor";
3010 
3011  /* callbacks */
3014 
3015  /* flags */
3017 
3018  /* props */
3020  "use_offset",
3021  true,
3022  "With Offset",
3023  "Offset the entire stroke instead of selected points only");
3024 }
3025 
3028 /* -------------------------------------------------------------------- */
3033  bContext *C,
3034  Object *obact,
3035  bGPdata *gpd,
3036  float r_centroid[3],
3037  float r_min[3],
3038  float r_max[3],
3039  size_t *count)
3040 {
3041  bool changed = false;
3042  /* calculate midpoints from selected points */
3043  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
3044  /* only editable and visible layers are considered */
3045  if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
3046  bGPDframe *gpf = gpl->actframe;
3047  float diff_mat[4][4];
3048 
3049  /* calculate difference matrix */
3050  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
3051 
3052  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3053  bGPDspoint *pt;
3054  int i;
3055 
3056  /* skip strokes that are invalid for current view */
3057  if (ED_gpencil_stroke_can_use(C, gps) == false) {
3058  continue;
3059  }
3060  /* check if the color is editable */
3061  if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
3062  continue;
3063  }
3064  /* only continue if this stroke is selected (editable doesn't guarantee this)... */
3065  if ((gps->flag & GP_STROKE_SELECT) == 0) {
3066  continue;
3067  }
3068 
3069  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
3070  if (pt->flag & GP_SPOINT_SELECT) {
3071  /* apply parent transformations */
3072  float fpt[3];
3073  mul_v3_m4v3(fpt, diff_mat, &pt->x);
3074 
3075  add_v3_v3(r_centroid, fpt);
3076  minmax_v3v3_v3(r_min, r_max, fpt);
3077 
3078  (*count)++;
3079  }
3080  }
3081 
3082  changed = true;
3083  }
3084  }
3085  }
3086 
3087  return changed;
3088 }
3089 
3091 {
3093  Object *obact = CTX_data_active_object(C);
3095  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3096 
3098 
3099  float *cursor = scene->cursor.location;
3100  float centroid[3] = {0.0f};
3101  float min[3], max[3];
3102  size_t count = 0;
3103 
3104  INIT_MINMAX(min, max);
3105 
3106  bool changed = false;
3107  if (is_curve_edit) {
3108  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
3109  }
3110  else {
3111  changed = gpencil_stroke_points_centroid(depsgraph, C, obact, gpd, centroid, min, max, &count);
3112  }
3113 
3114  if (changed) {
3116  mid_v3_v3v3(cursor, min, max);
3117  }
3118  else { /* #V3D_AROUND_CENTER_MEDIAN. */
3119  zero_v3(cursor);
3120  if (count) {
3121  mul_v3_fl(centroid, 1.0f / (float)count);
3122  copy_v3_v3(cursor, centroid);
3123  }
3124  }
3125 
3128  }
3129 
3130  return OPERATOR_FINISHED;
3131 }
3132 
3134 {
3135  /* identifiers */
3136  ot->name = "Snap Cursor to Selected Points";
3137  ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
3138  ot->description = "Snap cursor to center of selected points";
3139 
3140  /* callbacks */
3143 
3144  /* flags */
3146 }
3147 
3150 /* -------------------------------------------------------------------- */
3155 {
3158 
3159  /* sanity checks */
3160  if (ELEM(NULL, gpd, gpl, gpl->frames.first)) {
3161  return OPERATOR_CANCELLED;
3162  }
3163 
3164  /* loop all strokes */
3165  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
3166  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3167  /* Apply thickness */
3168  if ((gps->thickness == 0) && (gpl->line_change == 0)) {
3169  gps->thickness = gpl->thickness;
3170  }
3171  else {
3172  gps->thickness = gps->thickness + gpl->line_change;
3173  }
3174  }
3175  }
3176 
3177  /* clear value */
3178  gpl->thickness = 0.0f;
3179  gpl->line_change = 0;
3180 
3181  /* notifiers */
3184 
3185  return OPERATOR_FINISHED;
3186 }
3187 
3189 {
3190  /* identifiers */
3191  ot->name = "Apply Stroke Thickness";
3192  ot->idname = "GPENCIL_OT_stroke_apply_thickness";
3193  ot->description = "Apply the thickness change of the layer to its strokes";
3194 
3195  /* api callbacks */
3198 }
3199 
3202 /* -------------------------------------------------------------------- */
3206 enum {
3210 };
3211 
3213 {
3216 
3217  const int type = RNA_enum_get(op->ptr, "type");
3218  const bool geometry = RNA_boolean_get(op->ptr, "geometry");
3219  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
3220  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3221  bGPDstroke *gps = NULL;
3222 
3223  /* sanity checks */
3224  if (ELEM(NULL, gpd)) {
3225  return OPERATOR_CANCELLED;
3226  }
3227 
3228  bool changed = false;
3229  /* loop all selected strokes */
3230  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3231  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
3232 
3233  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
3234  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
3235  if (gpf == NULL) {
3236  continue;
3237  }
3238 
3239  for (gps = gpf->strokes.first; gps; gps = gps->next) {
3240  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
3241  /* skip strokes that are not selected or invalid for current view */
3242  if (((gps->flag & GP_STROKE_SELECT) == 0) ||
3243  ED_gpencil_stroke_can_use(C, gps) == false) {
3244  continue;
3245  }
3246  /* skip hidden or locked colors */
3247  if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
3248  (gp_style->flag & GP_MATERIAL_LOCKED)) {
3249  continue;
3250  }
3251 
3252  bool before = (bool)(gps->flag & GP_STROKE_CYCLIC);
3253  switch (type) {
3255  /* Close all (enable) */
3256  gps->flag |= GP_STROKE_CYCLIC;
3257  break;
3258  case GP_STROKE_CYCLIC_OPEN:
3259  /* Open all (disable) */
3260  gps->flag &= ~GP_STROKE_CYCLIC;
3261  break;
3263  /* Just toggle flag... */
3264  gps->flag ^= GP_STROKE_CYCLIC;
3265  break;
3266  default:
3267  BLI_assert(0);
3268  break;
3269  }
3270 
3271  if (before != (gps->flag & GP_STROKE_CYCLIC)) {
3272  /* Create new geometry. */
3273  if (is_curve_edit) {
3277  }
3278  else if ((gps->flag & GP_STROKE_CYCLIC) && geometry) {
3281  }
3282 
3283  changed = true;
3284  }
3285  }
3286 
3287  /* if not multiedit, exit loop*/
3288  if (!is_multiedit) {
3289  break;
3290  }
3291  }
3292  }
3293  }
3294  CTX_DATA_END;
3295 
3296  if (changed) {
3297  /* notifiers */
3300  }
3301 
3302  return OPERATOR_FINISHED;
3303 }
3304 
3306  wmOperator *UNUSED(op),
3307  const PropertyRNA *prop)
3308 {
3310  if (gpd != NULL && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
3311  const char *prop_id = RNA_property_identifier(prop);
3312  /* Only show type in curve edit mode */
3313  if (!STREQ(prop_id, "type")) {
3314  return false;
3315  }
3316  }
3317 
3318  return true;
3319 }
3320 
3326 {
3327  PropertyRNA *prop;
3328 
3329  static const EnumPropertyItem cyclic_type[] = {
3330  {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close All", ""},
3331  {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open All", ""},
3332  {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
3333  {0, NULL, 0, NULL, NULL},
3334  };
3335 
3336  /* identifiers */
3337  ot->name = "Set Cyclical State";
3338  ot->idname = "GPENCIL_OT_stroke_cyclical_set";
3339  ot->description = "Close or open the selected stroke adding an edge from last to first point";
3340 
3341  /* api callbacks */
3345 
3346  /* flags */
3348 
3349  /* properties */
3350  ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
3351  prop = RNA_def_boolean(
3352  ot->srna, "geometry", false, "Create Geometry", "Create new geometry for closing stroke");
3354 }
3355 
3358 /* -------------------------------------------------------------------- */
3362 enum {
3367 };
3368 
3370 {
3373  const int type = RNA_enum_get(op->ptr, "type");
3374 
3375  /* sanity checks */
3376  if (ELEM(NULL, gpd)) {
3377  return OPERATOR_CANCELLED;
3378  }
3379 
3380  bool changed = false;
3381  /* loop all selected strokes */
3382  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3383  if (gpl->actframe == NULL) {
3384  continue;
3385  }
3386 
3387  for (bGPDstroke *gps = gpl->actframe->strokes.last; gps; gps = gps->prev) {
3388  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
3389 
3390  /* skip strokes that are not selected or invalid for current view */
3391  if (((gps->flag & GP_STROKE_SELECT) == 0) || (ED_gpencil_stroke_can_use(C, gps) == false)) {
3392  continue;
3393  }
3394  /* skip hidden or locked colors */
3395  if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
3396  (gp_style->flag & GP_MATERIAL_LOCKED)) {
3397  continue;
3398  }
3399 
3400  short prev_first = gps->caps[0];
3401  short prev_last = gps->caps[1];
3402 
3404  ++gps->caps[0];
3405  if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
3406  gps->caps[0] = GP_STROKE_CAP_ROUND;
3407  }
3408  }
3410  ++gps->caps[1];
3411  if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
3412  gps->caps[1] = GP_STROKE_CAP_ROUND;
3413  }
3414  }
3416  gps->caps[0] = GP_STROKE_CAP_ROUND;
3417  gps->caps[1] = GP_STROKE_CAP_ROUND;
3418  }
3419 
3420  if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) {
3421  changed = true;
3422  }
3423  }
3424  }
3425  CTX_DATA_END;
3426 
3427  if (changed) {
3428  /* notifiers */
3431  }
3432 
3433  return OPERATOR_FINISHED;
3434 }
3435 
3440 {
3441  static const EnumPropertyItem toggle_type[] = {
3442  {GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
3443  {GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
3444  {GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
3445  {GP_STROKE_CAPS_TOGGLE_DEFAULT, "TOGGLE", 0, "Default", "Set as default rounded"},
3446  {0, NULL, 0, NULL, NULL},
3447  };
3448 
3449  /* identifiers */
3450  ot->name = "Set Caps Mode";
3451  ot->idname = "GPENCIL_OT_stroke_caps_set";
3452  ot->description = "Change stroke caps mode (rounded or flat)";
3453 
3454  /* api callbacks */
3457 
3458  /* flags */
3460 
3461  /* properties */
3462  ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", "");
3463 }
3464 
3467 /* -------------------------------------------------------------------- */
3471 typedef struct tJoinStrokes {
3474  bool used;
3476 
3478  const bGPDstroke *gps,
3479  const int totstrokes)
3480 {
3481  int index = -1;
3482  float min_dist = FLT_MAX;
3483  float dist, start_a[3], end_a[3], start_b[3], end_b[3];
3484 
3485  bGPDspoint *pt = &gps->points[0];
3486  copy_v3_v3(start_a, &pt->x);
3487 
3488  pt = &gps->points[gps->totpoints - 1];
3489  copy_v3_v3(end_a, &pt->x);
3490 
3491  for (int i = 0; i < totstrokes; i++) {
3492  tJoinStrokes *elem = &strokes_list[i];
3493  if (elem->used) {
3494  continue;
3495  }
3496  pt = &elem->gps->points[0];
3497  copy_v3_v3(start_b, &pt->x);
3498 
3499  pt = &elem->gps->points[elem->gps->totpoints - 1];
3500  copy_v3_v3(end_b, &pt->x);
3501 
3502  dist = len_squared_v3v3(start_a, start_b);
3503  if (dist < min_dist) {
3504  min_dist = dist;
3505  index = i;
3506  }
3507  dist = len_squared_v3v3(start_a, end_b);
3508  if (dist < min_dist) {
3509  min_dist = dist;
3510  index = i;
3511  }
3512  dist = len_squared_v3v3(end_a, start_b);
3513  if (dist < min_dist) {
3514  min_dist = dist;
3515  index = i;
3516  }
3517  dist = len_squared_v3v3(end_a, end_b);
3518  if (dist < min_dist) {
3519  min_dist = dist;
3520  index = i;
3521  }
3522  }
3523 
3524  return index;
3525 }
3526 
3528 {
3530  bGPDlayer *activegpl = BKE_gpencil_layer_active_get(gpd);
3532  /* Limit the number of strokes to join. It makes no sense to allow an very high number of strokes
3533  * for CPU time and because to have a stroke with thousands of points is unpractical, so limit
3534  * this number avoid to joining a full frame scene in one single stroke. */
3535  const int max_join_strokes = 128;
3536 
3537  const int type = RNA_enum_get(op->ptr, "type");
3538  const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
3539 
3540  /* sanity checks */
3541  if (ELEM(NULL, gpd)) {
3542  return OPERATOR_CANCELLED;
3543  }
3544 
3545  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3546  if (is_curve_edit) {
3547  return OPERATOR_CANCELLED;
3548  }
3549 
3550  if (activegpl->flag & GP_LAYER_LOCKED) {
3551  return OPERATOR_CANCELLED;
3552  }
3553 
3555 
3556  int tot_strokes = 0;
3558  tJoinStrokes *strokes_list = MEM_malloc_arrayN(sizeof(tJoinStrokes), max_join_strokes, __func__);
3559  tJoinStrokes *elem = NULL;
3560  /* Read all selected strokes to create a list. */
3561  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3562  bGPDframe *gpf = gpl->actframe;
3563  if (gpf == NULL) {
3564  continue;
3565  }
3566 
3567  /* Add all stroke selected of the frame. */
3568  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3569  if (gps->flag & GP_STROKE_SELECT) {
3570  /* skip strokes that are invalid for current view */
3571  if (ED_gpencil_stroke_can_use(C, gps) == false) {
3572  continue;
3573  }
3574  /* check if the color is editable. */
3575  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
3576  continue;
3577  }
3578  elem = &strokes_list[tot_strokes];
3579  elem->gpf = gpf;
3580  elem->gps = gps;
3581  elem->used = false;
3582 
3583  tot_strokes++;
3584  /* Limit the number of strokes. */
3585  if (tot_strokes == max_join_strokes) {
3586  BKE_reportf(op->reports,
3587  RPT_WARNING,
3588  "Too many strokes selected, only joined first %d strokes",
3589  max_join_strokes);
3590  break;
3591  }
3592  }
3593  }
3594  }
3595  CTX_DATA_END;
3596 
3597  /* Nothing to join. */
3598  if (tot_strokes < 2) {
3599  MEM_SAFE_FREE(strokes_list);
3600  return OPERATOR_CANCELLED;
3601  }
3602 
3603  /* Take first stroke. */
3604  elem = &strokes_list[0];
3605  elem->used = true;
3606 
3607  /* Create a new stroke. */
3608  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(elem->gps, true, true);
3609  gps_new->flag &= ~GP_STROKE_CYCLIC;
3610  BLI_insertlinkbefore(&elem->gpf->strokes, elem->gps, gps_new);
3611 
3612  /* Join all strokes until the list is completed. */
3613  while (true) {
3614  int i = gpencil_get_nearest_stroke_index(strokes_list, gps_new, tot_strokes);
3615  if (i < 0) {
3616  break;
3617  }
3618  elem = &strokes_list[i];
3619  /* Join new_stroke and stroke B. */
3620  BKE_gpencil_stroke_join(gps_new, elem->gps, leave_gaps, true);
3621  elem->used = true;
3622  }
3623 
3624  /* Calc geometry data for new stroke. */
3625  BKE_gpencil_stroke_geometry_update(gpd, gps_new);
3626 
3627  /* If join only, delete old strokes. */
3628  if (type == GP_STROKE_JOIN) {
3629  for (int i = 0; i < tot_strokes; i++) {
3630  elem = &strokes_list[i];
3631  BLI_remlink(&elem->gpf->strokes, elem->gps);
3633  }
3634  }
3635 
3636  /* Free memory. */
3637  MEM_SAFE_FREE(strokes_list);
3638 
3639  /* notifiers */
3642 
3643  return OPERATOR_FINISHED;
3644 }
3645 
3647 {
3648  static const EnumPropertyItem join_type[] = {
3649  {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
3650  {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
3651  {0, NULL, 0, NULL, NULL},
3652  };
3653 
3654  /* identifiers */
3655  ot->name = "Join Strokes";
3656  ot->idname = "GPENCIL_OT_stroke_join";
3657  ot->description = "Join selected strokes (optionally as new stroke)";
3658 
3659  /* api callbacks */
3662 
3663  /* flags */
3665 
3666  /* properties */
3667  ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
3669  "leave_gaps",
3670  false,
3671  "Leave Gaps",
3672  "Leave gaps between joined strokes instead of linking them");
3673 }
3674 
3677 /* -------------------------------------------------------------------- */
3682 {
3685 
3686  /* sanity checks */
3687  if (ELEM(NULL, gpd)) {
3688  return OPERATOR_CANCELLED;
3689  }
3690 
3691  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3692  bool changed = false;
3693  /* read all selected strokes */
3694  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3695  bGPDframe *gpf = gpl->actframe;
3696  if (gpf == NULL) {
3697  continue;
3698  }
3699 
3700  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3701  if (gps->flag & GP_STROKE_SELECT) {
3702  /* skip strokes that are invalid for current view */
3703  if (ED_gpencil_stroke_can_use(C, gps) == false) {
3704  continue;
3705  }
3706  /* check if the color is editable */
3707  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
3708  continue;
3709  }
3710 
3711  if (is_curve_edit) {
3712  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
3713  }
3714  else {
3715  /* Flip stroke. */
3717  }
3718 
3719  changed = true;
3720  }
3721  }
3722  }
3723  CTX_DATA_END;
3724 
3725  if (changed) {
3726  /* notifiers */
3729  }
3730 
3731  return OPERATOR_FINISHED;
3732 }
3733 
3735 {
3736  /* identifiers */
3737  ot->name = "Flip Stroke";
3738  ot->idname = "GPENCIL_OT_stroke_flip";
3739  ot->description = "Change direction of the points of the selected strokes";
3740 
3741  /* api callbacks */
3744 
3745  /* flags */
3747 }
3748 
3751 /* -------------------------------------------------------------------- */
3756 {
3760  ARegion *region = CTX_wm_region(C);
3761  int oldframe = (int)DEG_get_ctime(depsgraph);
3762  const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
3763  const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
3764  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3765 
3766  /* Init snap context for geometry projection. */
3767  SnapObjectContext *sctx = NULL;
3769 
3770  bool changed = false;
3771  /* Init space conversion stuff. */
3772  GP_SpaceConversion gsc = {NULL};
3774  int cfra_prv = INT_MIN;
3775 
3776  /* Go through each editable + selected stroke, adjusting each of its points one by one... */
3777  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
3778  bool curve_select = false;
3779  if (is_curve_edit && gps->editcurve != NULL) {
3780  curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
3781  }
3782 
3783  if (gps->flag & GP_STROKE_SELECT || curve_select) {
3784 
3785  /* update frame to get the new location of objects */
3786  if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf_->framenum)) {
3787  cfra_prv = gpf_->framenum;
3788  CFRA = gpf_->framenum;
3790  }
3791 
3792  ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf_, gps, mode, keep_original);
3793 
3794  if (is_curve_edit && gps->editcurve != NULL) {
3795  BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
3796  /* Update the selection from the stroke to the curve. */
3797  BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
3798 
3799  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
3801  }
3802 
3803  changed = true;
3804  }
3805  }
3806  GP_EDITABLE_STROKES_END(gpstroke_iter);
3807 
3808  /* return frame state and DB to original state */
3809  CFRA = oldframe;
3811 
3812  if (sctx != NULL) {
3814  }
3815 
3816  if (changed) {
3817  /* update changed data */
3820  }
3821 
3822  return OPERATOR_FINISHED;
3823 }
3824 
3826 {
3827  static const EnumPropertyItem reproject_type[] = {
3828  {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
3829  {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
3830  {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
3832  "VIEW",
3833  0,
3834  "View",
3835  "Reproject the strokes to end up on the same plane, as if drawn from the current viewpoint "
3836  "using 'Cursor' Stroke Placement"},
3838  "SURFACE",
3839  0,
3840  "Surface",
3841  "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"},
3843  "CURSOR",
3844  0,
3845  "Cursor",
3846  "Reproject the strokes using the orientation of 3D cursor"},
3847  {0, NULL, 0, NULL, NULL},
3848  };
3849 
3850  /* identifiers */
3851  ot->name = "Reproject Strokes";
3852  ot->idname = "GPENCIL_OT_reproject";
3853  ot->description =
3854  "Reproject the selected strokes from the current viewpoint as if they had been newly drawn "
3855  "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
3856  "or for matching deforming geometry)";
3857 
3858  /* callbacks */
3862 
3863  /* flags */
3865 
3866  /* properties */
3867  ot->prop = RNA_def_enum(
3868  ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
3869 
3871  ot->srna,
3872  "keep_original",
3873  0,
3874  "Keep Original",
3875  "Keep original strokes and create a copy before reprojecting instead of reproject them");
3876 }
3877 
3879 {
3881  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
3882  return OPERATOR_CANCELLED;
3883  }
3884 
3885  bGPdata *gpd = (bGPdata *)ob->data;
3886  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
3887  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
3888  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3890  }
3891  }
3892  }
3893 
3894  /* update changed data */
3897  return OPERATOR_FINISHED;
3898 }
3899 
3901 {
3902  /* identifiers */
3903  ot->name = "Recalculate internal geometry";
3904  ot->idname = "GPENCIL_OT_recalc_geometry";
3905  ot->description = "Update all internal geometry data";
3906 
3907  /* callbacks */
3910 
3911  /* flags */
3913 }
3914 
3917 /* -------------------------------------------------------------------- */
3921 /* helper to smooth */
3923 {
3924  const int repeat = RNA_int_get(op->ptr, "repeat");
3925  float factor = RNA_float_get(op->ptr, "factor");
3926  const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
3927  const bool smooth_position = RNA_boolean_get(op->ptr, "smooth_position");
3928  const bool smooth_thickness = RNA_boolean_get(op->ptr, "smooth_thickness");
3929  const bool smooth_strength = RNA_boolean_get(op->ptr, "smooth_strength");
3930  const bool smooth_uv = RNA_boolean_get(op->ptr, "smooth_uv");
3931 
3932  if (factor == 0.0f) {
3933  return;
3934  }
3935 
3936  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
3937  if (gps->flag & GP_STROKE_SELECT) {
3938  for (int r = 0; r < repeat; r++) {
3939  for (int i = 0; i < gps->totpoints; i++) {
3940  bGPDspoint *pt = &gps->points[i];
3941  if ((only_selected) && ((pt->flag & GP_SPOINT_SELECT) == 0)) {
3942  continue;
3943  }
3944 
3945  /* perform smoothing */
3946  if (smooth_position) {
3947  BKE_gpencil_stroke_smooth(gps, i, factor);
3948  }
3949  if (smooth_strength) {
3950  BKE_gpencil_stroke_smooth_strength(gps, i, factor);
3951  }
3952  if (smooth_thickness) {
3953  /* thickness need to repeat process several times */
3954  for (int r2 = 0; r2 < 20; r2++) {
3955  BKE_gpencil_stroke_smooth_thickness(gps, i, factor);
3956  }
3957  }
3958  if (smooth_uv) {
3959  BKE_gpencil_stroke_smooth_uv(gps, i, factor);
3960  }
3961  }
3962  }
3963  }
3964  }
3965  GP_EDITABLE_STROKES_END(gpstroke_iter);
3966 }
3967 
3968 /* helper: Count how many points need to be inserted */
3970 {
3971  bGPDspoint *pt;
3972  int i;
3973  int totnewpoints = 0;
3974  for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
3975  if (pt->flag & GP_SPOINT_SELECT) {
3976  if (i + 1 < gps->totpoints) {
3977  if (gps->points[i + 1].flag & GP_SPOINT_SELECT) {
3978  totnewpoints++;
3979  }
3980  }
3981  }
3982  }
3983 
3984  if ((gps->flag & GP_STROKE_CYCLIC) && (gps->points[0].flag & GP_SPOINT_SELECT) &&
3985  (gps->points[gps->totpoints - 1].flag & GP_SPOINT_SELECT)) {
3986  totnewpoints++;
3987  }
3988 
3989  return totnewpoints;
3990 }
3991 
3992 static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts)
3993 {
3994  bGPDspoint *temp_points;
3995  int totnewpoints, oldtotpoints;
3996  int i2;
3997  /* loop as many times as cuts */
3998  for (int s = 0; s < cuts; s++) {
3999  totnewpoints = gpencil_count_subdivision_cuts(gps);
4000  if (totnewpoints == 0) {
4001  continue;
4002  }
4003  /* duplicate points in a temp area */
4004  temp_points = MEM_dupallocN(gps->points);
4005  oldtotpoints = gps->totpoints;
4006 
4007  MDeformVert *temp_dverts = NULL;
4008  MDeformVert *dvert_final = NULL;
4009  MDeformVert *dvert = NULL;
4010  MDeformVert *dvert_next = NULL;
4011  if (gps->dvert != NULL) {
4012  temp_dverts = MEM_dupallocN(gps->dvert);
4013  }
4014 
4015  /* resize the points arrays */
4016  gps->totpoints += totnewpoints;
4017  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
4018  if (gps->dvert != NULL) {
4019  gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
4020  }
4021 
4022  /* loop and interpolate */
4023  i2 = 0;
4024  for (int i = 0; i < oldtotpoints; i++) {
4025  bGPDspoint *pt = &temp_points[i];
4026  bGPDspoint *pt_final = &gps->points[i2];
4027 
4028  /* copy current point */
4029  copy_v3_v3(&pt_final->x, &pt->x);
4030  pt_final->pressure = pt->pressure;
4031  pt_final->strength = pt->strength;
4032  pt_final->time = pt->time;
4033  pt_final->flag = pt->flag;
4034  copy_v4_v4(pt_final->vert_color, pt->vert_color);
4035 
4036  if (gps->dvert != NULL) {
4037  dvert = &temp_dverts[i];
4038  dvert_final = &gps->dvert[i2];
4039  dvert_final->totweight = dvert->totweight;
4040  dvert_final->dw = dvert->dw;
4041  }
4042  i2++;
4043 
4044  /* if next point is selected add a half way point */
4045  if (pt->flag & GP_SPOINT_SELECT) {
4046  if (i + 1 < oldtotpoints) {
4047  if (temp_points[i + 1].flag & GP_SPOINT_SELECT) {
4048  pt_final = &gps->points[i2];
4049  if (gps->dvert != NULL) {
4050  dvert_final = &gps->dvert[i2];
4051  }
4052  /* Interpolate all values */
4053  bGPDspoint *next = &temp_points[i + 1];
4054  interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
4055  pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
4056  pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
4057  CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
4058  interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
4059  pt_final->time = interpf(pt->time, next->time, 0.5f);
4060  pt_final->flag |= GP_SPOINT_SELECT;
4061 
4062  /* interpolate weights */
4063  if (gps->dvert != NULL) {
4064  dvert = &temp_dverts[i];
4065  dvert_next = &temp_dverts[i + 1];
4066  dvert_final = &gps->dvert[i2];
4067 
4068  dvert_final->totweight = dvert->totweight;
4069  dvert_final->dw = MEM_dupallocN(dvert->dw);
4070 
4071  /* interpolate weight values */
4072  for (int d = 0; d < dvert->totweight; d++) {
4073  MDeformWeight *dw_a = &dvert->dw[d];
4074  if (dvert_next->totweight > d) {
4075  MDeformWeight *dw_b = &dvert_next->dw[d];
4076  MDeformWeight *dw_final = &dvert_final->dw[d];
4077  dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
4078  }
4079  }
4080  }
4081 
4082  i2++;
4083  }
4084  }
4085  }
4086  }
4087 
4088  /* Subdivide between last and first point. */
4089  if (gps->flag & GP_STROKE_CYCLIC) {
4090  bGPDspoint *pt = &temp_points[oldtotpoints - 1];
4091  bGPDspoint *next = &temp_points[0];
4092  if ((pt->flag & GP_SPOINT_SELECT) && (next->flag & GP_SPOINT_SELECT)) {
4093  bGPDspoint *pt_final = &gps->points[i2];
4094  if (gps->dvert != NULL) {
4095  dvert_final = &gps->dvert[i2];
4096  }
4097  /* Interpolate all values */
4098  interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
4099  pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
4100  pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
4101  CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
4102  interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
4103  pt_final->time = interpf(pt->time, next->time, 0.5f);
4104  pt_final->flag |= GP_SPOINT_SELECT;
4105 
4106  /* interpolate weights */
4107  if (gps->dvert != NULL) {
4108  dvert = &temp_dverts[oldtotpoints - 1];
4109  dvert_next = &temp_dverts[0];
4110  dvert_final = &gps->dvert[i2];
4111 
4112  dvert_final->totweight = dvert->totweight;
4113  dvert_final->dw = MEM_dupallocN(dvert->dw);
4114 
4115  /* interpolate weight values */
4116  for (int d = 0; d < dvert->totweight; d++) {
4117  MDeformWeight *dw_a = &dvert->dw[d];
4118  if (dvert_next->totweight > d) {
4119  MDeformWeight *dw_b = &dvert_next->dw[d];
4120  MDeformWeight *dw_final = &dvert_final->dw[d];
4121  dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
4122  }
4123  }
4124  }
4125  }
4126  }
4127 
4128  /* free temp memory */
4129  MEM_SAFE_FREE(temp_points);
4130  MEM_SAFE_FREE(temp_dverts);
4131  }
4132 }
4133 
4135 {
4137  const int cuts = RNA_int_get(op->ptr, "number_cuts");
4138 
4139  /* sanity checks */
4140  if (ELEM(NULL, gpd)) {
4141  return OPERATOR_CANCELLED;
4142  }
4143 
4144  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4145 
4146  bool changed = false;
4147  if (is_curve_edit) {
4148  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
4149  {
4150  if (gpc->flag & GP_CURVE_SELECT) {
4153  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
4155  changed = true;
4156  }
4157  }
4158  GP_EDITABLE_CURVES_END(gps_iter);
4159  }
4160  else {
4161  /* Go through each editable + selected stroke */
4162  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4163  if (gps->flag & GP_STROKE_SELECT) {
4164  gpencil_stroke_subdivide(gps, cuts);
4165  /* Calc geometry data. */
4167  changed = true;
4168  }
4169  }
4170  GP_EDITABLE_STROKES_END(gpstroke_iter);
4171 
4172  if (changed) {
4173  /* smooth stroke */
4174  gpencil_smooth_stroke(C, op);
4175  }
4176  }
4177 
4178  if (changed) {
4179  /* notifiers */
4182  }
4183 
4184  return OPERATOR_FINISHED;
4185 }
4186 
4188  wmOperator *UNUSED(op),
4189  const PropertyRNA *prop)
4190 {
4192  if (gpd != NULL && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
4193  const char *prop_id = RNA_property_identifier(prop);
4194  /* Only show number_cuts in curve edit mode */
4195  if (!STREQ(prop_id, "number_cuts")) {
4196  return false;
4197  }
4198  }
4199 
4200  return true;
4201 }
4202 
4204 {
4205  PropertyRNA *prop;
4206 
4207  /* identifiers */
4208  ot->name = "Subdivide Stroke";
4209  ot->idname = "GPENCIL_OT_stroke_subdivide";
4210  ot->description =
4211  "Subdivide between continuous selected points of the stroke adding a point half way between "
4212  "them";
4213 
4214  /* api callbacks */
4218 
4219  /* flags */
4221 
4222  /* properties */
4223  prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5);
4224  /* avoid re-using last var because it can cause _very_ high value and annoy users */
4226 
4227  /* Smooth parameters */
4228  RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 2.0f, "Smooth", "", 0.0f, 2.0f);
4229  prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5);
4232  "only_selected",
4233  true,
4234  "Selected Points",
4235  "Smooth only selected points in the stroke");
4236  RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
4237  RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", "");
4238  RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
4239  RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
4240 }
4241 
4242 /* ** simplify stroke *** */
4244 {
4246  float factor = RNA_float_get(op->ptr, "factor");
4247 
4248  /* sanity checks */
4249  if (ELEM(NULL, gpd)) {
4250  return OPERATOR_CANCELLED;
4251  }
4252 
4253  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4254 
4255  bool changed = false;
4256  if (is_curve_edit) {
4257  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4258  }
4259  else {
4260  /* Go through each editable + selected stroke */
4261  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4262  if (gps->flag & GP_STROKE_SELECT) {
4263  /* simplify stroke using Ramer-Douglas-Peucker algorithm */
4264  BKE_gpencil_stroke_simplify_adaptive(gpd, gps, factor);
4265  changed = true;
4266  }
4267  }
4268  GP_EDITABLE_STROKES_END(gpstroke_iter);
4269  }
4270 
4271  if (changed) {
4272  /* notifiers */
4275  }
4276 
4277  return OPERATOR_FINISHED;
4278 }
4279 
4281 {
4282  PropertyRNA *prop;
4283 
4284  /* identifiers */
4285  ot->name = "Simplify Stroke";
4286  ot->idname = "GPENCIL_OT_stroke_simplify";
4287  ot->description = "Simplify selected stroked reducing number of points";
4288 
4289  /* api callbacks */
4292 
4293  /* flags */
4295 
4296  /* properties */
4297  prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f);
4298  /* avoid re-using last var */
4300 }
4301 
4302 /* ** simplify stroke using fixed algorithm *** */
4304 {
4306  int steps = RNA_int_get(op->ptr, "step");
4307 
4308  /* sanity checks */
4309  if (ELEM(NULL, gpd)) {
4310  return OPERATOR_CANCELLED;
4311  }
4312 
4313  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4314 
4315  bool changed = false;
4316  if (is_curve_edit) {
4317  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4318  }
4319  else {
4320  /* Go through each editable + selected stroke */
4321  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4322  if (gps->flag & GP_STROKE_SELECT) {
4323  changed |= true;
4324  for (int i = 0; i < steps; i++) {
4326  }
4327  }
4328  }
4329  GP_EDITABLE_STROKES_END(gpstroke_iter);
4330  }
4331 
4332  if (changed) {
4333  /* notifiers */
4336  }
4337 
4338  return OPERATOR_FINISHED;
4339 }
4340 
4342 {
4343  PropertyRNA *prop;
4344 
4345  /* identifiers */
4346  ot->name = "Simplify Fixed Stroke";
4347  ot->idname = "GPENCIL_OT_stroke_simplify_fixed";
4348  ot->description = "Simplify selected stroked reducing number of points using fixed algorithm";
4349 
4350  /* api callbacks */
4353 
4354  /* flags */
4356 
4357  /* properties */
4358  prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Steps", "Number of simplify steps", 1, 10);
4359 
4360  /* avoid re-using last var */
4362 }
4363 
4364 /* ** Resample stroke *** */
4366 {
4368  const float length = RNA_float_get(op->ptr, "length");
4369 
4370  /* sanity checks */
4371  if (ELEM(NULL, gpd)) {
4372  return OPERATOR_CANCELLED;
4373  }
4374 
4375  /* Go through each editable + selected stroke */
4376  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4377  if (gps->flag & GP_STROKE_SELECT) {
4378  BKE_gpencil_stroke_sample(gpd, gps, length, true);
4379  }
4380  }
4381  GP_EDITABLE_STROKES_END(gpstroke_iter);
4382 
4383  /* notifiers */
4386 
4387  return OPERATOR_FINISHED;
4388 }
4389 
4391 {
4392  PropertyRNA *prop;
4393 
4394  /* identifiers */
4395  ot->name = "Sample Stroke";
4396  ot->idname = "GPENCIL_OT_stroke_sample";
4397  ot->description = "Sample stroke points to predefined segment length";
4398 
4399  /* api callbacks */
4402 
4403  /* flags */
4405 
4406  /* properties */
4407  prop = RNA_def_float(ot->srna, "length", 0.1f, 0.0f, 100.0f, "Length", "", 0.0f, 100.0f);
4408  /* avoid re-using last var */
4410 }
4411 
4414 /* -------------------------------------------------------------------- */
4419 {
4421 
4422  /* sanity checks */
4423  if (ELEM(NULL, gpd)) {
4424  return OPERATOR_CANCELLED;
4425  }
4426 
4427  /* Go through each editable + selected stroke */
4428  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
4429  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4430 
4431  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
4432  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
4433 
4434  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
4435  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
4436 
4437  if (gpf == NULL) {
4438  continue;
4439  }
4440 
4441  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
4442 
4443  /* skip strokes that are invalid for current view */
4444  if (ED_gpencil_stroke_can_use(C, gps) == false) {
4445  continue;
4446  }
4447 
4448  if (gps->flag & GP_STROKE_SELECT) {
4449  if (is_curve_edit) {
4450  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4451  }
4452  else {
4453  BKE_gpencil_stroke_trim(gpd, gps);
4454  }
4455  }
4456  }
4457  /* if not multiedit, exit loop*/
4458  if (!is_multiedit) {
4459  break;
4460  }
4461  }
4462  }
4463  }
4464  CTX_DATA_END;
4465 
4466  /* notifiers */
4469 
4470  return OPERATOR_FINISHED;
4471 }
4472 
4474 {
4475  /* identifiers */
4476  ot->name = "Trim Stroke";
4477  ot->idname = "GPENCIL_OT_stroke_trim";
4478  ot->description = "Trim selected stroke to first loop or intersection";
4479 
4480  /* api callbacks */
4483 
4484  /* flags */
4486 }
4487 
4490 /* -------------------------------------------------------------------- */
4494 typedef enum eGP_SeparateModes {
4495  /* Points */
4497  /* Selected Strokes */
4499  /* Current Layer */
4502 
4504 {
4505  Base *base_new;
4506  Main *bmain = CTX_data_main(C);
4508  ViewLayer *view_layer = CTX_data_view_layer(C);
4509  Base *base_prev = CTX_data_active_base(C);
4510  bGPdata *gpd_src = ED_gpencil_data_get_active(C);
4512 
4513  Object *ob_dst = NULL;
4514  bGPdata *gpd_dst = NULL;
4515  bGPDlayer *gpl_dst = NULL;
4516  bGPDframe *gpf_dst = NULL;
4517  bGPDspoint *pt;
4518  Material *ma = NULL;
4519  int i, idx;
4520 
4521  eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
4522 
4523  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
4524  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
4525 
4526  /* sanity checks */
4527  if (ELEM(NULL, gpd_src)) {
4528  return OPERATOR_CANCELLED;
4529  }
4530 
4531  if ((mode == GP_SEPARATE_LAYER) && (BLI_listbase_is_single(&gpd_src->layers))) {
4532  BKE_report(op->reports, RPT_ERROR, "Cannot separate an object with one layer only");
4533  return OPERATOR_CANCELLED;
4534  }
4535 
4536  /* Cancel if nothing selected. */
4538  bool has_selected = false;
4539  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
4540  if (ED_gpencil_layer_has_selected_stroke(gpl, is_multiedit)) {
4541  has_selected = true;
4542  break;
4543  }
4544  }
4545  CTX_DATA_END;
4546 
4547  if (!has_selected) {
4548  BKE_report(op->reports, RPT_ERROR, "Nothing selected");
4549  return OPERATOR_CANCELLED;
4550  }
4551  }
4552 
4553  /* Create a new object. */
4554  /* Take into account user preferences for duplicating actions. */
4555  const eDupli_ID_Flags dupflag = (U.dupflag & USER_DUP_ACT);
4556 
4557  base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_prev, dupflag);
4558  ob_dst = base_new->object;
4559  ob_dst->mode = OB_MODE_OBJECT;
4560  /* Duplication will increment #bGPdata user-count, but since we create a new grease-pencil
4561  * data-block for ob_dst (which gets its own user automatically),
4562  * we have to decrement the user-count again. */
4563  gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2);
4564  id_us_min(ob_dst->data);
4565  ob_dst->data = (bGPdata *)gpd_dst;
4566 
4567  /* Loop old data-block and separate parts. */
4569  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
4570  gpl_dst = NULL;
4571  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
4572 
4573  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
4574  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
4575 
4576  if (gpf == NULL) {
4577  continue;
4578  }
4579 
4580  gpf_dst = NULL;
4581 
4582  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
4583 
4584  /* skip strokes that are invalid for current view */
4585  if (ED_gpencil_stroke_can_use(C, gps) == false) {
4586  continue;
4587  }
4588  /* check if the color is editable */
4589  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
4590  continue;
4591  }
4592  /* Separate selected strokes. */
4593  if (gps->flag & GP_STROKE_SELECT) {
4594  /* add layer if not created before */
4595  if (gpl_dst == NULL) {
4596  gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false);
4597  }
4598 
4599  /* add frame if not created before */
4600  if (gpf_dst == NULL) {
4601  gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
4602  }
4603 
4604  /* add duplicate materials */
4605 
4606  /* XXX same material can be in multiple slots. */
4607  ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
4608 
4609  idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
4610 
4611  /* selected points mode */
4612  if (mode == GP_SEPARATE_POINT) {
4613  if (is_curve_edit) {
4614  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4615  }
4616  else {
4617  /* make copy of source stroke */
4618  bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
4619 
4620  /* Reassign material. */
4621  gps_dst->mat_nr = idx;
4622 
4623  /* link to destination frame */
4624  BLI_addtail(&gpf_dst->strokes, gps_dst);
4625 
4626  /* Invert selection status of all points in destination stroke */
4627  for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
4628  pt->flag ^= GP_SPOINT_SELECT;
4629  }
4630 
4631  /* delete selected points from destination stroke */
4633  gpd_dst, gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, false, 0);
4634 
4635  /* delete selected points from origin stroke */
4637  gpd_src, gpf, gps, gps->next, GP_SPOINT_SELECT, false, false, 0);
4638  }
4639  }
4640  /* selected strokes mode */
4641  else if (mode == GP_SEPARATE_STROKE) {
4642  /* deselect old stroke */
4643  gps->flag &= ~GP_STROKE_SELECT;
4645  /* unlink from source frame */
4646  BLI_remlink(&gpf->strokes, gps);
4647  gps->prev = gps->next = NULL;
4648  /* relink to destination frame */
4649  BLI_addtail(&gpf_dst->strokes, gps);
4650  /* Reassign material. */
4651  gps->mat_nr = idx;
4652  }
4653  }
4654  }
4655  }
4656 
4657  /* if not multiedit, exit loop*/
4658  if (!is_multiedit) {
4659  break;
4660  }
4661  }
4662  }
4663  CTX_DATA_END;
4664  }
4665  else if (mode == GP_SEPARATE_LAYER) {
4667  if (gpl) {
4668  /* try to set a new active layer in source datablock */
4669  if (gpl->prev) {
4670  BKE_gpencil_layer_active_set(gpd_src, gpl->prev);
4671  }
4672  else if (gpl->next) {
4673  BKE_gpencil_layer_active_set(gpd_src, gpl->next);
4674  }
4675  /* unlink from source datablock */
4676  BLI_remlink(&gpd_src->layers, gpl);
4677  gpl->prev = gpl->next = NULL;
4678  /* relink to destination datablock */
4679  BLI_addtail(&gpd_dst->layers, gpl);
4680 
4681  /* add duplicate materials */
4682  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
4683  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
4684  /* skip strokes that are invalid for current view */
4685  if (ED_gpencil_stroke_can_use(C, gps) == false) {
4686  continue;
4687  }
4688  ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
4689  gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
4690  }
4691  }
4692  }
4693  }
4694 
4695  /* Ensure destination object has one active layer. */
4696  if (gpd_dst->layers.first != NULL) {
4697  if (BKE_gpencil_layer_active_get(gpd_dst) == NULL) {
4698  BKE_gpencil_layer_active_set(gpd_dst, gpd_dst->layers.first);
4699  }
4700  }
4701 
4702  /* Remove unused slots. */
4703  int actcol = ob_dst->actcol;
4704  for (int slot = 1; slot <= ob_dst->totcol; slot++) {
4705  while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst->data, slot)) {
4706  ob_dst->actcol = slot;
4707  BKE_object_material_slot_remove(bmain, ob_dst);
4708  if (actcol >= slot) {
4709  actcol--;
4710  }
4711  }
4712  }
4713  ob_dst->actcol = actcol;
4714 
4717 
4718  DEG_relations_tag_update(bmain);
4723 
4724  return OPERATOR_FINISHED;
4725 }
4726 
4728 {
4729  static const EnumPropertyItem separate_type[] = {
4730  {GP_SEPARATE_POINT, "POINT", 0, "Selected Points", "Separate the selected points"},
4731  {GP_SEPARATE_STROKE, "STROKE", 0, "Selected Strokes", "Separate the selected strokes"},
4732  {GP_SEPARATE_LAYER, "LAYER", 0, "Active Layer", "Separate the strokes of the current layer"},
4733  {0, NULL, 0, NULL, NULL},
4734  };
4735 
4736  /* identifiers */
4737  ot->name = "Separate Strokes";
4738  ot->idname = "GPENCIL_OT_stroke_separate";
4739  ot->description = "Separate the selected strokes or layer in a new grease pencil object";
4740 
4741  /* callbacks */
4745 
4746  /* flags */
4748 
4749  /* properties */
4750  ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", "");
4751 }
4752 
4755 /* -------------------------------------------------------------------- */
4760 {
4763  bGPDspoint *pt;
4764  int i;
4765 
4766  /* sanity checks */
4767  if (ELEM(NULL, gpd)) {
4768  return OPERATOR_CANCELLED;
4769  }
4770  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
4771  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4772 
4773  /* loop strokes and split parts */
4774  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
4775  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
4776 
4777  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
4778  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
4779 
4780  if (gpf == NULL) {
4781  continue;
4782  }
4783 
4784  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
4785 
4786  /* skip strokes that are invalid for current view */
4787  if (ED_gpencil_stroke_can_use(C, gps) == false) {
4788  continue;
4789  }
4790  /* check if the color is editable */
4791  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
4792  continue;
4793  }
4794  /* Split selected strokes. */
4795  if (gps->flag & GP_STROKE_SELECT) {
4796  if (is_curve_edit) {
4797  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4798  }
4799  else {
4800  /* make copy of source stroke */
4801  bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
4802 
4803  /* link to same frame */
4804  BLI_addtail(&gpf->strokes, gps_dst);
4805 
4806  /* invert selection status of all points in destination stroke */
4807  for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
4808  pt->flag ^= GP_SPOINT_SELECT;
4809  }
4810 
4811  /* delete selected points from destination stroke */
4813  gpd, gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, false, 0);
4814 
4815  /* delete selected points from origin stroke */
4817  gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, false, 0);
4818  }
4819  }
4820  }
4821  /* select again tagged points */
4822  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
4823  bGPDspoint *ptn = gps->points;
4824  for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) {
4825  if (ptn->flag & GP_SPOINT_TAG) {
4826  ptn->flag |= GP_SPOINT_SELECT;
4827  ptn->flag &= ~GP_SPOINT_TAG;
4828  }
4829  }
4830  }
4831  }
4832 
4833  /* if not multiedit, exit loop*/
4834  if (!is_multiedit) {
4835  break;
4836  }
4837  }
4838  }
4839  CTX_DATA_END;
4840 
4842 
4844 
4845  return OPERATOR_FINISHED;
4846 }
4847 
4849 {
4850  /* identifiers */
4851  ot->name = "Split Strokes";
4852  ot->idname = "GPENCIL_OT_stroke_split";
4853  ot->description = "Split selected points as new stroke on same frame";
4854 
4855  /* callbacks */
4858 
4859  /* flags */
4861 }
4862 
4865 /* -------------------------------------------------------------------- */
4870 {
4872 
4873  /* sanity checks */
4874  if (ELEM(NULL, gpd)) {
4875  return OPERATOR_CANCELLED;
4876  }
4877 
4878  gpencil_smooth_stroke(C, op);
4879 
4880  /* notifiers */
4883 
4884  return OPERATOR_FINISHED;
4885 }
4886 
4888 {
4889  PropertyRNA *prop;
4890 
4891  /* identifiers */
4892  ot->name = "Smooth Stroke";
4893  ot->idname = "GPENCIL_OT_stroke_smooth";
4894  ot->description = "Smooth selected strokes";
4895 
4896  /* api callbacks */
4899 
4900  /* flags */
4902 
4903  /* properties */
4904  prop = RNA_def_int(ot->srna, "repeat", 1, 1, 50, "Repeat", "", 1, 20);
4906 
4907  RNA_def_float(ot->srna, "factor", 0.5f, 0.0f, 2.0f, "Factor", "", 0.0f, 2.0f);
4909  "only_selected",
4910  true,
4911  "Selected Points",
4912  "Smooth only selected points in the stroke");
4913  RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
4914  RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", "");
4915  RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
4916  RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
4917 }
4918 
4921 /* -------------------------------------------------------------------- */
4925 /* smart stroke cutter for trimming stroke ends */
4928  const int (*mcoords)[2];
4930 };
4931 
4933  bGPDspoint *pt,
4934  const GP_SpaceConversion *gsc,
4935  const float diff_mat[4][4],
4936  void *user_data)
4937 {
4938  const struct GP_SelectLassoUserData *data = user_data;
4939  bGPDspoint pt2;
4940  int x0, y0;
4941  gpencil_point_to_parent_space(pt, diff_mat, &pt2);
4942  gpencil_point_to_xy(gsc, gps, &pt2, &x0, &y0);
4943  /* test if in lasso */
4944  return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) &&
4945  BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX));
4946 }
4947 
4948 typedef bool (*GPencilTestFn)(bGPDstroke *gps,
4949  bGPDspoint *pt,
4950  const GP_SpaceConversion *gsc,
4951  const float diff_mat[4][4],
4952  void *user_data);
4953 
4955  bGPDlayer *hit_layer,
4956  bGPDstroke *hit_stroke,
4957  const bool flat_caps)
4958 {
4959  bGPDspoint *pt = NULL;
4960  bGPDspoint *pt1 = NULL;
4961  int i;
4962 
4963  bGPDstroke *gpsn = hit_stroke->next;
4964 
4965  int totselect = 0;
4966  for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
4967  if (pt->flag & GP_SPOINT_SELECT) {
4968  totselect++;
4969  }
4970  }
4971 
4972  /* if all points selected delete or only 2 points and 1 selected */
4973  if (((totselect == 1) && (hit_stroke->totpoints == 2)) || (hit_stroke->totpoints == totselect)) {
4974  BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
4975  BKE_gpencil_free_stroke(hit_stroke);
4976  hit_stroke = NULL;
4977  }
4978 
4979  /* if very small distance delete */
4980  if ((hit_stroke) && (hit_stroke->totpoints == 2)) {
4981  pt = &hit_stroke->points[0];
4982  pt1 = &hit_stroke->points[1];
4983  if (len_v3v3(&pt->x, &pt1->x) < 0.001f) {
4984  BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
4985  BKE_gpencil_free_stroke(hit_stroke);
4986  hit_stroke = NULL;
4987  }
4988  }
4989 
4990  if (hit_stroke) {
4991  /* tag and dissolve (untag new points) */
4992  for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
4993  if (pt->flag & GP_SPOINT_SELECT) {
4994  pt->flag &= ~GP_SPOINT_SELECT;
4995  pt->flag |= GP_SPOINT_TAG;
4996  }
4997  else if (pt->flag & GP_SPOINT_TAG) {
4998  pt->flag &= ~GP_SPOINT_TAG;
4999  }
5000  }
5001  /* If flat caps mode check extremes. */
5002  if (flat_caps) {
5003  if (hit_stroke->points[0].flag & GP_SPOINT_TAG) {
5004  hit_stroke->caps[0] = GP_STROKE_CAP_FLAT;
5005  }
5006 
5007  if (hit_stroke->points[hit_stroke->totpoints - 1].flag & GP_SPOINT_TAG) {
5008  hit_stroke->caps[1] = GP_STROKE_CAP_FLAT;
5009  }
5010  }
5011 
5013  gpd, hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, flat_caps, 1);
5014  }
5015 }
5016 
5018  wmOperator *op,
5019  GPencilTestFn is_inside_fn,
5020  void *user_data)
5021 {
5023  Object *obact = CTX_data_active_object(C);
5025  ScrArea *area = CTX_wm_area(C);
5027  const float scale = ts->gp_sculpt.isect_threshold;
5028  const bool flat_caps = RNA_boolean_get(op->ptr, "flat_caps");
5029  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
5030 
5031  bGPDspoint *pt;
5032  GP_SpaceConversion gsc = {NULL};
5033 
5034  bool changed = false;
5035 
5036  /* sanity checks */
5037  if (area == NULL) {
5038  BKE_report(op->reports, RPT_ERROR, "No active area");
5039  return OPERATOR_CANCELLED;
5040  }
5041 
5042  /* init space conversion stuff */
5044 
5045  /* Deselect all strokes. */
5046  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
5047  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
5048  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
5049  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
5050  if (gps->flag & GP_STROKE_SELECT) {
5051  int i;
5052  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
5053  pt->flag &= ~GP_SPOINT_SELECT;
5054  }
5055 
5056  gps->flag &= ~GP_STROKE_SELECT;
5058  }
5059  }
5060  /* if not multiedit, exit loop. */
5061  if (!is_multiedit) {
5062  break;
5063  }
5064  }
5065  }
5066 
5067  /* Select points */
5068  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
5069  if ((gpl->flag & GP_LAYER_LOCKED) || ((gpl->flag & GP_LAYER_HIDE))) {
5070  continue;
5071  }
5072 
5073  float diff_mat[4][4];
5074  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
5075 
5076  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
5077  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
5078  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
5079  if (gpf == NULL) {
5080  continue;
5081  }
5082 
5083  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
5084  if (ED_gpencil_stroke_can_use(C, gps) == false) {
5085  continue;
5086  } /* check if the color is editable */
5087  if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
5088  continue;
5089  }
5090  int tot_inside = 0;
5091  const int oldtot = gps->totpoints;
5092  for (int i = 0; i < gps->totpoints; i++) {
5093  pt = &gps->points[i];
5094  if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) {
5095  continue;
5096  }
5097  /* convert point coords to screen-space */
5098  const bool is_inside = is_inside_fn(gps, pt, &gsc, diff_mat, user_data);
5099  if (is_inside) {
5100  tot_inside++;
5101  changed = true;
5102  pt->flag |= GP_SPOINT_SELECT;
5103  gps->flag |= GP_STROKE_SELECT;
5105  float r_hita[3], r_hitb[3];
5106  if (gps->totpoints > 1) {
5108  gpd, gpl, gps, pt, true, true, scale, r_hita, r_hitb);
5109  }
5110  /* avoid infinite loops */
5111  if (gps->totpoints > oldtot) {
5112  break;
5113  }
5114  }
5115  }
5116  /* if mark all points inside lasso set to remove all stroke */
5117  if ((tot_inside == oldtot) || ((tot_inside == 1) && (oldtot == 2))) {
5118  for (int i = 0; i < gps->totpoints; i++) {
5119  pt = &gps->points[i];
5120  pt->flag |= GP_SPOINT_SELECT;
5121  }
5122  }
5123  }
5124  /* if not multiedit, exit loop. */
5125  if (!is_multiedit) {
5126  break;
5127  }
5128  }
5129  }
5130  }
5131 
5132  /* Dissolve selected points. */
5133  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
5134  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
5135  bGPDframe *gpf_act = gpl->actframe;
5136  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
5137  gpl->actframe = gpf;
5138  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
5139  if (gps->flag & GP_STROKE_SELECT) {
5140  gpencil_cutter_dissolve(gpd, gpl, gps, flat_caps);
5141  }
5142  }
5143  /* if not multiedit, exit loop. */
5144  if (!is_multiedit) {
5145  break;
5146  }
5147  }
5148  gpl->actframe = gpf_act;
5149  }
5150 
5151  /* updates */
5152  if (changed) {
5156  }
5157 
5158  return OPERATOR_FINISHED;
5159 }
5160 
5162 {
5164 
5165  if (GPENCIL_PAINT_MODE(gpd)) {
5166  if (gpd->layers.first) {
5167  return true;
5168  }
5169  }
5170 
5171  return false;
5172 }
5173 
5175 {
5176  ScrArea *area = CTX_wm_area(C);
5177  /* sanity checks */
5178  if (area == NULL) {
5179  BKE_report(op->reports, RPT_ERROR, "No active area");
5180  return OPERATOR_CANCELLED;
5181  }
5182 
5183  struct GP_SelectLassoUserData data = {0};
5184  data.mcoords = WM_gesture_lasso_path_to_array(C, op, &data.mcoords_len);
5185 
5186  /* Sanity check. */
5187  if (data.mcoords == NULL) {
5188  return OPERATOR_PASS_THROUGH;
5189  }
5190 
5191  /* Compute boundbox of lasso (for faster testing later). */
5192  BLI_lasso_boundbox(&data.rect, data.mcoords, data.mcoords_len);
5193 
5195 
5196  MEM_freeN((void *)data.mcoords);
5197 
5198  return OPERATOR_FINISHED;
5199 }
5200 
5202 {
5203  /* identifiers */
5204  ot->name = "Stroke Cutter";
5205  ot->description = "Select section and cut";
5206  ot->idname = "GPENCIL_OT_stroke_cutter";
5207 
5208  /* callbacks */
5214 
5215  /* flag */
5216  ot->flag = OPTYPE_UNDO;
5217 
5218  /* properties */
5220 
5221  RNA_def_boolean(ot->srna, "flat_caps", 0, "Flat Caps", "");
5222 }
5223 
5224 bool ED_object_gpencil_exit(struct Main *bmain, Object *ob)
5225 {
5226  bool ok = false;
5227  if (ob) {
5228  bGPdata *gpd = (bGPdata *)ob->data;
5229 
5232 
5233  ob->restore_mode = ob->mode;
5236 
5237  /* Inform all CoW versions that we changed the mode. */
5239  ok = true;
5240  }
5241  return ok;
5242 }
5243 
5246 /* -------------------------------------------------------------------- */
5251 {
5253  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
5254  return false;
5255  }
5256  bGPdata *gpd = (bGPdata *)ob->data;
5257  if (gpd == NULL) {
5258  return false;
5259  }
5260 
5262 
5263  return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
5264 }
5265 
5267 {
5269  bGPdata *gpd = (bGPdata *)ob->data;
5270  const float threshold = RNA_float_get(op->ptr, "threshold");
5271  const bool unselected = RNA_boolean_get(op->ptr, "use_unselected");
5272 
5273  /* sanity checks */
5274  if (ELEM(NULL, gpd)) {
5275  return OPERATOR_CANCELLED;
5276  }
5277 
5278  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
5279 
5280  if (is_curve_edit) {
5281  /* TODO: merge curve points by distance */
5282  }
5283  else {
5284  /* Go through each editable selected stroke */
5285  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
5286  if (gps->flag & GP_STROKE_SELECT) {
5287  BKE_gpencil_stroke_merge_distance(gpd, gpf_, gps, threshold, unselected);
5288  }
5289  }
5290  GP_EDITABLE_STROKES_END(gpstroke_iter);
5291  }
5292 
5293  /* notifiers */
5296 
5297  return OPERATOR_FINISHED;
5298 }
5299 
5301 {
5302  PropertyRNA *prop;
5303 
5304  /* identifiers */
5305  ot->name = "Merge by Distance";
5306  ot->idname = "GPENCIL_OT_stroke_merge_by_distance";
5307  ot->description = "Merge points by distance";
5308 
5309  /* api callbacks */
5312 
5313  /* flags */
5315 
5316  /* properties */
5317  prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, 100.0f, "Threshold", "", 0.0f, 100.0f);
5318  /* avoid re-using last var */
5320 
5321  prop = RNA_def_boolean(
5322  ot->srna, "use_unselected", 0, "Unselected", "Use whole stroke, not only selected points");
5324 }
void BKE_brush_gpencil_paint_presets(struct Main *bmain, struct ToolSettings *ts, const bool reset)
Definition: brush.c:1308
void BKE_brush_gpencil_sculpt_presets(struct Main *bmain, struct ToolSettings *ts, const bool reset)
Definition: brush.c:1459
void BKE_brush_gpencil_weight_presets(struct Main *bmain, struct ToolSettings *ts, const bool reset)
Definition: brush.c:1534
void BKE_brush_gpencil_vertex_presets(struct Main *bmain, struct ToolSettings *ts, const bool reset)
Definition: brush.c:1412
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:714
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1034
struct Base * CTX_data_active_base(const bContext *C)
Definition: context.c:1284
#define CTX_DATA_BEGIN(C, Type, instance, member)
Definition: BKE_context.h:252
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1044
struct bGPDlayer * CTX_data_active_gpencil_layer(const bContext *C)
Definition: context.c:1376
struct Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Definition: context.c:1424
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1279
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:760
#define CTX_DATA_COUNT(C, member)
Definition: BKE_context.h:272
struct wmMsgBus * CTX_wm_message_bus(const bContext *C)
Definition: context.c:746
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1018
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1208
#define CTX_DATA_END
Definition: BKE_context.h:260
void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1203
void BKE_gpencil_layer_active_set(struct bGPdata *gpd, struct bGPDlayer *active)
Definition: gpencil.c:1698
struct bGPDlayer * BKE_gpencil_layer_active_get(struct bGPdata *gpd)
Definition: gpencil.c:1650
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps)
Definition: gpencil.c:370
int BKE_gpencil_object_material_ensure(struct Main *bmain, struct Object *ob, struct Material *material)
Definition: gpencil.c:1851
void BKE_gpencil_palette_ensure(struct Main *bmain, struct Scene *scene)
Definition: gpencil.c:2484
void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps)
Definition: gpencil.c:1210
struct bGPDcurve * BKE_gpencil_stroke_editcurve_new(const int tot_curve_points)
Definition: gpencil.c:910
bool BKE_gpencil_layer_is_editable(const struct bGPDlayer *gpl)
bool BKE_gpencil_layer_frame_delete(struct bGPDlayer *gpl, struct bGPDframe *gpf)
Definition: gpencil.c:1467
int BKE_gpencil_object_material_index_get(struct Object *ob, struct Material *ma)
Definition: gpencil.c:2437
struct bGPDframe * BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe)
Definition: gpencil.c:1281
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:401
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1307
#define GPENCIL_STRENGTH_MIN
Definition: BKE_gpencil.h:178
struct bGPdata * BKE_gpencil_data_addnew(struct Main *bmain, const char name[])
Definition: gpencil.c:742
void BKE_gpencil_layer_transform_matrix_get(const struct Depsgraph *depsgraph, struct Object *obact, struct bGPDlayer *gpl, float diff_mat[4][4])
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, const bool dup_points, const bool dup_curve)
Definition: gpencil.c:957
struct bGPDlayer * BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive)
Definition: gpencil.c:659
void BKE_gpencil_stroke_weights_duplicate(struct bGPDstroke *gps_src, struct bGPDstroke *gps_dst)
Definition: gpencil.c:928
@ GP_GETFRAME_ADD_NEW
Definition: BKE_gpencil.h:190
@ GP_GETFRAME_USE_PREV
Definition: BKE_gpencil.h:187
void BKE_gpencil_stroke_editcurve_update(struct bGPdata *gpd, struct bGPDlayer *gpl, struct bGPDstroke *gps)
void BKE_gpencil_editcurve_stroke_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps, struct bGPDcurve *gpc)
void BKE_gpencil_editcurve_recalculate_handles(struct bGPDstroke *gps)
void BKE_gpencil_editcurve_subdivide(struct bGPDstroke *gps, const int cuts)
void BKE_gpencil_stroke_flip(struct bGPDstroke *gps)
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence)
Definition: gpencil_geom.c:841
void BKE_gpencil_stroke_merge_distance(struct bGPdata *gpd, struct bGPDframe *gpf, struct bGPDstroke *gps, const float threshold, const bool use_unselected)
bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence)
Definition: gpencil_geom.c:905
struct bGPDstroke * BKE_gpencil_stroke_delete_tagged_points(struct bGPdata *gpd, struct bGPDframe *gpf, struct bGPDstroke *gps, struct bGPDstroke *next_stroke, int tag_flags, const bool select, const bool flat_cap, const int limit)
bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence)
Definition: gpencil_geom.c:968
void BKE_gpencil_stroke_simplify_adaptive(struct bGPdata *gpd, struct bGPDstroke *gps, float epsilon)
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps)
bool BKE_gpencil_stroke_sample(struct bGPdata *gpd, struct bGPDstroke *gps, const float dist, const bool select)
Definition: gpencil_geom.c:429
bool BKE_gpencil_stroke_smooth(struct bGPDstroke *gps, int i, float inf)
Definition: gpencil_geom.c:778
void BKE_gpencil_stroke_simplify_fixed(struct bGPdata *gpd, struct bGPDstroke *gps)
void BKE_gpencil_stroke_join(struct bGPDstroke *gps_a, struct bGPDstroke *gps_b, const bool leave_gaps, const bool fit_thickness)
void BKE_gpencil_curve_delete_tagged_points(struct bGPdata *gpd, struct bGPDframe *gpf, struct bGPDstroke *gps, struct bGPDstroke *next_stroke, struct bGPDcurve *gpc, int tag_flags)
bool BKE_gpencil_stroke_close(struct bGPDstroke *gps)
bool BKE_gpencil_stroke_trim(struct bGPdata *gpd, struct bGPDstroke *gps)
void id_us_min(struct ID *id)
Definition: lib_id.c:297
char * BKE_id_to_unique_string_key(const struct ID *id)
Definition: lib_id.c:2260
General operations, lookup, etc. for materials.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
Definition: material.c:713
bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob)
Definition: material.c:1107
bool BKE_object_material_slot_used(struct ID *id, short actcol)
Definition: material.c:465
struct Material * BKE_object_material_get(struct Object *ob, short act)
Definition: material.c:697
struct Material * BKE_gpencil_material(struct Object *ob, short act)
Definition: material.c:703
General operations, lookup, etc. for blender objects.
bool BKE_paint_ensure(struct ToolSettings *ts, struct Paint **r_paint)
Definition: paint.c:1032
void BKE_paint_toolslots_brush_validate(struct Main *bmain, struct Paint *paint)
void BKE_report(ReportList *reports, ReportType type, const char *message)
Definition: report.c:104
void BKE_reportf(ReportList *reports, ReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph)
Definition: scene.c:2794
#define BLI_assert(a)
Definition: BLI_assert.h:58
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:146
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:150
GHash * BLI_ghash_str_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
#define GHASH_ITER(gh_iter_, ghash_)
Definition: BLI_ghash.h:169
bool BLI_ghash_haskey(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:941
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:756
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:1008
GHash * BLI_ghash_ptr_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
bool BLI_ghash_ensure_p(GHash *gh, void *key, void ***r_val) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:851
void * BLI_ghash_lookup(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:803
void BLI_lasso_boundbox(struct rcti *rect, const int mcoords[][2], const unsigned int mcoords_len)
Definition: lasso_2d.c:31
bool BLI_lasso_is_point_inside(const int mcoords[][2], const unsigned int mcoords_len, const int sx, const int sy, const int error_value)
Definition: lasso_2d.c:54
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:124
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:87
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:281
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:188
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:352
void void void BLI_movelisttolist(struct ListBase *dst, struct ListBase *src) ATTR_NONNULL(1
void void BLI_INLINE bool BLI_listbase_is_single(const struct ListBase *lb)
Definition: BLI_listbase.h:120
void * BLI_findstring(const struct ListBase *listbase, const char *id, const int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:110
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:133
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:395
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float interpf(float a, float b, float t)
void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
Definition: math_matrix.c:3246
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:742
MINLINE void copy_v4_v4(float r[4], const float a[4])
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], const float t)
Definition: math_vector.c:49
MINLINE float len_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
void minmax_v3v3_v3(float min[3], float max[3], const float vec[3])
Definition: math_vector.c:1020
MINLINE float len_squared_v3v3(const float a[3], const float b[3]) ATTR_WARN_UNUSED_RESULT
MINLINE void sub_v3_v3v3(float r[3], const float a[3], const float b[3])
void interp_v4_v4v4(float r[4], const float a[4], const float b[4], const float t)
Definition: math_vector.c:58
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
Definition: math_vector.c:270
MINLINE void zero_v3(float r[3])
MINLINE void add_v3_v3(float r[3], const float a[3])
bool BLI_rcti_isect_pt(const struct rcti *rect, const int x, const int y)
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, const size_t maxncpy) ATTR_NONNULL()
Definition: string.c:108
#define INIT_MINMAX(min, max)
#define POINTER_FROM_INT(i)
#define UNUSED(x)
#define ELEM(...)
#define STREQ(a, b)
#define CLAMP_MIN(a, b)
#define DATA_(msgid)
struct Depsgraph Depsgraph
Definition: DEG_depsgraph.h:51
void DEG_id_tag_update_ex(struct Main *bmain, struct ID *id, int flag)
void DEG_id_tag_update(struct ID *id, int flag)
void DEG_relations_tag_update(struct Main *bmain)
float DEG_get_ctime(const Depsgraph *graph)
@ ID_RECALC_TRANSFORM
Definition: DNA_ID.h:599
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:654
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:611
#define BEZT_DESEL_ALL(bezt)
#define BEZT_SEL_IDX(bezt, i)
@ GP_CURVE_NEEDS_STROKE_UPDATE
@ GP_CURVE_SELECT
@ GP_STROKE_CAP_MAX
@ GP_STROKE_CAP_ROUND
@ GP_STROKE_CAP_FLAT
@ GP_STROKE_NEEDS_CURVE_UPDATE
@ GP_STROKE_SELECT
@ GP_STROKE_CYCLIC
#define GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)
#define GPENCIL_PAINT_MODE(gpd)
@ GP_LAYER_LOCKED
@ GP_LAYER_HIDE
@ GP_FRAME_SELECT
@ GP_DATA_STROKE_WEIGHTMODE
@ GP_DATA_STROKE_VERTEXMODE
@ GP_DATA_STROKE_PAINTMODE
@ GP_DATA_STROKE_SCULPTMODE
@ GP_DATA_AUTOLOCK_LAYERS
@ GP_DATA_STROKE_EDITMODE
#define GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)
@ GP_CURVE_POINT_SELECT
@ GP_SPOINT_TAG
@ GP_SPOINT_SELECT
@ GP_MATERIAL_LOCKED
@ GP_MATERIAL_HIDE
@ OB_MODE_VERTEX_GPENCIL
@ OB_MODE_EDIT_GPENCIL
@ OB_MODE_WEIGHT_GPENCIL
@ OB_MODE_SCULPT_GPENCIL
@ OB_MODE_OBJECT
@ OB_MODE_PAINT_GPENCIL
Object is a sort of wrapper for general info.
@ OB_GPENCIL
#define CFRA
@ GP_SELECTMODE_STROKE
@ SPACE_VIEW3D
eDupli_ID_Flags
@ USER_DUP_ACT
@ V3D_AROUND_CENTER_BOUNDS
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
eGP_ReprojectModes
Definition: ED_gpencil.h:68
@ GP_REPROJECT_VIEW
Definition: ED_gpencil.h:74
@ GP_REPROJECT_CURSOR
Definition: ED_gpencil.h:78
@ GP_REPROJECT_SIDE
Definition: ED_gpencil.h:71
@ GP_REPROJECT_TOP
Definition: ED_gpencil.h:72
@ GP_REPROJECT_FRONT
Definition: ED_gpencil.h:70
@ GP_REPROJECT_SURFACE
Definition: ED_gpencil.h:76
struct Base * ED_object_add_duplicate(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct Base *base, const eDupli_ID_Flags dupflag)
Definition: object_add.c:3313
void ED_object_posemode_set_for_weight_paint(struct bContext *C, struct Main *bmain, struct Object *ob, const bool is_mode_set)
Definition: object_modes.c:361
void ED_outliner_select_sync_from_object_tag(struct bContext *C)
Definition: outliner_sync.c:56
bool ED_operator_view3d_active(struct bContext *C)
Definition: screen_ops.c:230
SnapObjectContext * ED_transform_snap_object_context_create_view3d(struct Scene *scene, int flag, const struct ARegion *region, const struct View3D *v3d)
void ED_transform_snap_object_context_destroy(SnapObjectContext *sctx)
float ED_view3d_grid_view_scale(struct Scene *scene, struct View3D *v3d, struct ARegion *region, const char **r_grid_unit)
Definition: view3d_draw.c:917
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum const void *lists _GL_VOID_RET _GL_VOID const GLdouble *equation _GL_VOID_RET _GL_VOID GLdouble GLdouble blue _GL_VOID_RET _GL_VOID GLfloat GLfloat blue _GL_VOID_RET _GL_VOID GLint GLint blue _GL_VOID_RET _GL_VOID GLshort GLshort blue _GL_VOID_RET _GL_VOID GLubyte GLubyte blue _GL_VOID_RET _GL_VOID GLuint GLuint blue _GL_VOID_RET _GL_VOID GLushort GLushort blue _GL_VOID_RET _GL_VOID GLbyte GLbyte GLbyte alpha _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble alpha _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat alpha _GL_VOID_RET _GL_VOID GLint GLint GLint alpha _GL_VOID_RET _GL_VOID GLshort GLshort GLshort alpha _GL_VOID_RET _GL_VOID GLubyte GLubyte GLubyte alpha _GL_VOID_RET _GL_VOID GLuint GLuint GLuint alpha _GL_VOID_RET _GL_VOID GLushort GLushort GLushort alpha _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLint GLsizei GLsizei GLenum type _GL_VOID_RET _GL_VOID GLsizei GLenum GLenum const void *pixels _GL_VOID_RET _GL_VOID const void *pointer _GL_VOID_RET _GL_VOID GLdouble v _GL_VOID_RET _GL_VOID GLfloat v _GL_VOID_RET _GL_VOID GLint GLint i2 _GL_VOID_RET _GL_VOID GLint j _GL_VOID_RET _GL_VOID GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble GLdouble GLdouble zFar _GL_VOID_RET _GL_UINT GLdouble *equation _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLenum GLfloat *v _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLfloat *values _GL_VOID_RET _GL_VOID GLushort *values _GL_VOID_RET _GL_VOID GLenum GLfloat *params _GL_VOID_RET _GL_VOID GLenum GLdouble *params _GL_VOID_RET _GL_VOID GLenum GLint *params _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_BOOL GLfloat param _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLushort pattern _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLint GLdouble GLdouble GLint GLint const GLdouble *points _GL_VOID_RET _GL_VOID GLdouble GLdouble u2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLint GLdouble GLdouble v2 _GL_VOID_RET _GL_VOID GLenum GLfloat param _GL_VOID_RET _GL_VOID GLenum GLint param _GL_VOID_RET _GL_VOID GLenum mode _GL_VOID_RET _GL_VOID GLdouble GLdouble nz _GL_VOID_RET _GL_VOID GLfloat GLfloat nz _GL_VOID_RET _GL_VOID GLint GLint nz _GL_VOID_RET _GL_VOID GLshort GLshort nz _GL_VOID_RET _GL_VOID GLsizei const void *pointer _GL_VOID_RET _GL_VOID GLsizei const GLfloat *values _GL_VOID_RET _GL_VOID GLsizei const GLushort *values _GL_VOID_RET _GL_VOID GLint param _GL_VOID_RET _GL_VOID const GLuint const GLclampf *priorities _GL_VOID_RET _GL_VOID GLdouble y _GL_VOID_RET _GL_VOID GLfloat y _GL_VOID_RET _GL_VOID GLint y _GL_VOID_RET _GL_VOID GLshort y _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLfloat GLfloat z _GL_VOID_RET _GL_VOID GLint GLint z _GL_VOID_RET _GL_VOID GLshort GLshort z _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble w _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat w _GL_VOID_RET _GL_VOID GLint GLint GLint w _GL_VOID_RET _GL_VOID GLshort GLshort GLshort w _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble y2 _GL_VOID_RET _GL_VOID GLfloat GLfloat GLfloat y2 _GL_VOID_RET _GL_VOID GLint GLint GLint y2 _GL_VOID_RET _GL_VOID GLshort GLshort GLshort y2 _GL_VOID_RET _GL_VOID GLdouble GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLdouble GLdouble z _GL_VOID_RET _GL_VOID GLuint *buffer _GL_VOID_RET _GL_VOID GLdouble t _GL_VOID_RET _GL_VOID GLfloat t _GL_VOID_RET _GL_VOID GLint t _GL_VOID_RET _GL_VOID GLshort t _GL_VOID_RET _GL_VOID GLdouble GLdouble r _GL_VOID_RET _GL_VOID GLfloat GLfloat r _GL_VOID_RET _GL_VOID GLint GLint r _GL_VOID_RET _GL_VOID GLshort GLshort r _GL_VOID_RET _GL_VOID GLdouble GLdouble r
_GL_VOID GLfloat value _GL_VOID_RET _GL_VOID const GLuint GLboolean *residences _GL_BOOL_RET _GL_VOID GLsizei GLfloat GLfloat GLfloat GLfloat const GLubyte *bitmap _GL_VOID_RET _GL_VOID GLenum type
Read Guarded memory(de)allocation.
#define MEM_recallocN(vmemh, len)
#define MEM_SAFE_FREE(v)
Group RGB to Bright Vector Camera CLAMP
@ PROP_SKIP_SAVE
Definition: RNA_types.h:204
@ PROP_HIDDEN
Definition: RNA_types.h:202
#define C
Definition: RandGen.cpp:39
#define V2D_IS_CLIPPED
Definition: UI_view2d.h:40
#define NC_GEOM
Definition: WM_types.h:294
#define ND_DRAW
Definition: WM_types.h:362
@ OPTYPE_UNDO
Definition: WM_types.h:155
@ OPTYPE_REGISTER
Definition: WM_types.h:153
#define ND_DATA
Definition: WM_types.h:408
#define ND_GPENCIL_EDITMODE
Definition: WM_types.h:403
@ WM_OP_EXEC_DEFAULT
Definition: WM_types.h:204
#define ND_MODE
Definition: WM_types.h:345
#define NC_SCENE
Definition: WM_types.h:279
#define ND_TOOLSETTINGS
Definition: WM_types.h:349
#define NA_EDITED
Definition: WM_types.h:462
#define ND_SELECT
Definition: WM_types.h:407
#define NC_GPENCIL
Definition: WM_types.h:300
#define ND_SPACE_VIEW3D
Definition: WM_types.h:423
#define NC_OBJECT
Definition: WM_types.h:280
#define NC_SPACE
Definition: WM_types.h:293
#define NA_SELECTED
Definition: WM_types.h:467
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
unsigned int U
Definition: btGjkEpa3.h:78
SIMD_FORCE_INLINE btScalar length(const btQuaternion &q)
Return the length of a quaternion.
Definition: btQuaternion.h:895
Scene scene
const Depsgraph * depsgraph
void * user_data
static bool is_inside(int x, int y, int cols, int rows)
Definition: filesel.c:663
static bool gpencil_sculptmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:470
void ED_gpencil_strokes_copybuf_free(void)
void GPENCIL_OT_recalc_geometry(wmOperatorType *ot)
void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
static int gpencil_stroke_separate_exec(bContext *C, wmOperator *op)
static GHash * gpencil_strokes_copypastebuf_colors_material_to_name_create(Main *bmain)
void GPENCIL_OT_stroke_cyclical_set(wmOperatorType *ot)
static int gpencil_delete_selected_strokes(bContext *C)
static int gpencil_stroke_cyclical_set_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_move_to_layer(wmOperatorType *ot)
static bool gpencil_actframe_delete_all_poll(bContext *C)
static int gpencil_strokes_paste_exec(bContext *C, wmOperator *op)
static int gpencil_snap_to_cursor(bContext *C, wmOperator *op)
static void gpencil_cutter_dissolve(bGPdata *gpd, bGPDlayer *hit_layer, bGPDstroke *hit_stroke, const bool flat_caps)
static int gpencil_extrude_exec(bContext *C, wmOperator *op)
struct tJoinStrokes tJoinStrokes
static bool gpencil_vertexmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:691
static int gpencil_stroke_simplify_exec(bContext *C, wmOperator *op)
static bool annotation_actframe_delete_poll(bContext *C)
static bool gpencil_dissolve_selected_curve_points(bContext *C, bGPdata *gpd, eGP_DissolveMode mode)
void GPENCIL_OT_paintmode_toggle(wmOperatorType *ot)
Definition: gpencil_edit.c:442
void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot)
void GPENCIL_OT_copy(wmOperatorType *ot)
static GHash * gpencil_strokes_copypastebuf_colors
@ GP_STROKE_CAPS_TOGGLE_START
@ GP_STROKE_CAPS_TOGGLE_END
@ GP_STROKE_CAPS_TOGGLE_BOTH
@ GP_STROKE_CAPS_TOGGLE_DEFAULT
static void gpencil_duplicate_points(bGPdata *gpd, const bGPDstroke *gps, ListBase *new_strokes, const char *layername)
Definition: gpencil_edit.c:844
static int gpencil_snap_cursor_to_sel(bContext *C, wmOperator *op)
static bool gpencil_stroke_points_centroid(Depsgraph *depsgraph, bContext *C, Object *obact, bGPdata *gpd, float r_centroid[3], float r_min[3], float r_max[3], size_t *count)
void GPENCIL_OT_snap_to_grid(wmOperatorType *ot)
static int gpencil_selectmode_toggle_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:285
static void gpencil_add_move_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps)
void GPENCIL_OT_active_frame_delete(wmOperatorType *ot)
static int gpencil_paintmode_toggle_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:364
static bool gpencil_weightmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:579
@ GP_STROKE_CYCLIC_OPEN
@ GP_STROKE_CYCLIC_CLOSE
@ GP_STROKE_CYCLIC_TOGGLE
void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
static bool gpencil_strokes_edit3d_poll(bContext *C)
Definition: gpencil_edit.c:117
static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:166
void GPENCIL_OT_dissolve(wmOperatorType *ot)
static bool gpencil_dissolve_selected_stroke_points(bContext *C, bGPdata *gpd, eGP_DissolveMode mode)
static int gpencil_actframe_delete_all_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_stroke_simplify(wmOperatorType *ot)
static int gpencil_stroke_trim_exec(bContext *C, wmOperator *op)
static void gpencil_strokes_copypastebuf_colors_material_to_name_free(GHash *ma_to_name)
static int gpencil_cutter_lasso_select(bContext *C, wmOperator *op, GPencilTestFn is_inside_fn, void *user_data)
static void gpencil_strokes_copypastebuf_colors_name_to_material_free(GHash *name_to_ma)
static bool gpencil_cyclical_set_curve_edit_poll_property(const bContext *C, wmOperator *UNUSED(op), const PropertyRNA *prop)
static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:923
static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
static int gpencil_get_nearest_stroke_index(tJoinStrokes *strokes_list, const bGPDstroke *gps, const int totstrokes)
eGP_DissolveMode
@ GP_DISSOLVE_POINTS
@ GP_DISSOLVE_UNSELECT
@ GP_DISSOLVE_BETWEEN
int gpencil_delete_selected_point_wrap(bContext *C)
void GPENCIL_OT_snap_cursor_to_selected(wmOperatorType *ot)
static int gpencil_dissolve_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_weightmode_toggle(wmOperatorType *ot)
Definition: gpencil_edit.c:663
bool(* GPencilTestFn)(bGPDstroke *gps, bGPDspoint *pt, const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data)
static bool gpencil_merge_by_distance_poll(bContext *C)
static int gpencil_delete_selected_points(bContext *C)
static GHash * gpencil_strokes_copypastebuf_colors_name_to_material_create(Main *bmain)
static int gpencil_vertexmode_toggle_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:700
static bool gpencil_editmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:132
void GPENCIL_OT_stroke_split(wmOperatorType *ot)
static int gpencil_blank_frame_add_exec(bContext *C, wmOperator *op)
static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts)
static int gpencil_stroke_join_exec(bContext *C, wmOperator *op)
static bool gpencil_stroke_edit_poll(bContext *C)
Definition: gpencil_edit.c:104
void GPENCIL_OT_blank_frame_add(wmOperatorType *ot)
void GPENCIL_OT_annotation_active_frame_delete(wmOperatorType *ot)
void GPENCIL_OT_stroke_trim(wmOperatorType *ot)
static int gpencil_strokes_reproject_exec(bContext *C, wmOperator *op)
static bool gpencil_strokes_paste_poll(bContext *C)
void GPENCIL_OT_editmode_toggle(wmOperatorType *ot)
Definition: gpencil_edit.c:245
void GPENCIL_OT_active_frames_delete_all(wmOperatorType *ot)
void GPENCIL_OT_delete(wmOperatorType *ot)
static int gpencil_dissolve_selected_points(bContext *C, eGP_DissolveMode mode)
static int gpencil_stroke_apply_thickness_exec(bContext *C, wmOperator *UNUSED(op))
void GPENCIL_OT_stroke_merge_by_distance(wmOperatorType *ot)
static int gpencil_stroke_subdivide_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_vertexmode_toggle(wmOperatorType *ot)
Definition: gpencil_edit.c:772
bool ED_object_gpencil_exit(struct Main *bmain, Object *ob)
void GPENCIL_OT_stroke_apply_thickness(wmOperatorType *ot)
static bool gpencil_selectmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:274
static int gpencil_stroke_flip_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_paste(wmOperatorType *ot)
static int gpencil_move_to_layer_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_stroke_caps_set(wmOperatorType *ot)
static bool gpencil_actframe_delete_poll(bContext *C)
static bool gpencil_test_lasso(bGPDstroke *gps, bGPDspoint *pt, const GP_SpaceConversion *gsc, const float diff_mat[4][4], void *user_data)
static int gpencil_stroke_sample_exec(bContext *C, wmOperator *op)
static bool gpencil_snap_poll(bContext *C)
static int gpencil_sculptmode_toggle_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:480
static void gpencil_copy_move_point(bGPDstroke *gps, bGPDspoint *temp_points, MDeformVert *temp_dverts, int from_idx, int to_idx, const bool copy)
void GPENCIL_OT_stroke_separate(wmOperatorType *ot)
ListBase gpencil_strokes_copypastebuf
static int gpencil_snap_to_grid(bContext *C, wmOperator *UNUSED(op))
static int gpencil_stroke_smooth_exec(bContext *C, wmOperator *op)
static int gpencil_stroke_split_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_selection_opacity_toggle(wmOperatorType *ot)
Definition: gpencil_edit.c:822
static bool gpencil_paintmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:354
static void gpencil_curve_extrude_points(bGPdata *gpd, bGPDframe *gpf, bGPDstroke *gps, bGPDcurve *gpc)
static int gpencil_cutter_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_stroke_cutter(wmOperatorType *ot)
static int gpencil_actframe_delete_exec(bContext *C, wmOperator *op)
static int gpencil_weightmode_toggle_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:589
void GPENCIL_OT_duplicate(wmOperatorType *ot)
static bool gpencil_cutter_poll(bContext *C)
void GPENCIL_OT_stroke_sample(wmOperatorType *ot)
void GPENCIL_OT_stroke_smooth(wmOperatorType *ot)
static int gpencil_stroke_simplify_fixed_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_selectmode_toggle(wmOperatorType *ot)
Definition: gpencil_edit.c:327
eGP_PasteMode
@ GP_COPY_BY_LAYER
@ GP_COPY_TO_ACTIVE
static int gpencil_stroke_caps_set_exec(bContext *C, wmOperator *op)
static int gpencil_count_subdivision_cuts(bGPDstroke *gps)
static int gpencil_strokes_copy_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_extrude(wmOperatorType *ot)
void GPENCIL_OT_reproject(wmOperatorType *ot)
GHash * gpencil_copybuf_validate_colormap(bContext *C)
static bool gpencil_subdivide_curve_edit_poll_property(const bContext *C, wmOperator *UNUSED(op), const PropertyRNA *prop)
static int gpencil_recalc_geometry_exec(bContext *C, wmOperator *UNUSED(op))
static int gpencil_merge_by_distance_exec(bContext *C, wmOperator *op)
static int gpencil_hideselect_toggle_exec(bContext *C, wmOperator *UNUSED(op))
Definition: gpencil_edit.c:800
void GPENCIL_OT_sculptmode_toggle(wmOperatorType *ot)
Definition: gpencil_edit.c:555
eGP_DeleteMode
@ GP_DELETEOP_POINTS
@ GP_DELETEOP_STROKES
@ GP_DELETEOP_FRAME
static bool gpencil_stroke_not_in_curve_edit_mode(bContext *C)
Definition: gpencil_edit.c:148
eGP_SeparateModes
@ GP_SEPARATE_POINT
@ GP_SEPARATE_LAYER
@ GP_SEPARATE_STROKE
void GPENCIL_OT_stroke_join(wmOperatorType *ot)
static int gpencil_delete_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_stroke_subdivide(wmOperatorType *ot)
#define GP_EDITABLE_CURVES_END(gpstroke_iter)
#define GP_EDITABLE_CURVES_BEGIN(gpstroke_iter, C, gpl, gps, gpc)
@ GP_STROKE_JOIN
@ GP_STROKE_JOINCOPY
void gpencil_point_conversion_init(struct bContext *C, GP_SpaceConversion *r_gsc)
#define GP_EDITABLE_STROKES_BEGIN(gpstroke_iter, C, gpl, gps)
void gpencil_point_to_parent_space(const bGPDspoint *pt, const float diff_mat[4][4], bGPDspoint *r_pt)
void gpencil_apply_parent_point(struct Depsgraph *depsgraph, struct Object *obact, bGPDlayer *gpl, bGPDspoint *pt)
bool gpencil_active_layer_poll(struct bContext *C)
#define GP_EDITABLE_STROKES_END(gpstroke_iter)
bool gpencil_add_poll(struct bContext *C)
void gpencil_point_to_xy(const GP_SpaceConversion *gsc, const struct bGPDstroke *gps, const struct bGPDspoint *pt, int *r_x, int *r_y)
bool ED_gpencil_stroke_material_editable(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps)
int ED_gpencil_select_stroke_segment(bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, bGPDspoint *pt, bool select, bool insert, const float scale, float r_hita[3], float r_hitb[3])
void ED_gpencil_setup_modes(bContext *C, bGPdata *gpd, int newmode)
void ED_gpencil_stroke_reproject(Depsgraph *depsgraph, const GP_SpaceConversion *gsc, SnapObjectContext *sctx, bGPDlayer *gpl, bGPDframe *gpf, bGPDstroke *gps, const eGP_ReprojectModes mode, const bool keep_original)
bGPdata * ED_annotation_data_get_active(const bContext *C)
bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps)
void ED_gpencil_reset_layers_parent(Depsgraph *depsgraph, Object *obact, bGPdata *gpd)
bGPdata * ED_gpencil_data_get_active(const bContext *C)
bool ED_gpencil_layer_has_selected_stroke(const bGPDlayer *gpl, const bool is_multiedit)
int count
#define floorf(x)
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:48
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:42
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:47
static ulong * next
static void area(int d1, int d2, int e1, int e2, float weights[2])
static void copy(bNodeTree *dest_ntree, bNode *dest_node, const bNode *src_node)
const char * RNA_property_identifier(const PropertyRNA *prop)
Definition: rna_access.c:1145
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6308
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6355
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6261
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6402
PropertyRNA * RNA_def_float(StructOrFunctionRNA *cont_, const char *identifier, float default_value, float hardmin, float hardmax, const char *ui_name, const char *ui_description, float softmin, float softmax)
Definition: rna_define.c:3825
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3481
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1512
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3585
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3771
static const int steps
Definition: sky_nishita.cpp:28
#define min(a, b)
Definition: sort.c:51
struct Object * object
float vec[3][3]
struct BrushGpencilSettings * gpencil_settings
const int(* mcoords)[2]
char name[66]
Definition: DNA_ID.h:283
void * last
Definition: DNA_listBase.h:47
void * first
Definition: DNA_listBase.h:47
struct MDeformWeight * dw
Definition: BKE_main.h:116
ListBase materials
Definition: BKE_main.h:152
int restore_mode
void * data
struct Brush * brush
struct ToolSettings * toolsettings
View3DCursor cursor
char transform_pivot_point
GpWeightPaint * gp_weightpaint
GpPaint * gp_paint
GpSculptPaint * gp_sculptpaint
char gpencil_selectmode_edit
struct GP_Sculpt_Settings gp_sculpt
GpVertexPaint * gp_vertexpaint
float vertex_opacity
bGPDcurve_point * curve_points
struct bGPDframe * next
ListBase strokes
struct bGPDlayer * next
bGPDframe * actframe
ListBase frames
struct bGPDlayer * prev
float vert_color[4]
struct bGPDstroke * prev
bGPDspoint * points
bGPDtriangle * triangles
bGPDstroke_Runtime runtime
struct bGPDcurve * editcurve
struct MDeformVert * dvert
struct bGPDstroke * next
ListBase layers
bGPDframe * gpf
bGPDstroke * gps
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:752
const char * name
Definition: WM_types.h:721
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:768
const char * idname
Definition: WM_types.h:723
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
bool(* poll_property)(const struct bContext *C, struct wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:782
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:760
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
PropertyRNA * prop
Definition: WM_types.h:814
struct ReportList * reports
struct PointerRNA * ptr
float max
#define G(x, y, z)
uint len
void WM_main_add_notifier(unsigned int type, void *reference)
int WM_operator_name_call(bContext *C, const char *opstring, short context, PointerRNA *properties)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3156
int WM_gesture_lasso_modal(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_lasso_cancel(bContext *C, wmOperator *op)
int WM_gesture_lasso_invoke(bContext *C, wmOperator *op, const wmEvent *event)
const int(* WM_gesture_lasso_path_to_array(bContext *UNUSED(C), wmOperator *op, int *r_mcoords_len))[2]
#define WM_msg_publish_rna_prop(mbus, id_, data_, type_, prop_)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
Definition: wm_operators.c:982
void WM_toolsystem_update_from_context_view3d(bContext *C)