Blender  V3.3
gpencil_edit.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2008 Blender Foundation. */
3 
9 #include <math.h>
10 #include <stddef.h>
11 #include <stdio.h>
12 #include <stdlib.h>
13 #include <string.h>
14 
15 #include "MEM_guardedalloc.h"
16 
17 #include "BLI_blenlib.h"
18 #include "BLI_ghash.h"
19 #include "BLI_lasso_2d.h"
20 #include "BLI_math.h"
21 #include "BLI_string.h"
22 #include "BLI_utildefines.h"
23 
24 #include "BLT_translation.h"
25 
27 #include "DNA_gpencil_types.h"
28 #include "DNA_material_types.h"
29 #include "DNA_meshdata_types.h"
30 #include "DNA_object_types.h"
31 #include "DNA_scene_types.h"
32 #include "DNA_screen_types.h"
33 #include "DNA_space_types.h"
34 #include "DNA_view3d_types.h"
35 
36 #include "BKE_brush.h"
37 #include "BKE_context.h"
38 #include "BKE_deform.h"
39 #include "BKE_global.h"
40 #include "BKE_gpencil.h"
41 #include "BKE_gpencil_curve.h"
42 #include "BKE_gpencil_geom.h"
43 #include "BKE_layer.h"
44 #include "BKE_lib_id.h"
45 #include "BKE_library.h"
46 #include "BKE_main.h"
47 #include "BKE_material.h"
48 #include "BKE_object.h"
49 #include "BKE_paint.h"
50 #include "BKE_report.h"
51 #include "BKE_scene.h"
52 #include "BKE_workspace.h"
53 
54 #include "UI_interface.h"
55 #include "UI_resources.h"
56 
57 #include "WM_api.h"
58 #include "WM_message.h"
59 #include "WM_toolsystem.h"
60 #include "WM_types.h"
61 
62 #include "RNA_access.h"
63 #include "RNA_define.h"
64 #include "RNA_enum_types.h"
65 
66 #include "UI_view2d.h"
67 
68 #include "ED_armature.h"
69 #include "ED_gpencil.h"
70 #include "ED_keyframing.h"
71 #include "ED_object.h"
72 #include "ED_outliner.h"
73 #include "ED_screen.h"
74 #include "ED_select_utils.h"
75 #include "ED_space_api.h"
77 #include "ED_view3d.h"
78 
79 #include "DEG_depsgraph.h"
80 #include "DEG_depsgraph_build.h"
81 #include "DEG_depsgraph_query.h"
82 
83 #include "gpencil_intern.h"
84 
85 /* -------------------------------------------------------------------- */
89 /* poll callback for all stroke editing operators */
91 {
92  /* edit only supported with grease pencil objects */
94  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
95  return false;
96  }
97 
98  /* NOTE: this is a bit slower, but is the most accurate... */
99  return CTX_DATA_COUNT(C, editable_gpencil_strokes) != 0;
100 }
101 
102 /* poll callback to verify edit mode in 3D view only */
104 {
105  /* edit only supported with grease pencil objects */
107  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
108  return false;
109  }
110 
111  /* 2 Requirements:
112  * - 1) Editable GP data
113  * - 2) 3D View only
114  */
116 }
117 
119 {
120  /* edit only supported with grease pencil objects */
122  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
123  return false;
124  }
125 
126  /* if using gpencil object, use this gpd */
127  if (ob->type == OB_GPENCIL) {
128  return ob->data != NULL;
129  }
130 
131  return ED_gpencil_data_get_active(C) != NULL;
132 }
133 
135 {
137  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
138  return false;
139  }
140  bGPdata *gpd = (bGPdata *)ob->data;
142 
143  return (gpl != NULL && !GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd));
144 }
145 
148 /* -------------------------------------------------------------------- */
153 {
154  const int back = RNA_boolean_get(op->ptr, "back");
155 
156  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
158  bool is_object = false;
159  short mode;
160  /* if using a gpencil object, use this datablock */
162  if ((ob) && (ob->type == OB_GPENCIL)) {
163  gpd = ob->data;
164  is_object = true;
165  }
166 
167  if (gpd == NULL) {
168  BKE_report(op->reports, RPT_ERROR, "No active GP data");
169  return OPERATOR_CANCELLED;
170  }
171 
172  /* Just toggle editmode flag... */
174  /* recalculate parent matrix */
175  if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
178  }
179  /* set mode */
180  if (gpd->flag & GP_DATA_STROKE_EDITMODE) {
181  mode = OB_MODE_EDIT_GPENCIL;
182  }
183  else {
184  mode = OB_MODE_OBJECT;
185  }
186 
187  if (is_object) {
188  /* try to back previous mode */
189  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_EDITMODE) == 0) && (back == 1)) {
190  mode = ob->restore_mode;
191  }
192  ob->restore_mode = ob->mode;
193  ob->mode = mode;
194  }
195 
196  /* Recalculate editcurves for strokes where the geometry/vertex colors have changed */
198  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
199  {
200  if (gpc->flag & GP_CURVE_NEEDS_STROKE_UPDATE) {
202  /* Update the selection from the stroke to the curve. */
203  BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
204 
205  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
207  }
208  }
209  GP_EDITABLE_CURVES_END(gps_iter);
210  }
211 
212  /* setup other modes */
213  ED_gpencil_setup_modes(C, gpd, mode);
214  /* set cache as dirty */
216 
220 
221  if (is_object) {
222  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
223  }
224  if (G.background == false) {
226  }
227 
228  return OPERATOR_FINISHED;
229 }
230 
232 {
233  PropertyRNA *prop;
234 
235  /* identifiers */
236  ot->name = "Strokes Edit Mode Toggle";
237  ot->idname = "GPENCIL_OT_editmode_toggle";
238  ot->description = "Enter/Exit edit mode for Grease Pencil strokes";
239 
240  /* callbacks */
243 
244  /* flags */
246 
247  /* properties */
248  prop = RNA_def_boolean(
249  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
251 }
252 
255 /* -------------------------------------------------------------------- */
259 /* set select mode */
261 {
262  /* edit only supported with grease pencil objects */
264  if ((ob == NULL) || (ob->type != OB_GPENCIL) || (ob->mode != OB_MODE_EDIT_GPENCIL)) {
265  return false;
266  }
267 
269 }
270 
272 {
276  const int mode = RNA_int_get(op->ptr, "mode");
277  bool changed = false;
278 
279  if (ts->gpencil_selectmode_edit == mode) {
280  return OPERATOR_FINISHED;
281  }
282 
283  /* Just set mode */
284  ts->gpencil_selectmode_edit = mode;
285 
286  /* If the mode is Stroke, extend selection. */
287  if ((ob) && (ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE)) {
288  bGPdata *gpd = (bGPdata *)ob->data;
289  /* Extend selection to all points in all selected strokes. */
290  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
291  if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
292  changed = true;
293  bGPDspoint *pt;
294  for (int i = 0; i < gps->totpoints; i++) {
295  pt = &gps->points[i];
296  pt->flag |= GP_SPOINT_SELECT;
297  }
298  }
299  }
300  CTX_DATA_END;
301  if (changed) {
303  }
304  }
305 
309 
310  return OPERATOR_FINISHED;
311 }
312 
314 {
315  PropertyRNA *prop;
316 
317  /* identifiers */
318  ot->name = "Select Mode Toggle";
319  ot->idname = "GPENCIL_OT_selectmode_toggle";
320  ot->description = "Set selection mode for Grease Pencil strokes";
321 
322  /* callbacks */
325 
326  /* flags */
328 
329  /* properties */
330  prop = RNA_def_int(ot->srna, "mode", 0, 0, 2, "Select Mode", "Select mode", 0, 2);
332 }
333 
336 /* -------------------------------------------------------------------- */
341 {
342  /* if using gpencil object, use this gpd */
344  if ((ob) && (ob->type == OB_GPENCIL)) {
345  return ob->data != NULL;
346  }
347  return ED_gpencil_data_get_active(C) != NULL;
348 }
349 
351 {
352  const bool back = RNA_boolean_get(op->ptr, "back");
353 
354  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
355  Main *bmain = CTX_data_main(C);
358 
359  bool is_object = false;
360  short mode;
361  /* if using a gpencil object, use this datablock */
363  if ((ob) && (ob->type == OB_GPENCIL)) {
364  gpd = ob->data;
365  is_object = true;
366  }
367 
368  if (gpd == NULL) {
369  return OPERATOR_CANCELLED;
370  }
371 
372  /* Just toggle paintmode flag... */
374  /* set mode */
375  if (gpd->flag & GP_DATA_STROKE_PAINTMODE) {
376  mode = OB_MODE_PAINT_GPENCIL;
377  }
378  else {
379  mode = OB_MODE_OBJECT;
380  }
381 
382  if (is_object) {
383  /* try to back previous mode */
384  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0) && (back == 1)) {
385  mode = ob->restore_mode;
386  }
387  ob->restore_mode = ob->mode;
388  ob->mode = mode;
389  }
390 
391  if (mode == OB_MODE_PAINT_GPENCIL) {
392  /* Be sure we have brushes and Paint settings.
393  * Need Draw and Vertex (used for Tint). */
394  BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
395  BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint);
396 
397  BKE_brush_gpencil_paint_presets(bmain, ts, false);
398 
399  /* Ensure Palette by default. */
401 
402  Paint *paint = &ts->gp_paint->paint;
403  /* if not exist, create a new one */
404  if ((paint->brush == NULL) || (paint->brush->gpencil_settings == NULL)) {
405  BKE_brush_gpencil_paint_presets(bmain, ts, true);
406  }
408  }
409 
410  /* setup other modes */
411  ED_gpencil_setup_modes(C, gpd, mode);
412  /* set cache as dirty */
414 
417 
418  if (is_object) {
419  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
420  }
421  if (G.background == false) {
423  }
424 
425  return OPERATOR_FINISHED;
426 }
427 
429 {
430  PropertyRNA *prop;
431 
432  /* identifiers */
433  ot->name = "Strokes Paint Mode Toggle";
434  ot->idname = "GPENCIL_OT_paintmode_toggle";
435  ot->description = "Enter/Exit paint mode for Grease Pencil strokes";
436 
437  /* callbacks */
440 
441  /* flags */
443 
444  /* properties */
445  prop = RNA_def_boolean(
446  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
448 }
449 
452 /* -------------------------------------------------------------------- */
457 {
458  /* if using gpencil object, use this gpd */
460  if ((ob) && (ob->type == OB_GPENCIL)) {
461  return ob->data != NULL;
462  }
463  return ED_gpencil_data_get_active(C) != NULL;
464 }
465 
467 {
468  Main *bmain = CTX_data_main(C);
470 
471  const bool back = RNA_boolean_get(op->ptr, "back");
472 
473  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
475  bool is_object = false;
476  short mode;
477  /* if using a gpencil object, use this datablock */
479  if ((ob) && (ob->type == OB_GPENCIL)) {
480  gpd = ob->data;
481  is_object = true;
482  }
483 
484  if (gpd == NULL) {
485  return OPERATOR_CANCELLED;
486  }
487 
488  /* Just toggle sculptmode flag... */
490  /* set mode */
491  if (gpd->flag & GP_DATA_STROKE_SCULPTMODE) {
492  mode = OB_MODE_SCULPT_GPENCIL;
493  }
494  else {
495  mode = OB_MODE_OBJECT;
496  }
497 
498  if (is_object) {
499  /* try to back previous mode */
500  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_SCULPTMODE) == 0) && (back == 1)) {
501  mode = ob->restore_mode;
502  }
503  ob->restore_mode = ob->mode;
504  ob->mode = mode;
505  }
506 
507  if (mode == OB_MODE_SCULPT_GPENCIL) {
508  /* Be sure we have brushes. */
509  BKE_paint_ensure(ts, (Paint **)&ts->gp_sculptpaint);
510 
511  const bool reset_mode = (ts->gp_sculptpaint->paint.brush == NULL);
512  BKE_brush_gpencil_sculpt_presets(bmain, ts, reset_mode);
513 
515  }
516 
517  /* setup other modes */
518  ED_gpencil_setup_modes(C, gpd, mode);
519  /* set cache as dirty */
521 
524 
525  if (is_object) {
526  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
527  }
528  if (G.background == false) {
530  }
531 
532  return OPERATOR_FINISHED;
533 }
534 
537 /* -------------------------------------------------------------------- */
542 {
543  PropertyRNA *prop;
544 
545  /* identifiers */
546  ot->name = "Strokes Sculpt Mode Toggle";
547  ot->idname = "GPENCIL_OT_sculptmode_toggle";
548  ot->description = "Enter/Exit sculpt mode for Grease Pencil strokes";
549 
550  /* callbacks */
553 
554  /* flags */
556 
557  /* properties */
558  prop = RNA_def_boolean(
559  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
561 }
562 
563 /* Stroke Weight Paint Mode Management */
564 
566 {
567  /* if using gpencil object, use this gpd */
569  if ((ob) && (ob->type == OB_GPENCIL)) {
570  return ob->data != NULL;
571  }
572  return ED_gpencil_data_get_active(C) != NULL;
573 }
574 
576 {
577  Main *bmain = CTX_data_main(C);
579 
580  const bool back = RNA_boolean_get(op->ptr, "back");
581 
582  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
584  bool is_object = false;
585  short mode;
586  /* if using a gpencil object, use this datablock */
588  if ((ob) && (ob->type == OB_GPENCIL)) {
589  gpd = ob->data;
590  is_object = true;
591  }
592  const int mode_flag = OB_MODE_WEIGHT_GPENCIL;
593  const bool is_mode_set = (ob->mode & mode_flag) != 0;
594 
595  if (gpd == NULL) {
596  return OPERATOR_CANCELLED;
597  }
598 
599  /* Just toggle weightmode flag... */
601  /* set mode */
602  if (gpd->flag & GP_DATA_STROKE_WEIGHTMODE) {
603  mode = OB_MODE_WEIGHT_GPENCIL;
604  }
605  else {
606  mode = OB_MODE_OBJECT;
607  }
608 
609  if (is_object) {
610  /* try to back previous mode */
611  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_WEIGHTMODE) == 0) && (back == 1)) {
612  mode = ob->restore_mode;
613  }
614  ob->restore_mode = ob->mode;
615  ob->mode = mode;
616 
617  /* Prepare armature posemode. */
618  ED_object_posemode_set_for_weight_paint(C, bmain, ob, is_mode_set);
619  }
620 
621  if (mode == OB_MODE_WEIGHT_GPENCIL) {
622  /* Be sure we have brushes. */
623  BKE_paint_ensure(ts, (Paint **)&ts->gp_weightpaint);
624 
625  const bool reset_mode = (ts->gp_weightpaint->paint.brush == NULL);
626  BKE_brush_gpencil_weight_presets(bmain, ts, reset_mode);
627 
629  }
630 
631  /* setup other modes */
632  ED_gpencil_setup_modes(C, gpd, mode);
633  /* set cache as dirty */
635 
638 
639  if (is_object) {
640  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
641  }
642  if (G.background == false) {
644  }
645 
646  return OPERATOR_FINISHED;
647 }
648 
650 {
651  PropertyRNA *prop;
652 
653  /* identifiers */
654  ot->name = "Strokes Weight Mode Toggle";
655  ot->idname = "GPENCIL_OT_weightmode_toggle";
656  ot->description = "Enter/Exit weight paint mode for Grease Pencil strokes";
657 
658  /* callbacks */
661 
662  /* flags */
664 
665  /* properties */
666  prop = RNA_def_boolean(
667  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
669 }
670 
673 /* -------------------------------------------------------------------- */
678 {
679  /* if using gpencil object, use this gpd */
681  if ((ob) && (ob->type == OB_GPENCIL)) {
682  return ob->data != NULL;
683  }
684  return ED_gpencil_data_get_active(C) != NULL;
685 }
687 {
688  const bool back = RNA_boolean_get(op->ptr, "back");
689 
690  struct wmMsgBus *mbus = CTX_wm_message_bus(C);
691  Main *bmain = CTX_data_main(C);
694 
695  bool is_object = false;
696  short mode;
697  /* if using a gpencil object, use this datablock */
699  if ((ob) && (ob->type == OB_GPENCIL)) {
700  gpd = ob->data;
701  is_object = true;
702  }
703 
704  if (gpd == NULL) {
705  return OPERATOR_CANCELLED;
706  }
707 
708  /* Just toggle paintmode flag... */
710  /* set mode */
711  if (gpd->flag & GP_DATA_STROKE_VERTEXMODE) {
712  mode = OB_MODE_VERTEX_GPENCIL;
713  }
714  else {
715  mode = OB_MODE_OBJECT;
716  }
717 
718  if (is_object) {
719  /* try to back previous mode */
720  if ((ob->restore_mode) && ((gpd->flag & GP_DATA_STROKE_VERTEXMODE) == 0) && (back == 1)) {
721  mode = ob->restore_mode;
722  }
723  ob->restore_mode = ob->mode;
724  ob->mode = mode;
725  }
726 
727  if (mode == OB_MODE_VERTEX_GPENCIL) {
728  /* Be sure we have brushes.
729  * Need Draw as well (used for Palettes). */
730  BKE_paint_ensure(ts, (Paint **)&ts->gp_paint);
731  BKE_paint_ensure(ts, (Paint **)&ts->gp_vertexpaint);
732 
733  const bool reset_mode = (ts->gp_vertexpaint->paint.brush == NULL);
734  BKE_brush_gpencil_vertex_presets(bmain, ts, reset_mode);
735 
737 
738  /* Ensure Palette by default. */
740  }
741 
742  /* setup other modes */
743  ED_gpencil_setup_modes(C, gpd, mode);
744  /* set cache as dirty */
746 
749 
750  if (is_object) {
751  WM_msg_publish_rna_prop(mbus, &ob->id, ob, Object, mode);
752  }
753  if (G.background == false) {
755  }
756 
757  return OPERATOR_FINISHED;
758 }
759 
761 {
762  PropertyRNA *prop;
763 
764  /* identifiers */
765  ot->name = "Strokes Vertex Mode Toggle";
766  ot->idname = "GPENCIL_OT_vertexmode_toggle";
767  ot->description = "Enter/Exit vertex paint mode for Grease Pencil strokes";
768 
769  /* callbacks */
772 
773  /* flags */
775 
776  /* properties */
777  prop = RNA_def_boolean(
778  ot->srna, "back", 0, "Return to Previous Mode", "Return to previous mode");
780 }
781 
784 /* -------------------------------------------------------------------- */
789 {
790  View3D *v3d = CTX_wm_view3d(C);
791  if (v3d == NULL) {
792  return OPERATOR_CANCELLED;
793  }
794 
795  /* Just toggle alpha... */
796  if (v3d->vertex_opacity > 0.0f) {
797  v3d->vertex_opacity = 0.0f;
798  }
799  else {
800  v3d->vertex_opacity = 1.0f;
801  }
802 
806 
807  return OPERATOR_FINISHED;
808 }
809 
811 {
812  /* identifiers */
813  ot->name = "Hide Selected";
814  ot->idname = "GPENCIL_OT_selection_opacity_toggle";
815  ot->description = "Hide/Unhide selected points for Grease Pencil strokes setting alpha factor";
816 
817  /* callbacks */
820 
821  /* flags */
823 }
824 
827 /* -------------------------------------------------------------------- */
831 /* Make copies of selected point segments in a selected stroke */
833  const bGPDstroke *gps,
834  ListBase *new_strokes,
835  const char *layername)
836 {
837  bGPDspoint *pt;
838  int i;
839 
840  int start_idx = -1;
841 
842  /* Step through the original stroke's points:
843  * - We accumulate selected points (from start_idx to current index)
844  * and then convert that to a new stroke
845  */
846  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
847  /* searching for start, are waiting for end? */
848  if (start_idx == -1) {
849  /* is this the first selected point for a new island? */
850  if (pt->flag & GP_SPOINT_SELECT) {
851  start_idx = i;
852  }
853  }
854  if ((start_idx != -1) || (start_idx == gps->totpoints - 1)) {
855  size_t len = 0;
856 
857  /* is this the end of current island yet?
858  * 1) Point i-1 was the last one that was selected
859  * 2) Point i is the last in the array
860  */
861  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
862  len = i - start_idx;
863  }
864  else if (i == gps->totpoints - 1) {
865  len = i - start_idx + 1;
866  }
867 
868  /* make copies of the relevant data */
869  if (len) {
870  bGPDstroke *gpsd;
871 
872  /* make a stupid copy first of the entire stroke (to get the flags too) */
873  gpsd = BKE_gpencil_stroke_duplicate((bGPDstroke *)gps, false, true);
874 
875  /* saves original layer name */
876  BLI_strncpy(gpsd->runtime.tmp_layerinfo, layername, sizeof(gpsd->runtime.tmp_layerinfo));
877 
878  /* now, make a new points array, and copy of the relevant parts */
879  gpsd->points = MEM_mallocN(sizeof(bGPDspoint) * len, "gps stroke points copy");
880  memcpy(gpsd->points, gps->points + start_idx, sizeof(bGPDspoint) * len);
881  gpsd->totpoints = len;
882 
883  if (gps->dvert != NULL) {
884  gpsd->dvert = MEM_mallocN(sizeof(MDeformVert) * len, "gps stroke weights copy");
885  memcpy(gpsd->dvert, gps->dvert + start_idx, sizeof(MDeformVert) * len);
886 
887  /* Copy weights */
888  int e = start_idx;
889  for (int j = 0; j < gpsd->totpoints; j++) {
890  MDeformVert *dvert_dst = &gps->dvert[e];
891  MDeformVert *dvert_src = &gps->dvert[j];
892  dvert_dst->dw = MEM_dupallocN(dvert_src->dw);
893  e++;
894  }
895  }
896 
898 
899  /* add to temp buffer */
900  gpsd->next = gpsd->prev = NULL;
901 
902  BLI_addtail(new_strokes, gpsd);
903 
904  /* cleanup + reset for next */
905  start_idx = -1;
906  }
907  }
908  }
909 }
910 
912 {
914  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
915 
916  if (gpd == NULL) {
917  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
918  return OPERATOR_CANCELLED;
919  }
920 
922  BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
923  return OPERATOR_CANCELLED;
924  }
925 
926  bool changed = false;
927  if (is_curve_edit) {
928  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
929  }
930  else {
931  /* for each visible (and editable) layer's selected strokes,
932  * copy the strokes into a temporary buffer, then append
933  * once all done
934  */
935  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
936  ListBase new_strokes = {NULL, NULL};
937  bGPDframe *gpf = gpl->actframe;
938  bGPDstroke *gps;
939 
940  if (gpf == NULL) {
941  continue;
942  }
943 
944  /* make copies of selected strokes, and deselect these once we're done */
945  for (gps = gpf->strokes.first; gps; gps = gps->next) {
946  /* skip strokes that are invalid for current view */
947  if (ED_gpencil_stroke_can_use(C, gps) == false) {
948  continue;
949  }
950 
951  if (gps->flag & GP_STROKE_SELECT) {
952  if (gps->totpoints == 1) {
953  /* Special Case: If there's just a single point in this stroke... */
954  bGPDstroke *gpsd;
955 
956  /* make direct copies of the stroke and its points */
957  gpsd = BKE_gpencil_stroke_duplicate(gps, true, true);
958 
959  BLI_strncpy(
960  gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
961 
962  /* Initialize triangle information. */
964 
965  /* add to temp buffer */
966  gpsd->next = gpsd->prev = NULL;
967  BLI_addtail(&new_strokes, gpsd);
968  }
969  else {
970  /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
971  gpencil_duplicate_points(gpd, gps, &new_strokes, gpl->info);
972  }
973 
974  /* deselect original stroke, or else the originals get moved too
975  * (when using the copy + move macro)
976  */
977  bGPDspoint *pt;
978  int i;
979  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
980  pt->flag &= ~GP_SPOINT_SELECT;
981  }
982  gps->flag &= ~GP_STROKE_SELECT;
984 
985  changed = true;
986  }
987  }
988 
989  /* add all new strokes in temp buffer to the frame (preventing double-copies) */
990  BLI_movelisttolist(&gpf->strokes, &new_strokes);
991  BLI_assert(new_strokes.first == NULL);
992  }
993  CTX_DATA_END;
994  }
995 
996  if (changed) {
997  /* updates */
1000  return OPERATOR_FINISHED;
1001  }
1002 
1003  return OPERATOR_CANCELLED;
1004 }
1005 
1007 {
1008  /* identifiers */
1009  ot->name = "Duplicate Strokes";
1010  ot->idname = "GPENCIL_OT_duplicate";
1011  ot->description = "Duplicate the selected Grease Pencil strokes";
1012 
1013  /* callbacks */
1016 
1017  /* flags */
1019 }
1020 
1023 /* -------------------------------------------------------------------- */
1027 /* helper to copy a point to temp area */
1029  bGPDspoint *temp_points,
1030  MDeformVert *temp_dverts,
1031  int from_idx,
1032  int to_idx,
1033  const bool copy)
1034 {
1035  bGPDspoint *pt = &temp_points[from_idx];
1036  bGPDspoint *pt_final = &gps->points[to_idx];
1037 
1038  copy_v3_v3(&pt_final->x, &pt->x);
1039  pt_final->pressure = pt->pressure;
1040  pt_final->strength = pt->strength;
1041  pt_final->time = pt->time;
1042  pt_final->flag = pt->flag;
1043  pt_final->uv_fac = pt->uv_fac;
1044  pt_final->uv_rot = pt->uv_rot;
1045  copy_v4_v4(pt_final->vert_color, pt->vert_color);
1046 
1047  if (gps->dvert != NULL) {
1048  MDeformVert *dvert = &temp_dverts[from_idx];
1049  MDeformVert *dvert_final = &gps->dvert[to_idx];
1050 
1051  dvert_final->totweight = dvert->totweight;
1052  /* if copy, duplicate memory, otherwise move only the pointer */
1053  if (copy) {
1054  dvert_final->dw = MEM_dupallocN(dvert->dw);
1055  }
1056  else {
1057  dvert_final->dw = dvert->dw;
1058  }
1059  }
1060 }
1061 
1063 {
1064  bGPDspoint *temp_points = NULL;
1065  MDeformVert *temp_dverts = NULL;
1066  bGPDspoint *pt = NULL;
1067  const bGPDspoint *pt_start = &gps->points[0];
1068  const bGPDspoint *pt_last = &gps->points[gps->totpoints - 1];
1069  const bool do_first = (pt_start->flag & GP_SPOINT_SELECT);
1070  const bool do_last = ((pt_last->flag & GP_SPOINT_SELECT) && (pt_start != pt_last));
1071  const bool do_stroke = (do_first || do_last);
1072 
1073  /* review points in the middle of stroke to create new strokes */
1074  for (int i = 0; i < gps->totpoints; i++) {
1075  /* skip first and last point */
1076  if (ELEM(i, 0, gps->totpoints - 1)) {
1077  continue;
1078  }
1079 
1080  pt = &gps->points[i];
1081  if (pt->flag == GP_SPOINT_SELECT) {
1082  /* duplicate original stroke data */
1083  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false, true);
1084  gps_new->prev = gps_new->next = NULL;
1085 
1086  /* add new points array */
1087  gps_new->totpoints = 1;
1088  gps_new->points = MEM_callocN(sizeof(bGPDspoint), __func__);
1089  gps_new->dvert = NULL;
1090 
1091  if (gps->dvert != NULL) {
1092  gps_new->dvert = MEM_callocN(sizeof(MDeformVert), __func__);
1093  }
1094 
1095  BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
1096 
1097  /* copy selected point data to new stroke */
1098  gpencil_copy_move_point(gps_new, gps->points, gps->dvert, i, 0, true);
1099 
1100  /* Calc geometry data. */
1102  BKE_gpencil_stroke_geometry_update(gpd, gps_new);
1103 
1104  /* Deselect original point. */
1105  pt->flag &= ~GP_SPOINT_SELECT;
1106  }
1107  }
1108 
1109  /* review first and last point to reuse same stroke */
1110  int i2 = 0;
1111  int totnewpoints, oldtotpoints;
1112  /* if first or last, reuse stroke and resize */
1113  if ((do_first) || (do_last)) {
1114  totnewpoints = gps->totpoints;
1115  if (do_first) {
1116  totnewpoints++;
1117  }
1118  if (do_last) {
1119  totnewpoints++;
1120  }
1121 
1122  /* duplicate points in a temp area */
1123  temp_points = MEM_dupallocN(gps->points);
1124  oldtotpoints = gps->totpoints;
1125  if (gps->dvert != NULL) {
1126  temp_dverts = MEM_dupallocN(gps->dvert);
1127  }
1128 
1129  /* if first point, need move all one position */
1130  if (do_first) {
1131  i2 = 1;
1132  }
1133 
1134  /* resize the points arrays */
1135  gps->totpoints = totnewpoints;
1136  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
1137  if (gps->dvert != NULL) {
1138  gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
1139  }
1140 
1141  /* move points to new position */
1142  for (int i = 0; i < oldtotpoints; i++) {
1143  gpencil_copy_move_point(gps, temp_points, temp_dverts, i, i2, false);
1144  i2++;
1145  }
1146 
1147  /* If first point, add new point at the beginning. */
1148  if (do_first) {
1149  gpencil_copy_move_point(gps, temp_points, temp_dverts, 0, 0, true);
1150  /* deselect old */
1151  pt = &gps->points[1];
1152  pt->flag &= ~GP_SPOINT_SELECT;
1153  /* select new */
1154  pt = &gps->points[0];
1155  pt->flag |= GP_SPOINT_SELECT;
1156  }
1157 
1158  /* if last point, add new point at the end */
1159  if (do_last) {
1161  gps, temp_points, temp_dverts, oldtotpoints - 1, gps->totpoints - 1, true);
1162 
1163  /* deselect old */
1164  pt = &gps->points[gps->totpoints - 2];
1165  pt->flag &= ~GP_SPOINT_SELECT;
1166  /* select new */
1167  pt = &gps->points[gps->totpoints - 1];
1168  pt->flag |= GP_SPOINT_SELECT;
1169  }
1170 
1171  /* Flip stroke if it was only one point to consider extrude point as last point. */
1172  if (gps->totpoints == 2) {
1174  }
1175 
1176  /* Calc geometry data. */
1178 
1179  MEM_SAFE_FREE(temp_points);
1180  MEM_SAFE_FREE(temp_dverts);
1181  }
1182 
1183  /* if the stroke is not reused, deselect */
1184  if (!do_stroke) {
1185  gps->flag &= ~GP_STROKE_SELECT;
1187  }
1188 }
1189 
1191  bGPDframe *gpf,
1192  bGPDstroke *gps,
1193  bGPDcurve *gpc)
1194 {
1195  const int old_num_points = gpc->tot_curve_points;
1196  const bool first_select = gpc->curve_points[0].flag & GP_CURVE_POINT_SELECT;
1197  bool last_select = gpc->curve_points[old_num_points - 1].flag & GP_CURVE_POINT_SELECT;
1198 
1199  /* iterate over middle points */
1200  for (int i = 1; i < gpc->tot_curve_points - 1; i++) {
1201  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1202 
1203  /* Create new stroke if selected point */
1204  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1205  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(gps, false, false);
1206  gps_new->points = NULL;
1207  gps_new->flag &= ~GP_STROKE_CYCLIC;
1208  gps_new->prev = gps_new->next = NULL;
1209 
1211  bGPDcurve *new_gpc = gps_new->editcurve;
1212  for (int j = 0; j < new_gpc->tot_curve_points; j++) {
1213  bGPDcurve_point *gpc_pt_new = &new_gpc->curve_points[j];
1214  memcpy(gpc_pt_new, gpc_pt, sizeof(bGPDcurve_point));
1215  gpc_pt_new->flag &= ~GP_CURVE_POINT_SELECT;
1216  BEZT_DESEL_ALL(&gpc_pt_new->bezt);
1217  }
1218 
1219  /* select last point */
1220  bGPDcurve_point *gpc_pt_last = &new_gpc->curve_points[1];
1221  gpc_pt_last->flag |= GP_CURVE_POINT_SELECT;
1222  BEZT_SEL_IDX(&gpc_pt_last->bezt, 1);
1223  gps_new->editcurve->flag |= GP_CURVE_SELECT;
1224 
1225  BLI_insertlinkafter(&gpf->strokes, gps, gps_new);
1226 
1227  gps_new->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
1228  BKE_gpencil_stroke_geometry_update(gpd, gps_new);
1229 
1230  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1231  BEZT_DESEL_ALL(&gpc_pt->bezt);
1232  }
1233  }
1234 
1235  /* Edge-case for single curve point. */
1236  if (gpc->tot_curve_points == 1) {
1237  last_select = false;
1238  }
1239 
1240  if (first_select || last_select) {
1241  int new_num_points = old_num_points;
1242 
1243  if (first_select) {
1244  new_num_points++;
1245  }
1246  if (last_select) {
1247  new_num_points++;
1248  }
1249 
1250  /* Grow the array */
1251  gpc->tot_curve_points = new_num_points;
1252  gpc->curve_points = MEM_recallocN(gpc->curve_points, sizeof(bGPDcurve_point) * new_num_points);
1253 
1254  if (first_select) {
1255  /* shift points by one */
1256  memmove(
1257  &gpc->curve_points[1], &gpc->curve_points[0], sizeof(bGPDcurve_point) * old_num_points);
1258 
1259  bGPDcurve_point *old_first = &gpc->curve_points[1];
1260 
1261  old_first->flag &= ~GP_CURVE_POINT_SELECT;
1262  BEZT_DESEL_ALL(&old_first->bezt);
1263  }
1264 
1265  if (last_select) {
1266  bGPDcurve_point *old_last = &gpc->curve_points[gpc->tot_curve_points - 2];
1267  bGPDcurve_point *new_last = &gpc->curve_points[gpc->tot_curve_points - 1];
1268  memcpy(new_last, old_last, sizeof(bGPDcurve_point));
1269 
1270  old_last->flag &= ~GP_CURVE_POINT_SELECT;
1271  BEZT_DESEL_ALL(&old_last->bezt);
1272  }
1273 
1276  }
1277 }
1278 
1280 {
1281  Object *obact = CTX_data_active_object(C);
1282  bGPdata *gpd = (bGPdata *)obact->data;
1283  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1284  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1285  bGPDstroke *gps = NULL;
1286 
1287  if (gpd == NULL) {
1288  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
1289  return OPERATOR_CANCELLED;
1290  }
1291 
1292  bool changed = false;
1293  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1294  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
1295 
1296  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
1297  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1298  if (gpf == NULL) {
1299  continue;
1300  }
1301 
1302  for (gps = gpf->strokes.first; gps; gps = gps->next) {
1303  /* skip strokes that are invalid for current view */
1304  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1305  continue;
1306  }
1307 
1308  if (is_curve_edit) {
1309  if (gps->editcurve == NULL) {
1310  continue;
1311  }
1312  bGPDcurve *gpc = gps->editcurve;
1313  if (gpc->flag & GP_CURVE_SELECT) {
1314  gpencil_curve_extrude_points(gpd, gpf, gps, gpc);
1315  }
1316  }
1317  else {
1318  if (gps->flag & GP_STROKE_SELECT) {
1319  gpencil_add_move_points(gpd, gpf, gps);
1320  }
1321  }
1322 
1323  changed = true;
1324  }
1325  /* If not multi-edit, exit loop. */
1326  if (!is_multiedit) {
1327  break;
1328  }
1329  }
1330  }
1331  }
1332  CTX_DATA_END;
1333 
1334  if (changed) {
1335  /* updates */
1336  DEG_id_tag_update(&gpd->id,
1340  }
1341 
1342  return OPERATOR_FINISHED;
1343 }
1344 
1346 {
1347  /* identifiers */
1348  ot->name = "Extrude Stroke Points";
1349  ot->idname = "GPENCIL_OT_extrude";
1350  ot->description = "Extrude the selected Grease Pencil points";
1351 
1352  /* callbacks */
1355 
1356  /* flags */
1358 }
1359 
1362 /* -------------------------------------------------------------------- */
1374 
1375 /* Hash for hanging on to all the colors used by strokes in the buffer
1376  *
1377  * This is needed to prevent dangling and unsafe pointers when pasting across data-blocks,
1378  * or after a color used by a stroke in the buffer gets deleted (via user action or undo).
1379  */
1381 
1383 {
1384  GHash *ma_to_name = BLI_ghash_ptr_new(__func__);
1385 
1386  for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) {
1387  char *name = BKE_id_to_unique_string_key(&ma->id);
1388  BLI_ghash_insert(ma_to_name, ma, name);
1389  }
1390 
1391  return ma_to_name;
1392 }
1393 
1395 {
1396  BLI_ghash_free(ma_to_name, NULL, MEM_freeN);
1397 }
1398 
1400 {
1401  GHash *name_to_ma = BLI_ghash_str_new(__func__);
1402 
1403  for (Material *ma = bmain->materials.first; ma != NULL; ma = ma->id.next) {
1404  char *name = BKE_id_to_unique_string_key(&ma->id);
1405  BLI_ghash_insert(name_to_ma, name, ma);
1406  }
1407 
1408  return name_to_ma;
1409 }
1410 
1412 {
1413  BLI_ghash_free(name_to_ma, MEM_freeN, NULL);
1414 }
1415 
1417 {
1418  bGPDstroke *gps, *gpsn;
1419 
1420  /* Free the colors buffer
1421  * NOTE: This is done before the strokes so that the ptrs are still safe
1422  */
1426  }
1427 
1428  /* Free the stroke buffer */
1429  for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gpsn) {
1430  gpsn = gps->next;
1431 
1432  if (gps->points) {
1433  MEM_freeN(gps->points);
1434  }
1435  if (gps->dvert) {
1437  MEM_freeN(gps->dvert);
1438  }
1439 
1440  MEM_SAFE_FREE(gps->triangles);
1441 
1443  }
1444 
1446 }
1447 
1449 {
1450  Main *bmain = CTX_data_main(C);
1452  GHash *new_colors = BLI_ghash_int_new("GPencil Paste Dst Colors");
1453  GHashIterator gh_iter;
1454 
1455  /* For each color, check if exist and add if not */
1457 
1459  int *key = BLI_ghashIterator_getKey(&gh_iter);
1460  char *ma_name = BLI_ghashIterator_getValue(&gh_iter);
1461  Material *ma = BLI_ghash_lookup(name_to_ma, ma_name);
1462 
1463  BKE_gpencil_object_material_ensure(bmain, ob, ma);
1464 
1465  /* Store this mapping (for use later when pasting) */
1466  if (!BLI_ghash_haskey(new_colors, POINTER_FROM_INT(*key))) {
1467  BLI_ghash_insert(new_colors, POINTER_FROM_INT(*key), ma);
1468  }
1469  }
1470 
1472 
1473  return new_colors;
1474 }
1475 
1478 /* -------------------------------------------------------------------- */
1483 {
1484  Main *bmain = CTX_data_main(C);
1487  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1488 
1489  if (gpd == NULL) {
1490  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
1491  return OPERATOR_CANCELLED;
1492  }
1493 
1494  if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1495  BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1496  return OPERATOR_CANCELLED;
1497  }
1498 
1499  /* clear the buffer first */
1501 
1502  if (is_curve_edit) {
1503  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
1504  }
1505  else {
1506  /* for each visible (and editable) layer's selected strokes,
1507  * copy the strokes into a temporary buffer, then append
1508  * once all done
1509  */
1510  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1511  bGPDframe *gpf = gpl->actframe;
1512  bGPDstroke *gps;
1513 
1514  if (gpf == NULL) {
1515  continue;
1516  }
1517 
1518  /* make copies of selected strokes, and deselect these once we're done */
1519  for (gps = gpf->strokes.first; gps; gps = gps->next) {
1520  /* skip strokes that are invalid for current view */
1521  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1522  continue;
1523  }
1524 
1525  if (gps->flag & GP_STROKE_SELECT) {
1526  if (gps->totpoints == 1) {
1527  /* Special Case: If there's just a single point in this stroke... */
1528  bGPDstroke *gpsd;
1529 
1530  /* make direct copies of the stroke and its points */
1531  gpsd = BKE_gpencil_stroke_duplicate(gps, false, true);
1532 
1533  /* saves original layer name */
1534  BLI_strncpy(
1535  gpsd->runtime.tmp_layerinfo, gpl->info, sizeof(gpsd->runtime.tmp_layerinfo));
1536  gpsd->points = MEM_dupallocN(gps->points);
1537  if (gps->dvert != NULL) {
1538  gpsd->dvert = MEM_dupallocN(gps->dvert);
1540  }
1541 
1542  /* Calc geometry data. */
1544 
1545  /* add to temp buffer */
1546  gpsd->next = gpsd->prev = NULL;
1548  }
1549  else {
1550  /* delegate to a helper, as there's too much to fit in here (for copying subsets)... */
1552  }
1553  }
1554  }
1555  }
1556  CTX_DATA_END;
1557  }
1558 
1559  /* Build up hash of material colors used in these strokes */
1561  gpencil_strokes_copypastebuf_colors = BLI_ghash_int_new("GPencil CopyBuf Colors");
1564  if (ED_gpencil_stroke_can_use(C, gps)) {
1565  Material *ma = BKE_object_material_get(ob, gps->mat_nr + 1);
1566  /* Avoid default material. */
1567  if (ma == NULL) {
1568  continue;
1569  }
1570 
1571  char **ma_name_val;
1572  if (!BLI_ghash_ensure_p(
1573  gpencil_strokes_copypastebuf_colors, &gps->mat_nr, (void ***)&ma_name_val)) {
1574  char *ma_name = BLI_ghash_lookup(ma_to_name, ma);
1575  *ma_name_val = MEM_dupallocN(ma_name);
1576  }
1577  }
1578  }
1580  }
1581 
1582  /* updates (to ensure operator buttons are refreshed, when used via hotkeys) */
1583  WM_event_add_notifier(C, NC_GPENCIL | ND_DATA, NULL); /* XXX? */
1584 
1585  /* done */
1586  return OPERATOR_FINISHED;
1587 }
1588 
1590 {
1591  /* identifiers */
1592  ot->name = "Copy Strokes";
1593  ot->idname = "GPENCIL_OT_copy";
1594  ot->description = "Copy selected Grease Pencil points and strokes";
1595 
1596  /* callbacks */
1599 
1600  /* flags */
1601  // ot->flag = OPTYPE_REGISTER;
1602 }
1603 
1606 /* -------------------------------------------------------------------- */
1611 {
1612  ScrArea *area = CTX_wm_area(C);
1613  if (!((area != NULL) && (area->spacetype == SPACE_VIEW3D))) {
1614  return false;
1615  }
1616  /* 1) Must have GP datablock to paste to
1617  * - We don't need to have an active layer though, as that can easily get added
1618  * - If the active layer is locked, we can't paste there,
1619  * but that should prompt a warning instead.
1620  * 2) Copy buffer must at least have something (though it may be the wrong sort...).
1621  */
1622  return (ED_gpencil_data_get_active(C) != NULL) &&
1624 }
1625 
1626 typedef enum eGP_PasteMode {
1630 
1632 {
1634  bGPdata *gpd = (bGPdata *)ob->data;
1635  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1636  bGPDlayer *gpl = BKE_gpencil_layer_active_get(gpd); /* only use active for copy merge */
1638  bGPDframe *gpf;
1639 
1640  eGP_PasteMode type = RNA_enum_get(op->ptr, "type");
1641  const bool on_back = RNA_boolean_get(op->ptr, "paste_back");
1642  GHash *new_colors;
1643 
1644  /* Check for various error conditions. */
1645  if (GPENCIL_MULTIEDIT_SESSIONS_ON(gpd)) {
1646  BKE_report(op->reports, RPT_ERROR, "Operator not supported in multiframe edition");
1647  return OPERATOR_CANCELLED;
1648  }
1649 
1651  BKE_report(op->reports,
1652  RPT_ERROR,
1653  "No strokes to paste, select and copy some points before trying again");
1654  return OPERATOR_CANCELLED;
1655  }
1656 
1657  if (gpl == NULL) {
1658  /* no active layer - let's just create one */
1659  gpl = BKE_gpencil_layer_addnew(gpd, DATA_("GP_Layer"), true, false);
1660  }
1661  else if ((BKE_gpencil_layer_is_editable(gpl) == false) && (type == GP_COPY_TO_ACTIVE)) {
1662  BKE_report(
1663  op->reports, RPT_ERROR, "Can not paste strokes when active layer is hidden or locked");
1664  return OPERATOR_CANCELLED;
1665  }
1666  else {
1667  /* Check that some of the strokes in the buffer can be used */
1668  bGPDstroke *gps;
1669  bool ok = false;
1670 
1671  for (gps = gpencil_strokes_copypastebuf.first; gps; gps = gps->next) {
1672  if (ED_gpencil_stroke_can_use(C, gps)) {
1673  ok = true;
1674  break;
1675  }
1676  }
1677 
1678  if (ok == false) {
1679  return OPERATOR_CANCELLED;
1680  }
1681  }
1682 
1683  /* Deselect all strokes first */
1684  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
1685  bGPDspoint *pt;
1686  int i;
1687 
1688  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1689  pt->flag &= ~GP_SPOINT_SELECT;
1690  }
1691 
1692  gps->flag &= ~GP_STROKE_SELECT;
1694  }
1695  CTX_DATA_END;
1696 
1697  /* Ensure that all the necessary colors exist */
1698  new_colors = gpencil_copybuf_validate_colormap(C);
1699 
1700  if (is_curve_edit) {
1701  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
1702  }
1703  else {
1704  /* Copy over the strokes from the buffer (and adjust the colors) */
1705  bGPDstroke *gps_init = (!on_back) ? gpencil_strokes_copypastebuf.first :
1707  for (bGPDstroke *gps = gps_init; gps; gps = (!on_back) ? gps->next : gps->prev) {
1708  if (ED_gpencil_stroke_can_use(C, gps)) {
1709  /* Need to verify if layer exists */
1710  if (type != GP_COPY_TO_ACTIVE) {
1711  gpl = BLI_findstring(
1712  &gpd->layers, gps->runtime.tmp_layerinfo, offsetof(bGPDlayer, info));
1713  if (gpl == NULL) {
1714  /* no layer - use active (only if layer deleted before paste) */
1715  gpl = BKE_gpencil_layer_active_get(gpd);
1716  }
1717  }
1718 
1719  /* Ensure we have a frame to draw into
1720  * NOTE: Since this is an op which creates strokes,
1721  * we resuse active frame or add a new frame if one
1722  * doesn't exist already depending on REC button status.
1723  */
1724  if (IS_AUTOKEY_ON(scene) || (gpl->actframe == NULL)) {
1726  }
1727  else {
1729  }
1730  if (gpf) {
1731  /* Create new stroke */
1732  bGPDstroke *new_stroke = BKE_gpencil_stroke_duplicate(gps, true, true);
1733  new_stroke->runtime.tmp_layerinfo[0] = '\0';
1734  new_stroke->next = new_stroke->prev = NULL;
1735 
1736  /* Calc geometry data. */
1737  BKE_gpencil_stroke_geometry_update(gpd, new_stroke);
1738 
1739  if (on_back) {
1740  BLI_addhead(&gpf->strokes, new_stroke);
1741  }
1742  else {
1743  BLI_addtail(&gpf->strokes, new_stroke);
1744  }
1745 
1746  /* Remap material */
1747  Material *ma = BLI_ghash_lookup(new_colors, POINTER_FROM_INT(new_stroke->mat_nr));
1748  new_stroke->mat_nr = BKE_gpencil_object_material_index_get(ob, ma);
1749  CLAMP_MIN(new_stroke->mat_nr, 0);
1750  }
1751  }
1752  }
1753  }
1754 
1755  /* free temp data */
1756  BLI_ghash_free(new_colors, NULL, NULL);
1757 
1758  /* updates */
1761 
1762  return OPERATOR_FINISHED;
1763 }
1764 
1766 {
1767  PropertyRNA *prop;
1768 
1769  static const EnumPropertyItem copy_type[] = {
1770  {GP_COPY_TO_ACTIVE, "ACTIVE", 0, "Paste to Active", ""},
1771  {GP_COPY_BY_LAYER, "LAYER", 0, "Paste by Layer", ""},
1772  {0, NULL, 0, NULL, NULL},
1773  };
1774 
1775  /* identifiers */
1776  ot->name = "Paste Strokes";
1777  ot->idname = "GPENCIL_OT_paste";
1778  ot->description = "Paste previously copied strokes to active layer or to original layer";
1779 
1780  /* callbacks */
1783 
1784  /* flags */
1786 
1787  /* properties */
1788  ot->prop = RNA_def_enum(ot->srna, "type", copy_type, GP_COPY_TO_ACTIVE, "Type", "");
1789 
1790  prop = RNA_def_boolean(
1791  ot->srna, "paste_back", 0, "Paste on Back", "Add pasted strokes behind all strokes");
1793 }
1794 
1797 /* -------------------------------------------------------------------- */
1802 {
1804  bGPdata *gpd = (bGPdata *)ob->data;
1805  bGPDlayer *target_layer = NULL;
1806  ListBase strokes = {NULL, NULL};
1807  int layer_num = RNA_int_get(op->ptr, "layer");
1808  const bool use_autolock = (bool)(gpd->flag & GP_DATA_AUTOLOCK_LAYERS);
1809  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
1810 
1811  /* If autolock enabled, disabled now. */
1812  if (use_autolock) {
1813  gpd->flag &= ~GP_DATA_AUTOLOCK_LAYERS;
1814  }
1815 
1816  /* Try to get layer */
1817  if (layer_num > -1) {
1818  target_layer = BLI_findlink(&gpd->layers, layer_num);
1819  }
1820  else {
1821  /* Create a new layer. */
1822  PropertyRNA *prop;
1823  char name[128];
1824  prop = RNA_struct_find_property(op->ptr, "new_layer_name");
1825  if (RNA_property_is_set(op->ptr, prop)) {
1826  RNA_property_string_get(op->ptr, prop, name);
1827  }
1828  else {
1829  strcpy(name, "GP_Layer");
1830  }
1831  target_layer = BKE_gpencil_layer_addnew(gpd, name, true, false);
1832  }
1833 
1834  if (target_layer == NULL) {
1835  /* back autolock status */
1836  if (use_autolock) {
1837  gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
1838  }
1839  BKE_reportf(op->reports, RPT_ERROR, "There is no layer number %d", layer_num);
1840  return OPERATOR_CANCELLED;
1841  }
1842 
1843  /* Extract all strokes to move to this layer. */
1844  CTX_DATA_BEGIN (C, bGPDlayer *, gpl_src, editable_gpencil_layers) {
1845  /* Skip if this is the layer we're moving strokes to. */
1846  if (gpl_src == target_layer) {
1847  continue;
1848  }
1849  bGPDframe *init_gpf = (is_multiedit) ? gpl_src->frames.first : gpl_src->actframe;
1850  for (bGPDframe *gpf_src = init_gpf; gpf_src; gpf_src = gpf_src->next) {
1851  if ((gpf_src == gpl_src->actframe) ||
1852  ((gpf_src->flag & GP_FRAME_SELECT) && (is_multiedit))) {
1853  if (gpf_src == NULL) {
1854  continue;
1855  }
1856 
1857  bGPDstroke *gpsn = NULL;
1858  BLI_listbase_clear(&strokes);
1859  for (bGPDstroke *gps = gpf_src->strokes.first; gps; gps = gpsn) {
1860  gpsn = gps->next;
1861  /* Skip strokes that are invalid for current view. */
1862  if (ED_gpencil_stroke_can_use(C, gps) == false) {
1863  continue;
1864  }
1865  /* Check if the color is editable. */
1866  if (ED_gpencil_stroke_material_editable(ob, gpl_src, gps) == false) {
1867  continue;
1868  }
1869 
1870  if (gps->flag & GP_STROKE_SELECT) {
1871  BLI_remlink(&gpf_src->strokes, gps);
1872  BLI_addtail(&strokes, gps);
1873  }
1874  }
1875  /* Paste them all in one go. */
1876  if (strokes.first) {
1878  target_layer, gpf_src->framenum, GP_GETFRAME_ADD_NEW);
1879 
1880  BLI_movelisttolist(&gpf_dst->strokes, &strokes);
1881  BLI_assert((strokes.first == strokes.last) && (strokes.first == NULL));
1882  }
1883  }
1884  /* If not multi-edit, exit loop. */
1885  if (!is_multiedit) {
1886  break;
1887  }
1888  }
1889  /* If new layer and autolock, lock old layer. */
1890  if ((layer_num == -1) && (use_autolock)) {
1891  gpl_src->flag |= GP_LAYER_LOCKED;
1892  }
1893  }
1894  CTX_DATA_END;
1895 
1896  /* back autolock status */
1897  if (use_autolock) {
1898  gpd->flag |= GP_DATA_AUTOLOCK_LAYERS;
1899  }
1900 
1901  /* updates */
1904 
1905  return OPERATOR_FINISHED;
1906 }
1907 
1908 static void layer_new_name_get(bGPdata *gpd, char *rname)
1909 {
1910  int index = 0;
1911  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
1912  if (strstr(gpl->info, "GP_Layer")) {
1913  index++;
1914  }
1915  }
1916 
1917  if (index == 0) {
1918  BLI_strncpy(rname, "GP_Layer", 128);
1919  return;
1920  }
1921  char *name = BLI_sprintfN("%.*s.%03d", 128, "GP_Layer", index);
1922  BLI_strncpy(rname, name, 128);
1923  MEM_freeN(name);
1924 }
1925 
1927 {
1929  PropertyRNA *prop;
1930  if (RNA_int_get(op->ptr, "layer") == -1) {
1931  prop = RNA_struct_find_property(op->ptr, "new_layer_name");
1932  if (!RNA_property_is_set(op->ptr, prop)) {
1933  char name[MAX_NAME];
1934  bGPdata *gpd = ob->data;
1935  layer_new_name_get(gpd, name);
1936  RNA_property_string_set(op->ptr, prop, name);
1937  return WM_operator_props_dialog_popup(C, op, 200);
1938  }
1939  }
1940 
1941  return gpencil_move_to_layer_exec(C, op);
1942 }
1943 
1945 {
1946  PropertyRNA *prop;
1947 
1948  /* identifiers */
1949  ot->name = "Move Strokes to Layer";
1950  ot->idname = "GPENCIL_OT_move_to_layer";
1951  ot->description =
1952  "Move selected strokes to another layer"; /* XXX: allow moving individual points too? */
1953 
1954  /* callbacks */
1958 
1959  /* flags */
1961 
1962  /* GPencil layer to use. */
1963  prop = RNA_def_int(ot->srna, "layer", 0, -1, INT_MAX, "Grease Pencil Layer", "", -1, INT_MAX);
1965 
1966  prop = RNA_def_string(
1967  ot->srna, "new_layer_name", NULL, MAX_NAME, "Name", "Name of the newly added layer");
1969  ot->prop = prop;
1970 }
1971 
1974 /* -------------------------------------------------------------------- */
1979 {
1982  int cfra = scene->r.cfra;
1983 
1984  bGPDlayer *active_gpl = BKE_gpencil_layer_active_get(gpd);
1985 
1986  const bool all_layers = RNA_boolean_get(op->ptr, "all_layers");
1987 
1988  /* Initialize data-block and an active layer if nothing exists yet. */
1989  if (ELEM(NULL, gpd, active_gpl)) {
1990  /* Let's just be lazy, and call the "Add New Layer" operator,
1991  * which sets everything up as required. */
1992  WM_operator_name_call(C, "GPENCIL_OT_layer_add", WM_OP_EXEC_DEFAULT, NULL, NULL);
1993  }
1994 
1995  /* Go through each layer, adding a frame after the active one
1996  * and/or shunting all the others out of the way
1997  */
1998  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
1999  if ((all_layers == false) && (gpl != active_gpl)) {
2000  continue;
2001  }
2002 
2003  /* 1) Check for an existing frame on the current frame */
2004  bGPDframe *gpf = BKE_gpencil_layer_frame_find(gpl, cfra);
2005  if (gpf) {
2006  /* Shunt all frames after (and including) the existing one later by 1-frame */
2007  for (; gpf; gpf = gpf->next) {
2008  gpf->framenum += 1;
2009  }
2010  }
2011 
2012  /* 2) Now add a new frame, with nothing in it */
2013  gpl->actframe = BKE_gpencil_layer_frame_get(gpl, cfra, GP_GETFRAME_ADD_NEW);
2014  }
2015  CTX_DATA_END;
2016 
2017  /* notifiers */
2020 
2021  return OPERATOR_FINISHED;
2022 }
2023 
2025 {
2026  PropertyRNA *prop;
2027 
2028  /* identifiers */
2029  ot->name = "Insert Blank Frame";
2030  ot->idname = "GPENCIL_OT_blank_frame_add";
2031  ot->description =
2032  "Insert a blank frame on the current frame "
2033  "(all subsequently existing frames, if any, are shifted right by one frame)";
2034 
2035  /* callbacks */
2038 
2040 
2041  /* properties */
2042  prop = RNA_def_boolean(ot->srna,
2043  "all_layers",
2044  false,
2045  "All Layers",
2046  "Create blank frame in all layers, not only active");
2048 }
2049 
2052 /* -------------------------------------------------------------------- */
2057 {
2060 
2061  /* only if there's an active layer with an active frame */
2062  return (gpl && gpl->actframe);
2063 }
2064 
2066 {
2069 
2070  /* only if there's an active layer with an active frame */
2071  return (gpl && gpl->actframe);
2072 }
2073 
2074 /* delete active frame - wrapper around API calls */
2076 {
2077  const bool is_annotation = STREQ(op->idname, "GPENCIL_OT_annotation_active_frame_delete");
2078 
2079  bGPdata *gpd = (!is_annotation) ? ED_gpencil_data_get_active(C) :
2081 
2083 
2085 
2087 
2088  /* if there's no existing Grease-Pencil data there, add some */
2089  if (gpd == NULL) {
2090  BKE_report(op->reports, RPT_ERROR, "No grease pencil data");
2091  return OPERATOR_CANCELLED;
2092  }
2093  if (ELEM(NULL, gpl, gpf)) {
2094  BKE_report(op->reports, RPT_ERROR, "No active frame to delete");
2095  return OPERATOR_CANCELLED;
2096  }
2097 
2098  /* delete it... */
2100 
2101  /* notifiers */
2104 
2105  return OPERATOR_FINISHED;
2106 }
2107 
2109 {
2110  /* identifiers */
2111  ot->name = "Delete Active Frame";
2112  ot->idname = "GPENCIL_OT_active_frame_delete";
2113  ot->description = "Delete the active frame for the active Grease Pencil Layer";
2114 
2116 
2117  /* callbacks */
2120 }
2121 
2123 {
2124  /* identifiers */
2125  ot->name = "Delete Active Frame";
2126  ot->idname = "GPENCIL_OT_annotation_active_frame_delete";
2127  ot->description = "Delete the active frame for the active Annotation Layer";
2128 
2130 
2131  /* callbacks */
2134 }
2135 
2138 /* -------------------------------------------------------------------- */
2143 {
2145 
2146  /* 1) There must be grease pencil data
2147  * 2) Hopefully some of the layers have stuff we can use
2148  */
2149  return (gpd && gpd->layers.first);
2150 }
2151 
2153 {
2156 
2157  bool success = false;
2158 
2159  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
2160  /* try to get the "active" frame - but only if it actually occurs on this frame */
2162 
2163  if (gpf == NULL) {
2164  continue;
2165  }
2166 
2167  /* delete it... */
2169 
2170  /* we successfully modified something */
2171  success = true;
2172  }
2173  CTX_DATA_END;
2174 
2175  /* updates */
2176  if (success) {
2179  return OPERATOR_FINISHED;
2180  }
2181  BKE_report(op->reports, RPT_ERROR, "No active frame(s) to delete");
2182  return OPERATOR_CANCELLED;
2183 }
2184 
2186 {
2187  /* identifiers */
2188  ot->name = "Delete All Active Frames";
2189  ot->idname = "GPENCIL_OT_active_frames_delete_all";
2190  ot->description = "Delete the active frame(s) of all editable Grease Pencil layers";
2191 
2193 
2194  /* callbacks */
2197 }
2198 
2201 /* -------------------------------------------------------------------- */
2205 typedef enum eGP_DeleteMode {
2206  /* delete selected stroke points */
2208  /* delete selected strokes */
2210  /* delete active frame */
2213 
2214 typedef enum eGP_DissolveMode {
2215  /* dissolve all selected points */
2217  /* dissolve between selected points */
2219  /* dissolve unselected points */
2222 
2223 /* Delete selected strokes */
2225 {
2227  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
2228 
2229  bool changed = false;
2230  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
2231  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
2232 
2233  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
2234  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
2235 
2236  if (gpf == NULL) {
2237  continue;
2238  }
2239 
2240  /* simply delete strokes which are selected */
2241  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
2242 
2243  /* skip strokes that are invalid for current view */
2244  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2245  continue;
2246  }
2247 
2248  /* free stroke if selected */
2249  if (gps->flag & GP_STROKE_SELECT) {
2250  BLI_remlink(&gpf->strokes, gps);
2251  /* free stroke memory arrays, then stroke itself */
2253 
2254  changed = true;
2255  }
2256  }
2257  }
2258  }
2259  }
2260  CTX_DATA_END;
2261 
2262  if (changed) {
2265  return OPERATOR_FINISHED;
2266  }
2267  return OPERATOR_CANCELLED;
2268 }
2269 
2270 /* ----------------------------------- */
2271 
2273  bGPdata *gpd,
2274  eGP_DissolveMode mode)
2275 {
2276  bool changed = false;
2277  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
2278  {
2279  if (gpc->flag & GP_CURVE_SELECT) {
2280  int first = 0, last = 0;
2281  int num_points_remaining = gpc->tot_curve_points;
2282 
2283  switch (mode) {
2284  case GP_DISSOLVE_POINTS:
2285  for (int i = 0; i < gpc->tot_curve_points; i++) {
2286  bGPDcurve_point *cpt = &gpc->curve_points[i];
2287  if (cpt->flag & GP_CURVE_POINT_SELECT) {
2288  num_points_remaining--;
2289  }
2290  }
2291  break;
2292  case GP_DISSOLVE_BETWEEN:
2293  first = -1;
2294  for (int i = 0; i < gpc->tot_curve_points; i++) {
2295  bGPDcurve_point *cpt = &gpc->curve_points[i];
2296  if (cpt->flag & GP_CURVE_POINT_SELECT) {
2297  if (first < 0) {
2298  first = i;
2299  }
2300  last = i;
2301  }
2302  }
2303 
2304  for (int i = first + 1; i < last; i++) {
2305  bGPDcurve_point *cpt = &gpc->curve_points[i];
2306  if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
2307  num_points_remaining--;
2308  }
2309  }
2310  break;
2311  case GP_DISSOLVE_UNSELECT:
2312  for (int i = 0; i < gpc->tot_curve_points; i++) {
2313  bGPDcurve_point *cpt = &gpc->curve_points[i];
2314  if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
2315  num_points_remaining--;
2316  }
2317  }
2318  break;
2319  default:
2320  return false;
2321  break;
2322  }
2323 
2324  if (num_points_remaining < 1) {
2325  /* Delete stroke */
2326  BLI_remlink(&gpf_->strokes, gps);
2328  }
2329  else {
2330  bGPDcurve_point *new_points = MEM_callocN(sizeof(bGPDcurve_point) * num_points_remaining,
2331  __func__);
2332 
2333  int idx = 0;
2334  switch (mode) {
2335  case GP_DISSOLVE_POINTS:
2336  for (int i = 0; i < gpc->tot_curve_points; i++) {
2337  bGPDcurve_point *cpt = &gpc->curve_points[i];
2338  bGPDcurve_point *new_cpt = &new_points[idx];
2339  if ((cpt->flag & GP_CURVE_POINT_SELECT) == 0) {
2340  *new_cpt = *cpt;
2341  idx++;
2342  }
2343  }
2344  break;
2345  case GP_DISSOLVE_BETWEEN:
2346  for (int i = 0; i < first; i++) {
2347  bGPDcurve_point *cpt = &gpc->curve_points[i];
2348  bGPDcurve_point *new_cpt = &new_points[idx];
2349 
2350  *new_cpt = *cpt;
2351  idx++;
2352  }
2353 
2354  for (int i = first; i < last; i++) {
2355  bGPDcurve_point *cpt = &gpc->curve_points[i];
2356  bGPDcurve_point *new_cpt = &new_points[idx];
2357  if (cpt->flag & GP_CURVE_POINT_SELECT) {
2358  *new_cpt = *cpt;
2359  idx++;
2360  }
2361  }
2362 
2363  for (int i = last; i < gpc->tot_curve_points; i++) {
2364  bGPDcurve_point *cpt = &gpc->curve_points[i];
2365  bGPDcurve_point *new_cpt = &new_points[idx];
2366 
2367  *new_cpt = *cpt;
2368  idx++;
2369  }
2370  break;
2371  case GP_DISSOLVE_UNSELECT:
2372  for (int i = 0; i < gpc->tot_curve_points; i++) {
2373  bGPDcurve_point *cpt = &gpc->curve_points[i];
2374  bGPDcurve_point *new_cpt = &new_points[idx];
2375  if (cpt->flag & GP_CURVE_POINT_SELECT) {
2376  *new_cpt = *cpt;
2377  idx++;
2378  }
2379  }
2380  break;
2381  default:
2382  return false;
2383  break;
2384  }
2385 
2386  if (gpc->curve_points != NULL) {
2387  MEM_freeN(gpc->curve_points);
2388  }
2389 
2390  gpc->curve_points = new_points;
2391  gpc->tot_curve_points = num_points_remaining;
2392 
2394  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
2396  }
2397 
2398  changed = true;
2399  }
2400  }
2401  GP_EDITABLE_CURVES_END(gps_iter);
2402 
2403  return changed;
2404 }
2405 
2407  bGPdata *gpd,
2408  eGP_DissolveMode mode)
2409 {
2410  bool changed = false;
2411  int first = 0;
2412  int last = 0;
2413 
2414  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
2415  /* the stroke must have at least one point selected for any operator */
2416  if (gps->flag & GP_STROKE_SELECT) {
2417  bGPDspoint *pt;
2418  MDeformVert *dvert = NULL;
2419  int i;
2420 
2421  int tot = gps->totpoints; /* number of points in new buffer */
2422 
2423  /* first pass: count points to remove */
2424  switch (mode) {
2425  case GP_DISSOLVE_POINTS:
2426  /* Count how many points are selected (i.e. how many to remove) */
2427  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2428  if (pt->flag & GP_SPOINT_SELECT) {
2429  /* selected point - one of the points to remove */
2430  tot--;
2431  }
2432  }
2433  break;
2434  case GP_DISSOLVE_BETWEEN:
2435  /* need to find first and last point selected */
2436  first = -1;
2437  last = 0;
2438  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2439  if (pt->flag & GP_SPOINT_SELECT) {
2440  if (first < 0) {
2441  first = i;
2442  }
2443  last = i;
2444  }
2445  }
2446  /* count unselected points in the range */
2447  for (i = first, pt = gps->points + first; i < last; i++, pt++) {
2448  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
2449  tot--;
2450  }
2451  }
2452  break;
2453  case GP_DISSOLVE_UNSELECT:
2454  /* count number of unselected points */
2455  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2456  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
2457  tot--;
2458  }
2459  }
2460  break;
2461  default:
2462  return false;
2463  break;
2464  }
2465 
2466  /* if no points are left, we simply delete the entire stroke */
2467  if (tot <= 0) {
2468  /* remove the entire stroke */
2469  BLI_remlink(&gpf_->strokes, gps);
2471  }
2472  else {
2473  /* just copy all points to keep into a smaller buffer */
2474  bGPDspoint *new_points = MEM_callocN(sizeof(bGPDspoint) * tot,
2475  "new gp stroke points copy");
2476  bGPDspoint *npt = new_points;
2477 
2478  MDeformVert *new_dvert = NULL;
2479  MDeformVert *ndvert = NULL;
2480 
2481  if (gps->dvert != NULL) {
2482  new_dvert = MEM_callocN(sizeof(MDeformVert) * tot, "new gp stroke weights copy");
2483  ndvert = new_dvert;
2484  }
2485 
2486  switch (mode) {
2487  case GP_DISSOLVE_POINTS:
2488  (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
2489  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2490  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
2491  *npt = *pt;
2492  npt++;
2493 
2494  if (gps->dvert != NULL) {
2495  *ndvert = *dvert;
2496  ndvert->dw = MEM_dupallocN(dvert->dw);
2497  ndvert++;
2498  }
2499  }
2500  if (gps->dvert != NULL) {
2501  dvert++;
2502  }
2503  }
2504  break;
2505  case GP_DISSOLVE_BETWEEN:
2506  /* copy first segment */
2507  (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
2508  for (i = 0, pt = gps->points; i < first; i++, pt++) {
2509  *npt = *pt;
2510  npt++;
2511 
2512  if (gps->dvert != NULL) {
2513  *ndvert = *dvert;
2514  ndvert->dw = MEM_dupallocN(dvert->dw);
2515  ndvert++;
2516  dvert++;
2517  }
2518  }
2519  /* copy segment (selected points) */
2520  (gps->dvert != NULL) ? dvert = gps->dvert + first : NULL;
2521  for (i = first, pt = gps->points + first; i < last; i++, pt++) {
2522  if (pt->flag & GP_SPOINT_SELECT) {
2523  *npt = *pt;
2524  npt++;
2525 
2526  if (gps->dvert != NULL) {
2527  *ndvert = *dvert;
2528  ndvert->dw = MEM_dupallocN(dvert->dw);
2529  ndvert++;
2530  }
2531  }
2532  if (gps->dvert != NULL) {
2533  dvert++;
2534  }
2535  }
2536  /* copy last segment */
2537  (gps->dvert != NULL) ? dvert = gps->dvert + last : NULL;
2538  for (i = last, pt = gps->points + last; i < gps->totpoints; i++, pt++) {
2539  *npt = *pt;
2540  npt++;
2541 
2542  if (gps->dvert != NULL) {
2543  *ndvert = *dvert;
2544  ndvert->dw = MEM_dupallocN(dvert->dw);
2545  ndvert++;
2546  dvert++;
2547  }
2548  }
2549 
2550  break;
2551  case GP_DISSOLVE_UNSELECT:
2552  /* copy any selected point */
2553  (gps->dvert != NULL) ? dvert = gps->dvert : NULL;
2554  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2555  if (pt->flag & GP_SPOINT_SELECT) {
2556  *npt = *pt;
2557  npt++;
2558 
2559  if (gps->dvert != NULL) {
2560  *ndvert = *dvert;
2561  ndvert->dw = MEM_dupallocN(dvert->dw);
2562  ndvert++;
2563  }
2564  }
2565  if (gps->dvert != NULL) {
2566  dvert++;
2567  }
2568  }
2569  break;
2570  }
2571 
2572  /* free the old buffer */
2573  if (gps->points) {
2574  MEM_freeN(gps->points);
2575  }
2576  if (gps->dvert) {
2578  MEM_freeN(gps->dvert);
2579  }
2580 
2581  /* save the new buffer */
2582  gps->points = new_points;
2583  gps->dvert = new_dvert;
2584  gps->totpoints = tot;
2585 
2586  /* Calc geometry data. */
2588 
2589  /* deselect the stroke, since none of its selected points will still be selected */
2590  gps->flag &= ~GP_STROKE_SELECT;
2592  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2593  pt->flag &= ~GP_SPOINT_SELECT;
2594  }
2595  }
2596 
2597  changed = true;
2598  }
2599  }
2600  GP_EDITABLE_STROKES_END(gpstroke_iter);
2601 
2602  return changed;
2603 }
2604 
2605 /* Delete selected points but keep the stroke */
2607 {
2609  bGPdata *gpd = (bGPdata *)ob->data;
2610  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2611  bool changed = false;
2612 
2613  if (is_curve_edit) {
2614  changed = gpencil_dissolve_selected_curve_points(C, gpd, mode);
2615  }
2616  else {
2617  changed = gpencil_dissolve_selected_stroke_points(C, gpd, mode);
2618  }
2619 
2620  if (changed) {
2623  return OPERATOR_FINISHED;
2624  }
2625  return OPERATOR_CANCELLED;
2626 }
2627 
2628 /* ----------------------------------- */
2629 
2630 /* Split selected strokes into segments, splitting on selected points */
2632 {
2635  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2636  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
2637  bool changed = false;
2638 
2639  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
2640  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
2641 
2642  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
2643  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
2644 
2645  if (gpf == NULL) {
2646  continue;
2647  }
2648 
2649  /* simply delete strokes which are selected */
2650  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
2651 
2652  /* skip strokes that are invalid for current view */
2653  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2654  continue;
2655  }
2656  /* check if the color is editable */
2657  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
2658  continue;
2659  }
2660 
2661  if (gps->flag & GP_STROKE_SELECT) {
2662  /* deselect old stroke, since it will be used as template for the new strokes */
2663  gps->flag &= ~GP_STROKE_SELECT;
2665 
2666  if (is_curve_edit) {
2667  bGPDcurve *gpc = gps->editcurve;
2669  gpd, gpf, gps, gps->next, gpc, GP_CURVE_POINT_SELECT);
2670  }
2671  else {
2672  /* delete unwanted points by splitting stroke into several smaller ones */
2674  gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, false, 0);
2675  }
2676 
2677  changed = true;
2678  }
2679  }
2680  }
2681  }
2682  }
2683  CTX_DATA_END;
2684 
2685  if (changed) {
2688  return OPERATOR_FINISHED;
2689  }
2690  return OPERATOR_CANCELLED;
2691 }
2692 
2694 {
2696 }
2697 
2700 /* -------------------------------------------------------------------- */
2705 {
2706  eGP_DeleteMode mode = RNA_enum_get(op->ptr, "type");
2707  int result = OPERATOR_CANCELLED;
2708 
2709  switch (mode) {
2710  case GP_DELETEOP_STROKES: /* selected strokes */
2712  break;
2713 
2714  case GP_DELETEOP_POINTS: /* selected points (breaks the stroke into segments) */
2716  break;
2717 
2718  case GP_DELETEOP_FRAME: /* active frame */
2720  break;
2721  }
2722 
2723  return result;
2724 }
2725 
2727 {
2728  static const EnumPropertyItem prop_gpencil_delete_types[] = {
2730  "POINTS",
2731  0,
2732  "Points",
2733  "Delete selected points and split strokes into segments"},
2734  {GP_DELETEOP_STROKES, "STROKES", 0, "Strokes", "Delete selected strokes"},
2735  {GP_DELETEOP_FRAME, "FRAME", 0, "Frame", "Delete active frame"},
2736  {0, NULL, 0, NULL, NULL},
2737  };
2738 
2739  /* identifiers */
2740  ot->name = "Delete";
2741  ot->idname = "GPENCIL_OT_delete";
2742  ot->description = "Delete selected Grease Pencil strokes, vertices, or frames";
2743 
2744  /* callbacks */
2748 
2749  /* flags */
2751 
2752  /* props */
2753  ot->prop = RNA_def_enum(ot->srna,
2754  "type",
2755  prop_gpencil_delete_types,
2756  0,
2757  "Type",
2758  "Method used for deleting Grease Pencil data");
2759 }
2760 
2763 /* -------------------------------------------------------------------- */
2768 {
2769  eGP_DissolveMode mode = RNA_enum_get(op->ptr, "type");
2770 
2771  return gpencil_dissolve_selected_points(C, mode);
2772 }
2773 
2775 {
2776  static EnumPropertyItem prop_gpencil_dissolve_types[] = {
2777  {GP_DISSOLVE_POINTS, "POINTS", 0, "Dissolve", "Dissolve selected points"},
2779  "BETWEEN",
2780  0,
2781  "Dissolve Between",
2782  "Dissolve points between selected points"},
2783  {GP_DISSOLVE_UNSELECT, "UNSELECT", 0, "Dissolve Unselect", "Dissolve all unselected points"},
2784  {0, NULL, 0, NULL, NULL},
2785  };
2786 
2787  /* identifiers */
2788  ot->name = "Dissolve";
2789  ot->idname = "GPENCIL_OT_dissolve";
2790  ot->description = "Delete selected points without splitting strokes";
2791 
2792  /* callbacks */
2796 
2797  /* flags */
2799 
2800  /* props */
2801  ot->prop = RNA_def_enum(ot->srna,
2802  "type",
2803  prop_gpencil_dissolve_types,
2804  0,
2805  "Type",
2806  "Method used for dissolving stroke points");
2807 }
2808 
2811 /* -------------------------------------------------------------------- */
2815 /* Poll callback for snap operators */
2816 /* NOTE: For now, we only allow these in the 3D view, as other editors do not
2817  * define a cursor or grid-step which can be used.
2818  */
2820 {
2821  ScrArea *area = CTX_wm_area(C);
2823 
2824  return (ob != NULL) && (ob->type == OB_GPENCIL) &&
2825  ((area != NULL) && (area->spacetype == SPACE_VIEW3D));
2826 }
2827 
2829 {
2831  ARegion *region = CTX_wm_region(C);
2832  View3D *v3d = CTX_wm_view3d(C);
2835  Object *obact = CTX_data_active_object(C);
2836  const float gridf = ED_view3d_grid_view_scale(scene, v3d, region, NULL);
2837  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2838 
2839  bool changed = false;
2840  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2841  /* only editable and visible layers are considered */
2842  if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2843  bGPDframe *gpf = gpl->actframe;
2844  float diff_mat[4][4];
2845 
2846  /* calculate difference matrix object */
2847  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
2848 
2849  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2850  /* skip strokes that are invalid for current view */
2851  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2852  continue;
2853  }
2854  /* check if the color is editable */
2855  if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
2856  continue;
2857  }
2858 
2859  if (is_curve_edit) {
2860  if (gps->editcurve == NULL) {
2861  continue;
2862  }
2863  float inv_diff_mat[4][4];
2864  invert_m4_m4_safe(inv_diff_mat, diff_mat);
2865 
2866  bGPDcurve *gpc = gps->editcurve;
2867  for (int i = 0; i < gpc->tot_curve_points; i++) {
2868  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
2869  BezTriple *bezt = &gpc_pt->bezt;
2870  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
2871  float tmp0[3], tmp1[3], tmp2[3], offset[3];
2872  mul_v3_m4v3(tmp0, diff_mat, bezt->vec[0]);
2873  mul_v3_m4v3(tmp1, diff_mat, bezt->vec[1]);
2874  mul_v3_m4v3(tmp2, diff_mat, bezt->vec[2]);
2875 
2876  /* calculate the offset vector */
2877  offset[0] = gridf * floorf(0.5f + tmp1[0] / gridf) - tmp1[0];
2878  offset[1] = gridf * floorf(0.5f + tmp1[1] / gridf) - tmp1[1];
2879  offset[2] = gridf * floorf(0.5f + tmp1[2] / gridf) - tmp1[2];
2880 
2881  /* shift bezTriple */
2882  add_v3_v3(bezt->vec[0], offset);
2883  add_v3_v3(bezt->vec[1], offset);
2884  add_v3_v3(bezt->vec[2], offset);
2885 
2886  mul_v3_m4v3(tmp0, inv_diff_mat, bezt->vec[0]);
2887  mul_v3_m4v3(tmp1, inv_diff_mat, bezt->vec[1]);
2888  mul_v3_m4v3(tmp2, inv_diff_mat, bezt->vec[2]);
2889  copy_v3_v3(bezt->vec[0], tmp0);
2890  copy_v3_v3(bezt->vec[1], tmp1);
2891  copy_v3_v3(bezt->vec[2], tmp2);
2892 
2893  changed = true;
2894  }
2895  }
2896 
2897  if (changed) {
2899  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
2901  }
2902  }
2903  else {
2904  /* TODO: if entire stroke is selected, offset entire stroke by same amount? */
2905  for (int i = 0; i < gps->totpoints; i++) {
2906  bGPDspoint *pt = &gps->points[i];
2907  /* only if point is selected */
2908  if (pt->flag & GP_SPOINT_SELECT) {
2909  /* apply parent transformations */
2910  float fpt[3];
2911  mul_v3_m4v3(fpt, diff_mat, &pt->x);
2912 
2913  fpt[0] = gridf * floorf(0.5f + fpt[0] / gridf);
2914  fpt[1] = gridf * floorf(0.5f + fpt[1] / gridf);
2915  fpt[2] = gridf * floorf(0.5f + fpt[2] / gridf);
2916 
2917  /* return data */
2918  copy_v3_v3(&pt->x, fpt);
2919  gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
2920 
2921  changed = true;
2922  }
2923  }
2924  }
2925  }
2926  }
2927  }
2928 
2929  if (changed) {
2933  }
2934 
2935  return OPERATOR_FINISHED;
2936 }
2937 
2939 {
2940  /* identifiers */
2941  ot->name = "Snap Selection to Grid";
2942  ot->idname = "GPENCIL_OT_snap_to_grid";
2943  ot->description = "Snap selected points to the nearest grid points";
2944 
2945  /* callbacks */
2948 
2949  /* flags */
2951 }
2952 
2955 /* -------------------------------------------------------------------- */
2960 {
2962  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2965  Object *obact = CTX_data_active_object(C);
2966 
2967  const bool use_offset = RNA_boolean_get(op->ptr, "use_offset");
2968  const float *cursor_global = scene->cursor.location;
2969 
2970  bool changed = false;
2971  if (is_curve_edit) {
2972  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
2973  }
2974  else {
2975  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
2976  /* only editable and visible layers are considered */
2977  if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
2978  bGPDframe *gpf = gpl->actframe;
2979  float diff_mat[4][4];
2980 
2981  /* calculate difference matrix */
2982  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
2983 
2984  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2985  bGPDspoint *pt;
2986  int i;
2987 
2988  /* skip strokes that are invalid for current view */
2989  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2990  continue;
2991  }
2992  /* check if the color is editable */
2993  if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
2994  continue;
2995  }
2996  /* only continue if this stroke is selected (editable doesn't guarantee this)... */
2997  if ((gps->flag & GP_STROKE_SELECT) == 0) {
2998  continue;
2999  }
3000 
3001  if (use_offset) {
3002  float offset[3];
3003 
3004  /* compute offset from first point of stroke to cursor */
3005  /* TODO: Allow using midpoint instead? */
3006  sub_v3_v3v3(offset, cursor_global, &gps->points->x);
3007 
3008  /* apply offset to all points in the stroke */
3009  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
3010  add_v3_v3(&pt->x, offset);
3011  }
3012 
3013  changed = true;
3014  }
3015  else {
3016  /* affect each selected point */
3017  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
3018  if (pt->flag & GP_SPOINT_SELECT) {
3019  copy_v3_v3(&pt->x, cursor_global);
3020  gpencil_apply_parent_point(depsgraph, obact, gpl, pt);
3021 
3022  changed = true;
3023  }
3024  }
3025  }
3026  }
3027  }
3028  }
3029  }
3030 
3031  if (changed) {
3035  }
3036 
3037  return OPERATOR_FINISHED;
3038 }
3039 
3041 {
3042  /* identifiers */
3043  ot->name = "Snap Selection to Cursor";
3044  ot->idname = "GPENCIL_OT_snap_to_cursor";
3045  ot->description = "Snap selected points/strokes to the cursor";
3046 
3047  /* callbacks */
3050 
3051  /* flags */
3053 
3054  /* props */
3056  "use_offset",
3057  true,
3058  "With Offset",
3059  "Offset the entire stroke instead of selected points only");
3060 }
3061 
3064 /* -------------------------------------------------------------------- */
3069  bContext *C,
3070  Object *obact,
3071  bGPdata *gpd,
3072  float r_centroid[3],
3073  float r_min[3],
3074  float r_max[3],
3075  size_t *count)
3076 {
3077  bool changed = false;
3078  /* calculate midpoints from selected points */
3079  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
3080  /* only editable and visible layers are considered */
3081  if (BKE_gpencil_layer_is_editable(gpl) && (gpl->actframe != NULL)) {
3082  bGPDframe *gpf = gpl->actframe;
3083  float diff_mat[4][4];
3084 
3085  /* calculate difference matrix */
3086  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
3087 
3088  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3089  bGPDspoint *pt;
3090  int i;
3091 
3092  /* skip strokes that are invalid for current view */
3093  if (ED_gpencil_stroke_can_use(C, gps) == false) {
3094  continue;
3095  }
3096  /* check if the color is editable */
3097  if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
3098  continue;
3099  }
3100  /* only continue if this stroke is selected (editable doesn't guarantee this)... */
3101  if ((gps->flag & GP_STROKE_SELECT) == 0) {
3102  continue;
3103  }
3104 
3105  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
3106  if (pt->flag & GP_SPOINT_SELECT) {
3107  /* apply parent transformations */
3108  float fpt[3];
3109  mul_v3_m4v3(fpt, diff_mat, &pt->x);
3110 
3111  add_v3_v3(r_centroid, fpt);
3112  minmax_v3v3_v3(r_min, r_max, fpt);
3113 
3114  (*count)++;
3115  }
3116  }
3117 
3118  changed = true;
3119  }
3120  }
3121  }
3122 
3123  return changed;
3124 }
3125 
3127 {
3129  Object *obact = CTX_data_active_object(C);
3131  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3132 
3134 
3135  float *cursor = scene->cursor.location;
3136  float centroid[3] = {0.0f};
3137  float min[3], max[3];
3138  size_t count = 0;
3139 
3140  INIT_MINMAX(min, max);
3141 
3142  bool changed = false;
3143  if (is_curve_edit) {
3144  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
3145  }
3146  else {
3147  changed = gpencil_stroke_points_centroid(depsgraph, C, obact, gpd, centroid, min, max, &count);
3148  }
3149 
3150  if (changed) {
3152  mid_v3_v3v3(cursor, min, max);
3153  }
3154  else { /* #V3D_AROUND_CENTER_MEDIAN. */
3155  zero_v3(cursor);
3156  if (count) {
3157  mul_v3_fl(centroid, 1.0f / (float)count);
3158  copy_v3_v3(cursor, centroid);
3159  }
3160  }
3161 
3164  }
3165 
3166  return OPERATOR_FINISHED;
3167 }
3168 
3170 {
3171  /* identifiers */
3172  ot->name = "Snap Cursor to Selected Points";
3173  ot->idname = "GPENCIL_OT_snap_cursor_to_selected";
3174  ot->description = "Snap cursor to center of selected points";
3175 
3176  /* callbacks */
3179 
3180  /* flags */
3182 }
3183 
3186 /* -------------------------------------------------------------------- */
3191 {
3194 
3195  /* sanity checks */
3196  if (ELEM(NULL, gpd, gpl, gpl->frames.first)) {
3197  return OPERATOR_CANCELLED;
3198  }
3199 
3200  /* loop all strokes */
3201  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
3202  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3203  /* Apply thickness */
3204  if ((gps->thickness == 0) && (gpl->line_change == 0)) {
3205  gps->thickness = gpl->thickness;
3206  }
3207  else {
3208  gps->thickness = gps->thickness + gpl->line_change;
3209  }
3210  }
3211  }
3212 
3213  /* clear value */
3214  gpl->thickness = 0.0f;
3215  gpl->line_change = 0;
3216 
3217  /* notifiers */
3220 
3221  return OPERATOR_FINISHED;
3222 }
3223 
3225 {
3226  /* identifiers */
3227  ot->name = "Apply Stroke Thickness";
3228  ot->idname = "GPENCIL_OT_stroke_apply_thickness";
3229  ot->description = "Apply the thickness change of the layer to its strokes";
3230 
3231  /* api callbacks */
3234 }
3235 
3238 /* -------------------------------------------------------------------- */
3242 enum {
3246 };
3247 
3249 {
3252 
3253  const int type = RNA_enum_get(op->ptr, "type");
3254  const bool geometry = RNA_boolean_get(op->ptr, "geometry");
3255  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
3256  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3257  bGPDstroke *gps = NULL;
3258 
3259  /* sanity checks */
3260  if (ELEM(NULL, gpd)) {
3261  return OPERATOR_CANCELLED;
3262  }
3263 
3264  bool changed = false;
3265  /* loop all selected strokes */
3266  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3267  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
3268 
3269  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
3270  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
3271  if (gpf == NULL) {
3272  continue;
3273  }
3274 
3275  for (gps = gpf->strokes.first; gps; gps = gps->next) {
3276  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
3277  /* skip strokes that are not selected or invalid for current view */
3278  if (((gps->flag & GP_STROKE_SELECT) == 0) ||
3279  ED_gpencil_stroke_can_use(C, gps) == false) {
3280  continue;
3281  }
3282  /* skip hidden or locked colors */
3283  if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
3284  (gp_style->flag & GP_MATERIAL_LOCKED)) {
3285  continue;
3286  }
3287 
3288  bool before = (bool)(gps->flag & GP_STROKE_CYCLIC);
3289  switch (type) {
3291  /* Close all (enable) */
3292  gps->flag |= GP_STROKE_CYCLIC;
3293  break;
3294  case GP_STROKE_CYCLIC_OPEN:
3295  /* Open all (disable) */
3296  gps->flag &= ~GP_STROKE_CYCLIC;
3297  break;
3299  /* Just toggle flag... */
3300  gps->flag ^= GP_STROKE_CYCLIC;
3301  break;
3302  default:
3303  BLI_assert(0);
3304  break;
3305  }
3306 
3307  if (before != (gps->flag & GP_STROKE_CYCLIC)) {
3308  /* Create new geometry. */
3309  if (is_curve_edit) {
3313  }
3314  else if ((gps->flag & GP_STROKE_CYCLIC) && geometry) {
3317  }
3318 
3319  changed = true;
3320  }
3321  }
3322 
3323  /* If not multi-edit, exit loop. */
3324  if (!is_multiedit) {
3325  break;
3326  }
3327  }
3328  }
3329  }
3330  CTX_DATA_END;
3331 
3332  if (changed) {
3333  /* notifiers */
3336  }
3337 
3338  return OPERATOR_FINISHED;
3339 }
3340 
3342  wmOperator *UNUSED(op),
3343  const PropertyRNA *prop)
3344 {
3346  if (gpd != NULL && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
3347  const char *prop_id = RNA_property_identifier(prop);
3348  /* Only show type in curve edit mode */
3349  if (!STREQ(prop_id, "type")) {
3350  return false;
3351  }
3352  }
3353 
3354  return true;
3355 }
3356 
3358 {
3359  PropertyRNA *prop;
3360 
3361  static const EnumPropertyItem cyclic_type[] = {
3362  {GP_STROKE_CYCLIC_CLOSE, "CLOSE", 0, "Close All", ""},
3363  {GP_STROKE_CYCLIC_OPEN, "OPEN", 0, "Open All", ""},
3364  {GP_STROKE_CYCLIC_TOGGLE, "TOGGLE", 0, "Toggle", ""},
3365  {0, NULL, 0, NULL, NULL},
3366  };
3367 
3368  /* identifiers */
3369  ot->name = "Set Cyclical State";
3370  ot->idname = "GPENCIL_OT_stroke_cyclical_set";
3371  ot->description = "Close or open the selected stroke adding an edge from last to first point";
3372 
3373  /* api callbacks */
3377 
3378  /* flags */
3380 
3381  /* properties */
3382  ot->prop = RNA_def_enum(ot->srna, "type", cyclic_type, GP_STROKE_CYCLIC_TOGGLE, "Type", "");
3383  prop = RNA_def_boolean(
3384  ot->srna, "geometry", false, "Create Geometry", "Create new geometry for closing stroke");
3386 }
3387 
3390 /* -------------------------------------------------------------------- */
3394 enum {
3399 };
3400 
3402 {
3405  const int type = RNA_enum_get(op->ptr, "type");
3406  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
3407 
3408  /* sanity checks */
3409  if (ELEM(NULL, gpd)) {
3410  return OPERATOR_CANCELLED;
3411  }
3412 
3413  bool changed = false;
3414  /* loop all selected strokes */
3415  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3416  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
3417 
3418  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
3419  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
3420  if (gpf == NULL) {
3421  continue;
3422  }
3423 
3424  for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
3425  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
3426 
3427  /* skip strokes that are not selected or invalid for current view */
3428  if (((gps->flag & GP_STROKE_SELECT) == 0) ||
3429  (ED_gpencil_stroke_can_use(C, gps) == false)) {
3430  continue;
3431  }
3432  /* skip hidden or locked colors */
3433  if (!gp_style || (gp_style->flag & GP_MATERIAL_HIDE) ||
3434  (gp_style->flag & GP_MATERIAL_LOCKED)) {
3435  continue;
3436  }
3437 
3438  short prev_first = gps->caps[0];
3439  short prev_last = gps->caps[1];
3440 
3442  ++gps->caps[0];
3443  if (gps->caps[0] >= GP_STROKE_CAP_MAX) {
3444  gps->caps[0] = GP_STROKE_CAP_ROUND;
3445  }
3446  }
3448  ++gps->caps[1];
3449  if (gps->caps[1] >= GP_STROKE_CAP_MAX) {
3450  gps->caps[1] = GP_STROKE_CAP_ROUND;
3451  }
3452  }
3454  gps->caps[0] = GP_STROKE_CAP_ROUND;
3455  gps->caps[1] = GP_STROKE_CAP_ROUND;
3456  }
3457 
3458  if (prev_first != gps->caps[0] || prev_last != gps->caps[1]) {
3459  changed = true;
3460  }
3461  }
3462  /* If not multi-edit, exit loop. */
3463  if (!is_multiedit) {
3464  break;
3465  }
3466  }
3467  }
3468  }
3469  CTX_DATA_END;
3470 
3471  if (changed) {
3472  /* notifiers */
3475  }
3476 
3477  return OPERATOR_FINISHED;
3478 }
3479 
3481 {
3482  static const EnumPropertyItem toggle_type[] = {
3483  {GP_STROKE_CAPS_TOGGLE_BOTH, "TOGGLE", 0, "Both", ""},
3484  {GP_STROKE_CAPS_TOGGLE_START, "START", 0, "Start", ""},
3485  {GP_STROKE_CAPS_TOGGLE_END, "END", 0, "End", ""},
3486  {GP_STROKE_CAPS_TOGGLE_DEFAULT, "DEFAULT", 0, "Default", "Set as default rounded"},
3487  {0, NULL, 0, NULL, NULL},
3488  };
3489 
3490  /* identifiers */
3491  ot->name = "Set Caps Mode";
3492  ot->idname = "GPENCIL_OT_stroke_caps_set";
3493  ot->description = "Change stroke caps mode (rounded or flat)";
3494 
3495  /* api callbacks */
3498 
3499  /* flags */
3501 
3502  /* properties */
3503  ot->prop = RNA_def_enum(ot->srna, "type", toggle_type, GP_STROKE_CAPS_TOGGLE_BOTH, "Type", "");
3504 }
3505 
3508 /* -------------------------------------------------------------------- */
3512 typedef struct tJoinStrokes {
3515  bool used;
3517 
3519  const bGPDstroke *gps,
3520  const int totstrokes)
3521 {
3522  int index = -1;
3523  float min_dist = FLT_MAX;
3524  float dist, start_a[3], end_a[3], start_b[3], end_b[3];
3525 
3526  bGPDspoint *pt = &gps->points[0];
3527  copy_v3_v3(start_a, &pt->x);
3528 
3529  pt = &gps->points[gps->totpoints - 1];
3530  copy_v3_v3(end_a, &pt->x);
3531 
3532  for (int i = 0; i < totstrokes; i++) {
3533  tJoinStrokes *elem = &strokes_list[i];
3534  if (elem->used) {
3535  continue;
3536  }
3537  pt = &elem->gps->points[0];
3538  copy_v3_v3(start_b, &pt->x);
3539 
3540  pt = &elem->gps->points[elem->gps->totpoints - 1];
3541  copy_v3_v3(end_b, &pt->x);
3542 
3543  dist = len_squared_v3v3(start_a, start_b);
3544  if (dist < min_dist) {
3545  min_dist = dist;
3546  index = i;
3547  }
3548  dist = len_squared_v3v3(start_a, end_b);
3549  if (dist < min_dist) {
3550  min_dist = dist;
3551  index = i;
3552  }
3553  dist = len_squared_v3v3(end_a, start_b);
3554  if (dist < min_dist) {
3555  min_dist = dist;
3556  index = i;
3557  }
3558  dist = len_squared_v3v3(end_a, end_b);
3559  if (dist < min_dist) {
3560  min_dist = dist;
3561  index = i;
3562  }
3563  }
3564 
3565  return index;
3566 }
3567 
3569 {
3571  bGPDlayer *activegpl = BKE_gpencil_layer_active_get(gpd);
3573  /* Limit the number of strokes to join. It makes no sense to allow an very high number of
3574  * strokes for CPU time and because to have a stroke with thousands of points is unpractical,
3575  * so limit this number avoid to joining a full frame scene in one single stroke. */
3576  const int max_join_strokes = 128;
3577 
3578  const int type = RNA_enum_get(op->ptr, "type");
3579  const bool leave_gaps = RNA_boolean_get(op->ptr, "leave_gaps");
3580 
3581  /* sanity checks */
3582  if (ELEM(NULL, gpd)) {
3583  return OPERATOR_CANCELLED;
3584  }
3585 
3586  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3587  if (is_curve_edit) {
3588  return OPERATOR_CANCELLED;
3589  }
3590 
3591  if (activegpl->flag & GP_LAYER_LOCKED) {
3592  return OPERATOR_CANCELLED;
3593  }
3594 
3596 
3597  int tot_strokes = 0;
3599  tJoinStrokes *strokes_list = MEM_malloc_arrayN(max_join_strokes, sizeof(tJoinStrokes), __func__);
3600  tJoinStrokes *elem = NULL;
3601  /* Read all selected strokes to create a list. */
3602  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3603  bGPDframe *gpf = gpl->actframe;
3604  if (gpf == NULL) {
3605  continue;
3606  }
3607 
3608  /* Add all stroke selected of the frame. */
3609  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3610  if (gps->flag & GP_STROKE_SELECT) {
3611  /* skip strokes that are invalid for current view */
3612  if (ED_gpencil_stroke_can_use(C, gps) == false) {
3613  continue;
3614  }
3615  /* check if the color is editable. */
3616  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
3617  continue;
3618  }
3619  elem = &strokes_list[tot_strokes];
3620  elem->gpf = gpf;
3621  elem->gps = gps;
3622  elem->used = false;
3623 
3624  tot_strokes++;
3625  /* Limit the number of strokes. */
3626  if (tot_strokes == max_join_strokes) {
3627  BKE_reportf(op->reports,
3628  RPT_WARNING,
3629  "Too many strokes selected, only joined first %d strokes",
3630  max_join_strokes);
3631  break;
3632  }
3633  }
3634  }
3635  }
3636  CTX_DATA_END;
3637 
3638  /* Nothing to join. */
3639  if (tot_strokes < 2) {
3640  MEM_SAFE_FREE(strokes_list);
3641  return OPERATOR_CANCELLED;
3642  }
3643 
3644  /* Take first stroke. */
3645  elem = &strokes_list[0];
3646  elem->used = true;
3647 
3648  /* Create a new stroke. */
3649  bGPDstroke *gps_new = BKE_gpencil_stroke_duplicate(elem->gps, true, true);
3650  gps_new->flag &= ~GP_STROKE_CYCLIC;
3651  BLI_insertlinkbefore(&elem->gpf->strokes, elem->gps, gps_new);
3652 
3653  /* Join all strokes until the list is completed. */
3654  while (true) {
3655  int i = gpencil_get_nearest_stroke_index(strokes_list, gps_new, tot_strokes);
3656  if (i < 0) {
3657  break;
3658  }
3659  elem = &strokes_list[i];
3660  /* Join new_stroke and stroke B. */
3661  BKE_gpencil_stroke_join(gps_new, elem->gps, leave_gaps, true, false);
3662  elem->used = true;
3663  }
3664 
3665  /* Calc geometry data for new stroke. */
3666  BKE_gpencil_stroke_geometry_update(gpd, gps_new);
3667 
3668  /* If join only, delete old strokes. */
3669  if (type == GP_STROKE_JOIN) {
3670  for (int i = 0; i < tot_strokes; i++) {
3671  elem = &strokes_list[i];
3672  BLI_remlink(&elem->gpf->strokes, elem->gps);
3674  }
3675  }
3676 
3677  /* Free memory. */
3678  MEM_SAFE_FREE(strokes_list);
3679 
3680  /* notifiers */
3683 
3684  return OPERATOR_FINISHED;
3685 }
3686 
3688 {
3689  static const EnumPropertyItem join_type[] = {
3690  {GP_STROKE_JOIN, "JOIN", 0, "Join", ""},
3691  {GP_STROKE_JOINCOPY, "JOINCOPY", 0, "Join and Copy", ""},
3692  {0, NULL, 0, NULL, NULL},
3693  };
3694 
3695  /* identifiers */
3696  ot->name = "Join Strokes";
3697  ot->idname = "GPENCIL_OT_stroke_join";
3698  ot->description = "Join selected strokes (optionally as new stroke)";
3699 
3700  /* api callbacks */
3703 
3704  /* flags */
3706 
3707  /* properties */
3708  ot->prop = RNA_def_enum(ot->srna, "type", join_type, GP_STROKE_JOIN, "Type", "");
3710  "leave_gaps",
3711  false,
3712  "Leave Gaps",
3713  "Leave gaps between joined strokes instead of linking them");
3714 }
3715 
3718 /* -------------------------------------------------------------------- */
3723 {
3726 
3727  /* sanity checks */
3728  if (ELEM(NULL, gpd)) {
3729  return OPERATOR_CANCELLED;
3730  }
3731 
3732  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
3733  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3734 
3735  bool changed = false;
3736  /* Read all selected strokes. */
3737  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3738  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
3739 
3740  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
3741  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
3742  if (gpf == NULL) {
3743  continue;
3744  }
3745  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3746  if (gps->flag & GP_STROKE_SELECT) {
3747  /* skip strokes that are invalid for current view */
3748  if (ED_gpencil_stroke_can_use(C, gps) == false) {
3749  continue;
3750  }
3751  /* check if the color is editable */
3752  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
3753  continue;
3754  }
3755 
3756  if (is_curve_edit) {
3757  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
3758  }
3759  else {
3760  /* Flip stroke. */
3762  changed = true;
3763  }
3764  }
3765  }
3766  }
3767  /* If not multi-edit, exit loop. */
3768  if (!is_multiedit) {
3769  break;
3770  }
3771  }
3772  }
3773  CTX_DATA_END;
3774 
3775  if (changed) {
3776  /* notifiers */
3779  }
3780 
3781  return OPERATOR_FINISHED;
3782 }
3783 
3785 {
3786  /* identifiers */
3787  ot->name = "Flip Stroke";
3788  ot->idname = "GPENCIL_OT_stroke_flip";
3789  ot->description = "Change direction of the points of the selected strokes";
3790 
3791  /* api callbacks */
3794 
3795  /* flags */
3797 }
3798 
3801 /* -------------------------------------------------------------------- */
3806 {
3810  int oldframe = (int)DEG_get_ctime(depsgraph);
3811  const eGP_ReprojectModes mode = RNA_enum_get(op->ptr, "type");
3812  const bool keep_original = RNA_boolean_get(op->ptr, "keep_original");
3813  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
3814  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
3815 
3816  /* Init snap context for geometry projection. */
3817  SnapObjectContext *sctx = NULL;
3819 
3820  bool changed = false;
3821  /* Init space conversion stuff. */
3822  GP_SpaceConversion gsc = {NULL};
3824  int cfra_prv = INT_MIN;
3825 
3826  /* Go through each editable + selected stroke, adjusting each of its points one by one... */
3827  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
3828  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
3829 
3830  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
3831  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
3832  if (gpf == NULL) {
3833  continue;
3834  }
3835  for (bGPDstroke *gps = gpf->strokes.first; gps; gps = gps->next) {
3836  /* skip strokes that are invalid for current view */
3837  if (ED_gpencil_stroke_can_use(C, gps) == false) {
3838  continue;
3839  }
3840  bool curve_select = false;
3841  if (is_curve_edit && gps->editcurve != NULL) {
3842  curve_select = gps->editcurve->flag & GP_CURVE_SELECT;
3843  }
3844 
3845  if (gps->flag & GP_STROKE_SELECT || curve_select) {
3846 
3847  /* update frame to get the new location of objects */
3848  if ((mode == GP_REPROJECT_SURFACE) && (cfra_prv != gpf->framenum)) {
3849  cfra_prv = gpf->framenum;
3850  scene->r.cfra = gpf->framenum;
3852  }
3853 
3854  ED_gpencil_stroke_reproject(depsgraph, &gsc, sctx, gpl, gpf, gps, mode, keep_original);
3855 
3856  if (is_curve_edit && gps->editcurve != NULL) {
3857  BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps);
3858  /* Update the selection from the stroke to the curve. */
3859  BKE_gpencil_editcurve_stroke_sync_selection(gpd, gps, gps->editcurve);
3860 
3861  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
3863  }
3864 
3865  changed = true;
3866  }
3867  }
3868  }
3869  /* If not multi-edit, exit loop. */
3870  if (!is_multiedit) {
3871  break;
3872  }
3873  }
3874  }
3875  CTX_DATA_END;
3876 
3877  /* return frame state and DB to original state */
3878  scene->r.cfra = oldframe;
3880 
3881  if (sctx != NULL) {
3883  }
3884 
3885  if (changed) {
3886  /* update changed data */
3889  }
3890 
3891  return OPERATOR_FINISHED;
3892 }
3893 
3895 {
3896  static const EnumPropertyItem reproject_type[] = {
3897  {GP_REPROJECT_FRONT, "FRONT", 0, "Front", "Reproject the strokes using the X-Z plane"},
3898  {GP_REPROJECT_SIDE, "SIDE", 0, "Side", "Reproject the strokes using the Y-Z plane"},
3899  {GP_REPROJECT_TOP, "TOP", 0, "Top", "Reproject the strokes using the X-Y plane"},
3901  "VIEW",
3902  0,
3903  "View",
3904  "Reproject the strokes to end up on the same plane, as if drawn from the current "
3905  "viewpoint "
3906  "using 'Cursor' Stroke Placement"},
3908  "SURFACE",
3909  0,
3910  "Surface",
3911  "Reproject the strokes on to the scene geometry, as if drawn using 'Surface' placement"},
3913  "CURSOR",
3914  0,
3915  "Cursor",
3916  "Reproject the strokes using the orientation of 3D cursor"},
3917  {0, NULL, 0, NULL, NULL},
3918  };
3919 
3920  /* identifiers */
3921  ot->name = "Reproject Strokes";
3922  ot->idname = "GPENCIL_OT_reproject";
3923  ot->description =
3924  "Reproject the selected strokes from the current viewpoint as if they had been newly "
3925  "drawn "
3926  "(e.g. to fix problems from accidental 3D cursor movement or accidental viewport changes, "
3927  "or for matching deforming geometry)";
3928 
3929  /* callbacks */
3933 
3934  /* flags */
3936 
3937  /* properties */
3938  ot->prop = RNA_def_enum(
3939  ot->srna, "type", reproject_type, GP_REPROJECT_VIEW, "Projection Type", "");
3940 
3942  ot->srna,
3943  "keep_original",
3944  0,
3945  "Keep Original",
3946  "Keep original strokes and create a copy before reprojecting instead of reproject them");
3947 }
3948 
3950 {
3952  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
3953  return OPERATOR_CANCELLED;
3954  }
3955 
3956  bGPdata *gpd = (bGPdata *)ob->data;
3957  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
3958  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
3959  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
3961  }
3962  }
3963  }
3964 
3965  /* update changed data */
3968  return OPERATOR_FINISHED;
3969 }
3970 
3972 {
3973  /* identifiers */
3974  ot->name = "Recalculate internal geometry";
3975  ot->idname = "GPENCIL_OT_recalc_geometry";
3976  ot->description = "Update all internal geometry data";
3977 
3978  /* callbacks */
3981 
3982  /* flags */
3984 }
3985 
3988 /* -------------------------------------------------------------------- */
3992 /* helper to smooth */
3994 {
3995  const int repeat = RNA_int_get(op->ptr, "repeat");
3996  float factor = RNA_float_get(op->ptr, "factor");
3997  const bool only_selected = RNA_boolean_get(op->ptr, "only_selected");
3998  const bool smooth_position = RNA_boolean_get(op->ptr, "smooth_position");
3999  const bool smooth_thickness = RNA_boolean_get(op->ptr, "smooth_thickness");
4000  const bool smooth_strength = RNA_boolean_get(op->ptr, "smooth_strength");
4001  const bool smooth_uv = RNA_boolean_get(op->ptr, "smooth_uv");
4002 
4003  if (factor == 0.0f) {
4004  return;
4005  }
4006 
4007  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4008  if (gps->flag & GP_STROKE_SELECT) {
4009  /* TODO use `BKE_gpencil_stroke_smooth` when the weights are better used. */
4010  bGPDstroke gps_old = *gps;
4011  gps_old.points = (bGPDspoint *)MEM_dupallocN(gps->points);
4012  bool need_update = false;
4013  /* Here the iteration needs to be done outside the smooth functions,
4014  * as there are points that don't get smoothed. */
4015  for (int n = 0; n < repeat; n++) {
4016  for (int i = 0; i < gps->totpoints; i++) {
4017  if (only_selected && (gps->points[i].flag & GP_SPOINT_SELECT) == 0) {
4018  continue;
4019  }
4020 
4021  /* Perform smoothing. */
4022  if (smooth_position) {
4023  BKE_gpencil_stroke_smooth_point(&gps_old, i, factor, 1, false, false, gps);
4024  need_update = true;
4025  }
4026  if (smooth_strength) {
4027  BKE_gpencil_stroke_smooth_strength(&gps_old, i, factor, 1, gps);
4028  }
4029  if (smooth_thickness) {
4030  BKE_gpencil_stroke_smooth_thickness(&gps_old, i, 1.0f - factor, 1, gps);
4031  }
4032  if (smooth_uv) {
4033  BKE_gpencil_stroke_smooth_uv(&gps_old, i, factor, 1, gps);
4034  need_update = true;
4035  }
4036  }
4037  if (n < repeat - 1) {
4038  memcpy(gps_old.points, gps->points, sizeof(bGPDspoint) * gps->totpoints);
4039  }
4040  }
4041  MEM_freeN(gps_old.points);
4042 
4043  if (need_update) {
4044  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
4046  }
4047  }
4048  }
4049  GP_EDITABLE_STROKES_END(gpstroke_iter);
4050 }
4051 
4052 /* helper: Count how many points need to be inserted */
4054 {
4055  bGPDspoint *pt;
4056  int i;
4057  int totnewpoints = 0;
4058  for (i = 0, pt = gps->points; i < gps->totpoints && pt; i++, pt++) {
4059  if (pt->flag & GP_SPOINT_SELECT) {
4060  if (i + 1 < gps->totpoints) {
4061  if (gps->points[i + 1].flag & GP_SPOINT_SELECT) {
4062  totnewpoints++;
4063  }
4064  }
4065  }
4066  }
4067 
4068  if ((gps->flag & GP_STROKE_CYCLIC) && (gps->points[0].flag & GP_SPOINT_SELECT) &&
4069  (gps->points[gps->totpoints - 1].flag & GP_SPOINT_SELECT)) {
4070  totnewpoints++;
4071  }
4072 
4073  return totnewpoints;
4074 }
4075 
4076 static void gpencil_stroke_subdivide(bGPDstroke *gps, const int cuts)
4077 {
4078  bGPDspoint *temp_points;
4079  int totnewpoints, oldtotpoints;
4080  int i2;
4081  /* loop as many times as cuts */
4082  for (int s = 0; s < cuts; s++) {
4083  totnewpoints = gpencil_count_subdivision_cuts(gps);
4084  if (totnewpoints == 0) {
4085  continue;
4086  }
4087  /* duplicate points in a temp area */
4088  temp_points = MEM_dupallocN(gps->points);
4089  oldtotpoints = gps->totpoints;
4090 
4091  MDeformVert *temp_dverts = NULL;
4092  MDeformVert *dvert_final = NULL;
4093  MDeformVert *dvert = NULL;
4094  MDeformVert *dvert_next = NULL;
4095  if (gps->dvert != NULL) {
4096  temp_dverts = MEM_dupallocN(gps->dvert);
4097  }
4098 
4099  /* resize the points arrays */
4100  gps->totpoints += totnewpoints;
4101  gps->points = MEM_recallocN(gps->points, sizeof(*gps->points) * gps->totpoints);
4102  if (gps->dvert != NULL) {
4103  gps->dvert = MEM_recallocN(gps->dvert, sizeof(*gps->dvert) * gps->totpoints);
4104  }
4105 
4106  /* loop and interpolate */
4107  i2 = 0;
4108  for (int i = 0; i < oldtotpoints; i++) {
4109  bGPDspoint *pt = &temp_points[i];
4110  bGPDspoint *pt_final = &gps->points[i2];
4111 
4112  /* copy current point */
4113  copy_v3_v3(&pt_final->x, &pt->x);
4114  pt_final->pressure = pt->pressure;
4115  pt_final->strength = pt->strength;
4116  pt_final->time = pt->time;
4117  pt_final->flag = pt->flag;
4118  copy_v4_v4(pt_final->vert_color, pt->vert_color);
4119 
4120  if (gps->dvert != NULL) {
4121  dvert = &temp_dverts[i];
4122  dvert_final = &gps->dvert[i2];
4123  dvert_final->totweight = dvert->totweight;
4124  dvert_final->dw = dvert->dw;
4125  }
4126  i2++;
4127 
4128  /* if next point is selected add a half way point */
4129  if (pt->flag & GP_SPOINT_SELECT) {
4130  if (i + 1 < oldtotpoints) {
4131  if (temp_points[i + 1].flag & GP_SPOINT_SELECT) {
4132  pt_final = &gps->points[i2];
4133  if (gps->dvert != NULL) {
4134  dvert_final = &gps->dvert[i2];
4135  }
4136  /* Interpolate all values */
4137  bGPDspoint *next = &temp_points[i + 1];
4138  interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
4139  pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
4140  pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
4141  CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
4142  interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
4143  pt_final->time = interpf(pt->time, next->time, 0.5f);
4144  pt_final->flag |= GP_SPOINT_SELECT;
4145 
4146  /* interpolate weights */
4147  if (gps->dvert != NULL) {
4148  dvert = &temp_dverts[i];
4149  dvert_next = &temp_dverts[i + 1];
4150  dvert_final = &gps->dvert[i2];
4151 
4152  dvert_final->totweight = dvert->totweight;
4153  dvert_final->dw = MEM_dupallocN(dvert->dw);
4154 
4155  /* interpolate weight values */
4156  for (int d = 0; d < dvert->totweight; d++) {
4157  MDeformWeight *dw_a = &dvert->dw[d];
4158  if (dvert_next->totweight > d) {
4159  MDeformWeight *dw_b = &dvert_next->dw[d];
4160  MDeformWeight *dw_final = &dvert_final->dw[d];
4161  dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
4162  }
4163  }
4164  }
4165 
4166  i2++;
4167  }
4168  }
4169  }
4170  }
4171 
4172  /* Subdivide between last and first point. */
4173  if (gps->flag & GP_STROKE_CYCLIC) {
4174  bGPDspoint *pt = &temp_points[oldtotpoints - 1];
4175  bGPDspoint *next = &temp_points[0];
4176  if ((pt->flag & GP_SPOINT_SELECT) && (next->flag & GP_SPOINT_SELECT)) {
4177  bGPDspoint *pt_final = &gps->points[i2];
4178  if (gps->dvert != NULL) {
4179  dvert_final = &gps->dvert[i2];
4180  }
4181  /* Interpolate all values */
4182  interp_v3_v3v3(&pt_final->x, &pt->x, &next->x, 0.5f);
4183  pt_final->pressure = interpf(pt->pressure, next->pressure, 0.5f);
4184  pt_final->strength = interpf(pt->strength, next->strength, 0.5f);
4185  CLAMP(pt_final->strength, GPENCIL_STRENGTH_MIN, 1.0f);
4186  interp_v4_v4v4(pt_final->vert_color, pt->vert_color, next->vert_color, 0.5f);
4187  pt_final->time = interpf(pt->time, next->time, 0.5f);
4188  pt_final->flag |= GP_SPOINT_SELECT;
4189 
4190  /* interpolate weights */
4191  if (gps->dvert != NULL) {
4192  dvert = &temp_dverts[oldtotpoints - 1];
4193  dvert_next = &temp_dverts[0];
4194  dvert_final = &gps->dvert[i2];
4195 
4196  dvert_final->totweight = dvert->totweight;
4197  dvert_final->dw = MEM_dupallocN(dvert->dw);
4198 
4199  /* interpolate weight values */
4200  for (int d = 0; d < dvert->totweight; d++) {
4201  MDeformWeight *dw_a = &dvert->dw[d];
4202  if (dvert_next->totweight > d) {
4203  MDeformWeight *dw_b = &dvert_next->dw[d];
4204  MDeformWeight *dw_final = &dvert_final->dw[d];
4205  dw_final->weight = interpf(dw_a->weight, dw_b->weight, 0.5f);
4206  }
4207  }
4208  }
4209  }
4210  }
4211 
4212  /* free temp memory */
4213  MEM_SAFE_FREE(temp_points);
4214  MEM_SAFE_FREE(temp_dverts);
4215  }
4216 }
4217 
4219 {
4221  const int cuts = RNA_int_get(op->ptr, "number_cuts");
4222 
4223  /* sanity checks */
4224  if (ELEM(NULL, gpd)) {
4225  return OPERATOR_CANCELLED;
4226  }
4227 
4228  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4229 
4230  bool changed = false;
4231  if (is_curve_edit) {
4232  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
4233  {
4234  if (gpc->flag & GP_CURVE_SELECT) {
4237  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
4239  changed = true;
4240  }
4241  }
4242  GP_EDITABLE_CURVES_END(gps_iter);
4243  }
4244  else {
4245  /* Go through each editable + selected stroke */
4246  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4247  if (gps->flag & GP_STROKE_SELECT) {
4248  gpencil_stroke_subdivide(gps, cuts);
4249  /* Calc geometry data. */
4251  changed = true;
4252  }
4253  }
4254  GP_EDITABLE_STROKES_END(gpstroke_iter);
4255 
4256  if (changed) {
4257  /* smooth stroke */
4258  gpencil_smooth_stroke(C, op);
4259  }
4260  }
4261 
4262  if (changed) {
4263  /* notifiers */
4266  }
4267 
4268  return OPERATOR_FINISHED;
4269 }
4270 
4272  wmOperator *UNUSED(op),
4273  const PropertyRNA *prop)
4274 {
4276  if (gpd != NULL && GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)) {
4277  const char *prop_id = RNA_property_identifier(prop);
4278  /* Only show number_cuts in curve edit mode */
4279  if (!STREQ(prop_id, "number_cuts")) {
4280  return false;
4281  }
4282  }
4283 
4284  return true;
4285 }
4286 
4288 {
4289  PropertyRNA *prop;
4290 
4291  /* identifiers */
4292  ot->name = "Subdivide Stroke";
4293  ot->idname = "GPENCIL_OT_stroke_subdivide";
4294  ot->description =
4295  "Subdivide between continuous selected points of the stroke adding a point half way "
4296  "between "
4297  "them";
4298 
4299  /* api callbacks */
4303 
4304  /* flags */
4306 
4307  /* properties */
4308  prop = RNA_def_int(ot->srna, "number_cuts", 1, 1, 10, "Number of Cuts", "", 1, 5);
4309  /* avoid re-using last var because it can cause _very_ high value and annoy users */
4311 
4312  /* Smooth parameters */
4313  RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 2.0f, "Smooth", "", 0.0f, 2.0f);
4314  prop = RNA_def_int(ot->srna, "repeat", 1, 1, 10, "Repeat", "", 1, 5);
4317  "only_selected",
4318  true,
4319  "Selected Points",
4320  "Smooth only selected points in the stroke");
4321  RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
4322  RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", "");
4323  RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
4324  RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
4325 }
4326 
4327 /* ** simplify stroke *** */
4329 {
4331  float factor = RNA_float_get(op->ptr, "factor");
4332 
4333  /* sanity checks */
4334  if (ELEM(NULL, gpd)) {
4335  return OPERATOR_CANCELLED;
4336  }
4337 
4338  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4339 
4340  bool changed = false;
4341  if (is_curve_edit) {
4342  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4343  }
4344  else {
4345  /* Go through each editable + selected stroke */
4346  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4347  if (gps->flag & GP_STROKE_SELECT) {
4348  /* simplify stroke using Ramer-Douglas-Peucker algorithm */
4349  BKE_gpencil_stroke_simplify_adaptive(gpd, gps, factor);
4350  changed = true;
4351  }
4352  }
4353  GP_EDITABLE_STROKES_END(gpstroke_iter);
4354  }
4355 
4356  if (changed) {
4357  /* notifiers */
4360  }
4361 
4362  return OPERATOR_FINISHED;
4363 }
4364 
4366 {
4367  PropertyRNA *prop;
4368 
4369  /* identifiers */
4370  ot->name = "Simplify Stroke";
4371  ot->idname = "GPENCIL_OT_stroke_simplify";
4372  ot->description = "Simplify selected stroked reducing number of points";
4373 
4374  /* api callbacks */
4377 
4378  /* flags */
4380 
4381  /* properties */
4382  prop = RNA_def_float(ot->srna, "factor", 0.0f, 0.0f, 100.0f, "Factor", "", 0.0f, 100.0f);
4383  /* avoid re-using last var */
4385 }
4386 
4387 /* ** simplify stroke using fixed algorithm *** */
4389 {
4391  int steps = RNA_int_get(op->ptr, "step");
4392 
4393  /* sanity checks */
4394  if (ELEM(NULL, gpd)) {
4395  return OPERATOR_CANCELLED;
4396  }
4397 
4398  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4399 
4400  bool changed = false;
4401  if (is_curve_edit) {
4402  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4403  }
4404  else {
4405  /* Go through each editable + selected stroke */
4406  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4407  if (gps->flag & GP_STROKE_SELECT) {
4408  changed |= true;
4409  for (int i = 0; i < steps; i++) {
4411  }
4412  }
4413  }
4414  GP_EDITABLE_STROKES_END(gpstroke_iter);
4415  }
4416 
4417  if (changed) {
4418  /* notifiers */
4421  }
4422 
4423  return OPERATOR_FINISHED;
4424 }
4425 
4427 {
4428  PropertyRNA *prop;
4429 
4430  /* identifiers */
4431  ot->name = "Simplify Fixed Stroke";
4432  ot->idname = "GPENCIL_OT_stroke_simplify_fixed";
4433  ot->description = "Simplify selected stroked reducing number of points using fixed algorithm";
4434 
4435  /* api callbacks */
4438 
4439  /* flags */
4441 
4442  /* properties */
4443  prop = RNA_def_int(ot->srna, "step", 1, 1, 100, "Steps", "Number of simplify steps", 1, 10);
4444 
4445  /* avoid re-using last var */
4447 }
4448 
4449 /* ** Resample stroke *** */
4451 {
4453  const float length = RNA_float_get(op->ptr, "length");
4454  const float sharp_threshold = RNA_float_get(op->ptr, "sharp_threshold");
4455 
4456  /* sanity checks */
4457  if (ELEM(NULL, gpd)) {
4458  return OPERATOR_CANCELLED;
4459  }
4460 
4461  /* Go through each editable + selected stroke */
4462  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
4463  if (gps->flag & GP_STROKE_SELECT) {
4464  BKE_gpencil_stroke_sample(gpd, gps, length, true, sharp_threshold);
4465  }
4466  }
4467  GP_EDITABLE_STROKES_END(gpstroke_iter);
4468 
4469  /* notifiers */
4472 
4473  return OPERATOR_FINISHED;
4474 }
4475 
4477 {
4478  PropertyRNA *prop;
4479 
4480  /* identifiers */
4481  ot->name = "Sample Stroke";
4482  ot->idname = "GPENCIL_OT_stroke_sample";
4483  ot->description = "Sample stroke points to predefined segment length";
4484 
4485  /* api callbacks */
4488 
4489  /* flags */
4491 
4492  /* properties */
4493  prop = RNA_def_float(ot->srna, "length", 0.1f, 0.0f, 100.0f, "Length", "", 0.0f, 100.0f);
4494  prop = RNA_def_float(
4495  ot->srna, "sharp_threshold", 0.1f, 0.0f, M_PI, "Sharp Threshold", "", 0.0f, M_PI);
4496  /* avoid re-using last var */
4498 }
4499 
4502 /* -------------------------------------------------------------------- */
4507 {
4509 
4510  /* sanity checks */
4511  if (ELEM(NULL, gpd)) {
4512  return OPERATOR_CANCELLED;
4513  }
4514 
4515  /* Go through each editable + selected stroke */
4516  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
4517  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4518 
4519  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
4520  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
4521 
4522  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
4523  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
4524 
4525  if (gpf == NULL) {
4526  continue;
4527  }
4528 
4529  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
4530 
4531  /* skip strokes that are invalid for current view */
4532  if (ED_gpencil_stroke_can_use(C, gps) == false) {
4533  continue;
4534  }
4535 
4536  if (gps->flag & GP_STROKE_SELECT) {
4537  if (is_curve_edit) {
4538  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4539  }
4540  else {
4541  BKE_gpencil_stroke_trim(gpd, gps);
4542  }
4543  }
4544  }
4545  /* If not multi-edit, exit loop. */
4546  if (!is_multiedit) {
4547  break;
4548  }
4549  }
4550  }
4551  }
4552  CTX_DATA_END;
4553 
4554  /* notifiers */
4557 
4558  return OPERATOR_FINISHED;
4559 }
4560 
4562 {
4563  /* identifiers */
4564  ot->name = "Trim Stroke";
4565  ot->idname = "GPENCIL_OT_stroke_trim";
4566  ot->description = "Trim selected stroke to first loop or intersection";
4567 
4568  /* api callbacks */
4571 
4572  /* flags */
4574 }
4575 
4578 /* -------------------------------------------------------------------- */
4582 typedef enum eGP_SeparateModes {
4583  /* Points */
4585  /* Selected Strokes */
4587  /* Current Layer */
4590 
4592 {
4593  Base *base_new;
4594  Main *bmain = CTX_data_main(C);
4596  ViewLayer *view_layer = CTX_data_view_layer(C);
4597  Base *base_prev = CTX_data_active_base(C);
4598  bGPdata *gpd_src = ED_gpencil_data_get_active(C);
4600 
4601  Object *ob_dst = NULL;
4602  bGPdata *gpd_dst = NULL;
4603  bGPDlayer *gpl_dst = NULL;
4604  bGPDframe *gpf_dst = NULL;
4605  bGPDspoint *pt;
4606  Material *ma = NULL;
4607  int i, idx;
4608 
4609  eGP_SeparateModes mode = RNA_enum_get(op->ptr, "mode");
4610 
4611  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd_src);
4612  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd_src);
4613 
4614  /* sanity checks */
4615  if (ELEM(NULL, gpd_src)) {
4616  return OPERATOR_CANCELLED;
4617  }
4618 
4619  if ((mode == GP_SEPARATE_LAYER) && (BLI_listbase_is_single(&gpd_src->layers))) {
4620  BKE_report(op->reports, RPT_ERROR, "Cannot separate an object with one layer only");
4621  return OPERATOR_CANCELLED;
4622  }
4623 
4624  /* Cancel if nothing selected. */
4626  bool has_selected = false;
4627  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
4628  if (ED_gpencil_layer_has_selected_stroke(gpl, is_multiedit)) {
4629  has_selected = true;
4630  break;
4631  }
4632  }
4633  CTX_DATA_END;
4634 
4635  if (!has_selected) {
4636  BKE_report(op->reports, RPT_ERROR, "Nothing selected");
4637  return OPERATOR_CANCELLED;
4638  }
4639  }
4640 
4641  /* Create a new object. */
4642  /* Take into account user preferences for duplicating actions. */
4643  const eDupli_ID_Flags dupflag = (U.dupflag & USER_DUP_ACT);
4644 
4645  base_new = ED_object_add_duplicate(bmain, scene, view_layer, base_prev, dupflag);
4646  ob_dst = base_new->object;
4647  ob_dst->mode = OB_MODE_OBJECT;
4648  /* Duplication will increment #bGPdata user-count, but since we create a new grease-pencil
4649  * data-block for ob_dst (which gets its own user automatically),
4650  * we have to decrement the user-count again. */
4651  gpd_dst = BKE_gpencil_data_addnew(bmain, gpd_src->id.name + 2);
4652  id_us_min(ob_dst->data);
4653  ob_dst->data = (bGPdata *)gpd_dst;
4654 
4657 
4658  /* Loop old data-block and separate parts. */
4660  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
4661  gpl_dst = NULL;
4662  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
4663 
4664  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
4665  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
4666 
4667  if (gpf == NULL) {
4668  continue;
4669  }
4670 
4671  gpf_dst = NULL;
4672 
4673  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
4674 
4675  /* skip strokes that are invalid for current view */
4676  if (ED_gpencil_stroke_can_use(C, gps) == false) {
4677  continue;
4678  }
4679  /* check if the color is editable */
4680  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
4681  continue;
4682  }
4683  /* Separate selected strokes. */
4684  if (gps->flag & GP_STROKE_SELECT) {
4685  /* add layer if not created before */
4686  if (gpl_dst == NULL) {
4687  gpl_dst = BKE_gpencil_layer_addnew(gpd_dst, gpl->info, false, false);
4688  BKE_gpencil_layer_copy_settings(gpl, gpl_dst);
4689  /* Copy masks. */
4690  BKE_gpencil_layer_mask_copy(gpl, gpl_dst);
4691  }
4692 
4693  /* add frame if not created before */
4694  if (gpf_dst == NULL) {
4695  gpf_dst = BKE_gpencil_layer_frame_get(gpl_dst, gpf->framenum, GP_GETFRAME_ADD_NEW);
4696  }
4697 
4698  /* add duplicate materials */
4699 
4700  /* XXX same material can be in multiple slots. */
4701  ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
4702 
4703  idx = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
4704 
4705  /* selected points mode */
4706  if (mode == GP_SEPARATE_POINT) {
4707  if (is_curve_edit) {
4708  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4709  }
4710  else {
4711  /* Check if all points are selected. */
4712  bool all_points_selected = true;
4713  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
4714  if ((pt->flag & GP_SPOINT_SELECT) == 0) {
4715  all_points_selected = false;
4716  break;
4717  }
4718  }
4719 
4720  /* Separate the entire stroke. */
4721  if (all_points_selected) {
4722  /* deselect old stroke */
4723  gps->flag &= ~GP_STROKE_SELECT;
4725  /* unlink from source frame */
4726  BLI_remlink(&gpf->strokes, gps);
4727  gps->prev = gps->next = NULL;
4728  /* relink to destination frame */
4729  BLI_addtail(&gpf_dst->strokes, gps);
4730  /* Reassign material. */
4731  gps->mat_nr = idx;
4732 
4733  continue;
4734  }
4735 
4736  /* make copy of source stroke */
4737  bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
4738 
4739  /* Reassign material. */
4740  gps_dst->mat_nr = idx;
4741 
4742  /* link to destination frame */
4743  BLI_addtail(&gpf_dst->strokes, gps_dst);
4744 
4745  /* Invert selection status of all points in destination stroke */
4746  for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
4747  pt->flag ^= GP_SPOINT_SELECT;
4748  }
4749 
4750  /* delete selected points from destination stroke */
4752  gpd_dst, gpf_dst, gps_dst, NULL, GP_SPOINT_SELECT, false, false, 0);
4753 
4754  /* delete selected points from origin stroke */
4756  gpd_src, gpf, gps, gps->next, GP_SPOINT_SELECT, false, false, 0);
4757  }
4758  }
4759  /* selected strokes mode */
4760  else if (mode == GP_SEPARATE_STROKE) {
4761  /* deselect old stroke */
4762  gps->flag &= ~GP_STROKE_SELECT;
4764  /* unlink from source frame */
4765  BLI_remlink(&gpf->strokes, gps);
4766  gps->prev = gps->next = NULL;
4767  /* relink to destination frame */
4768  BLI_addtail(&gpf_dst->strokes, gps);
4769  /* Reassign material. */
4770  gps->mat_nr = idx;
4771  }
4772  }
4773  }
4774  }
4775 
4776  /* If not multi-edit, exit loop. */
4777  if (!is_multiedit) {
4778  break;
4779  }
4780  }
4781  }
4782  CTX_DATA_END;
4783  }
4784  else if (mode == GP_SEPARATE_LAYER) {
4786  if (gpl) {
4787  /* try to set a new active layer in source datablock */
4788  if (gpl->prev) {
4789  BKE_gpencil_layer_active_set(gpd_src, gpl->prev);
4790  }
4791  else if (gpl->next) {
4792  BKE_gpencil_layer_active_set(gpd_src, gpl->next);
4793  }
4794  /* unlink from source datablock */
4795  BLI_remlink(&gpd_src->layers, gpl);
4796  gpl->prev = gpl->next = NULL;
4797  /* relink to destination datablock */
4798  BLI_addtail(&gpd_dst->layers, gpl);
4799 
4800  /* add duplicate materials */
4801  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
4802  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
4803  /* skip strokes that are invalid for current view */
4804  if (ED_gpencil_stroke_can_use(C, gps) == false) {
4805  continue;
4806  }
4807  ma = BKE_gpencil_material(ob, gps->mat_nr + 1);
4808  gps->mat_nr = BKE_gpencil_object_material_ensure(bmain, ob_dst, ma);
4809  }
4810  }
4811  }
4812  }
4813 
4814  /* Ensure destination object has one active layer. */
4815  if (gpd_dst->layers.first != NULL) {
4816  if (BKE_gpencil_layer_active_get(gpd_dst) == NULL) {
4817  BKE_gpencil_layer_active_set(gpd_dst, gpd_dst->layers.first);
4818  }
4819  }
4820 
4821  /* Remove unused slots. */
4822  int actcol = ob_dst->actcol;
4823  for (int slot = 1; slot <= ob_dst->totcol; slot++) {
4824  while (slot <= ob_dst->totcol && !BKE_object_material_slot_used(ob_dst, slot)) {
4825  ob_dst->actcol = slot;
4826  if (!BKE_object_material_slot_remove(bmain, ob_dst)) {
4827  break;
4828  }
4829  if (actcol >= slot) {
4830  actcol--;
4831  }
4832  }
4833  }
4834  ob_dst->actcol = actcol;
4835 
4836  /* Remove any invalid Mask relationship. */
4838 
4841 
4842  DEG_relations_tag_update(bmain);
4847 
4848  return OPERATOR_FINISHED;
4849 }
4850 
4852 {
4853  static const EnumPropertyItem separate_type[] = {
4854  {GP_SEPARATE_POINT, "POINT", 0, "Selected Points", "Separate the selected points"},
4855  {GP_SEPARATE_STROKE, "STROKE", 0, "Selected Strokes", "Separate the selected strokes"},
4856  {GP_SEPARATE_LAYER, "LAYER", 0, "Active Layer", "Separate the strokes of the current layer"},
4857  {0, NULL, 0, NULL, NULL},
4858  };
4859 
4860  /* identifiers */
4861  ot->name = "Separate Strokes";
4862  ot->idname = "GPENCIL_OT_stroke_separate";
4863  ot->description = "Separate the selected strokes or layer in a new grease pencil object";
4864 
4865  /* callbacks */
4869 
4870  /* flags */
4872 
4873  /* properties */
4874  ot->prop = RNA_def_enum(ot->srna, "mode", separate_type, GP_SEPARATE_POINT, "Mode", "");
4875 }
4876 
4879 /* -------------------------------------------------------------------- */
4884 {
4887  bGPDspoint *pt;
4888  int i;
4889 
4890  /* sanity checks */
4891  if (ELEM(NULL, gpd)) {
4892  return OPERATOR_CANCELLED;
4893  }
4894  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
4895  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
4896 
4897  /* loop strokes and split parts */
4898  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
4899  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
4900 
4901  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
4902  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
4903 
4904  if (gpf == NULL) {
4905  continue;
4906  }
4907 
4908  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
4909 
4910  /* skip strokes that are invalid for current view */
4911  if (ED_gpencil_stroke_can_use(C, gps) == false) {
4912  continue;
4913  }
4914  /* check if the color is editable */
4915  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
4916  continue;
4917  }
4918  /* Split selected strokes. */
4919  if (gps->flag & GP_STROKE_SELECT) {
4920  if (is_curve_edit) {
4921  BKE_report(op->reports, RPT_ERROR, "Not implemented!");
4922  }
4923  else {
4924  /* make copy of source stroke */
4925  bGPDstroke *gps_dst = BKE_gpencil_stroke_duplicate(gps, true, true);
4926 
4927  /* link to same frame */
4928  BLI_addtail(&gpf->strokes, gps_dst);
4929 
4930  /* invert selection status of all points in destination stroke */
4931  for (i = 0, pt = gps_dst->points; i < gps_dst->totpoints; i++, pt++) {
4932  pt->flag ^= GP_SPOINT_SELECT;
4933  }
4934 
4935  /* delete selected points from destination stroke */
4937  gpd, gpf, gps_dst, NULL, GP_SPOINT_SELECT, true, false, 0);
4938 
4939  /* delete selected points from origin stroke */
4941  gpd, gpf, gps, gps->next, GP_SPOINT_SELECT, false, false, 0);
4942  }
4943  }
4944  }
4945  /* select again tagged points */
4946  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
4947  bGPDspoint *ptn = gps->points;
4948  for (int i2 = 0; i2 < gps->totpoints; i2++, ptn++) {
4949  if (ptn->flag & GP_SPOINT_TAG) {
4950  ptn->flag |= GP_SPOINT_SELECT;
4951  ptn->flag &= ~GP_SPOINT_TAG;
4952  }
4953  }
4954  }
4955  }
4956 
4957  /* If not multi-edit, exit loop. */
4958  if (!is_multiedit) {
4959  break;
4960  }
4961  }
4962  }
4963  CTX_DATA_END;
4964 
4966 
4968 
4969  return OPERATOR_FINISHED;
4970 }
4971 
4973 {
4974  /* identifiers */
4975  ot->name = "Split Strokes";
4976  ot->idname = "GPENCIL_OT_stroke_split";
4977  ot->description = "Split selected points as new stroke on same frame";
4978 
4979  /* callbacks */
4982 
4983  /* flags */
4985 }
4986 
4989 /* -------------------------------------------------------------------- */
4994 {
4996 
4997  /* sanity checks */
4998  if (ELEM(NULL, gpd)) {
4999  return OPERATOR_CANCELLED;
5000  }
5001 
5002  gpencil_smooth_stroke(C, op);
5003 
5004  /* notifiers */
5007 
5008  return OPERATOR_FINISHED;
5009 }
5010 
5012 {
5013  PropertyRNA *prop;
5014 
5015  /* identifiers */
5016  ot->name = "Smooth Stroke";
5017  ot->idname = "GPENCIL_OT_stroke_smooth";
5018  ot->description = "Smooth selected strokes";
5019 
5020  /* api callbacks */
5023 
5024  /* flags */
5026 
5027  /* properties */
5028  prop = RNA_def_int(ot->srna, "repeat", 2, 1, 1000, "Repeat", "", 1, 1000);
5030 
5031  RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 2.0f, "Factor", "", 0.0f, 1.0f);
5033  "only_selected",
5034  true,
5035  "Selected Points",
5036  "Smooth only selected points in the stroke");
5037  RNA_def_boolean(ot->srna, "smooth_position", true, "Position", "");
5038  RNA_def_boolean(ot->srna, "smooth_thickness", true, "Thickness", "");
5039  RNA_def_boolean(ot->srna, "smooth_strength", false, "Strength", "");
5040  RNA_def_boolean(ot->srna, "smooth_uv", false, "UV", "");
5041 }
5042 
5045 /* -------------------------------------------------------------------- */
5049 /* smart stroke cutter for trimming stroke ends */
5052  const int (*mcoords)[2];
5054 };
5055 
5057  bGPDspoint *pt,
5058  const GP_SpaceConversion *gsc,
5059  const float diff_mat[4][4],
5060  void *user_data)
5061 {
5062  const struct GP_SelectLassoUserData *data = user_data;
5063  bGPDspoint pt2;
5064  int x0, y0;
5065  gpencil_point_to_parent_space(pt, diff_mat, &pt2);
5066  gpencil_point_to_xy(gsc, gps, &pt2, &x0, &y0);
5067  /* test if in lasso */
5068  return ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(&data->rect, x0, y0) &&
5069  BLI_lasso_is_point_inside(data->mcoords, data->mcoords_len, x0, y0, INT_MAX));
5070 }
5071 
5073  bGPDspoint *pt,
5074  const GP_SpaceConversion *gsc,
5075  const float diff_mat[4][4],
5076  void *user_data);
5077 
5079  bGPDlayer *hit_layer,
5080  bGPDstroke *hit_stroke,
5081  const bool flat_caps)
5082 {
5083  bGPDspoint *pt = NULL;
5084  bGPDspoint *pt1 = NULL;
5085  int i;
5086 
5087  bGPDstroke *gpsn = hit_stroke->next;
5088 
5089  int totselect = 0;
5090  for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
5091  if (pt->flag & GP_SPOINT_SELECT) {
5092  totselect++;
5093  }
5094  }
5095 
5096  /* if all points selected delete or only 2 points and 1 selected */
5097  if (((totselect == 1) && (hit_stroke->totpoints == 2)) || (hit_stroke->totpoints == totselect)) {
5098  BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
5099  BKE_gpencil_free_stroke(hit_stroke);
5100  hit_stroke = NULL;
5101  }
5102 
5103  /* if very small distance delete */
5104  if ((hit_stroke) && (hit_stroke->totpoints == 2)) {
5105  pt = &hit_stroke->points[0];
5106  pt1 = &hit_stroke->points[1];
5107  if (len_v3v3(&pt->x, &pt1->x) < 0.001f) {
5108  BLI_remlink(&hit_layer->actframe->strokes, hit_stroke);
5109  BKE_gpencil_free_stroke(hit_stroke);
5110  hit_stroke = NULL;
5111  }
5112  }
5113 
5114  if (hit_stroke) {
5115  /* tag and dissolve (untag new points) */
5116  for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
5117  if (pt->flag & GP_SPOINT_SELECT) {
5118  pt->flag &= ~GP_SPOINT_SELECT;
5119  pt->flag |= GP_SPOINT_TAG;
5120  }
5121  else if (pt->flag & GP_SPOINT_TAG) {
5122  pt->flag &= ~GP_SPOINT_TAG;
5123  }
5124  }
5125  /* If flat caps mode check extremes. */
5126  if (flat_caps) {
5127  if (hit_stroke->points[0].flag & GP_SPOINT_TAG) {
5128  hit_stroke->caps[0] = GP_STROKE_CAP_FLAT;
5129  }
5130 
5131  if (hit_stroke->points[hit_stroke->totpoints - 1].flag & GP_SPOINT_TAG) {
5132  hit_stroke->caps[1] = GP_STROKE_CAP_FLAT;
5133  }
5134  }
5135 
5137  gpd, hit_layer->actframe, hit_stroke, gpsn, GP_SPOINT_TAG, false, false, 1);
5138  }
5139 }
5140 
5142  wmOperator *op,
5143  GPencilTestFn is_inside_fn,
5144  void *user_data)
5145 {
5147  Object *obact = CTX_data_active_object(C);
5149  ScrArea *area = CTX_wm_area(C);
5151  const float scale = ts->gp_sculpt.isect_threshold;
5152  const bool flat_caps = RNA_boolean_get(op->ptr, "flat_caps");
5153  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
5154 
5155  bGPDspoint *pt;
5156  GP_SpaceConversion gsc = {NULL};
5157 
5158  bool changed = false;
5159 
5160  /* sanity checks */
5161  if (area == NULL) {
5162  BKE_report(op->reports, RPT_ERROR, "No active area");
5163  return OPERATOR_CANCELLED;
5164  }
5165 
5166  /* init space conversion stuff */
5168 
5169  /* Deselect all strokes. */
5170  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
5171  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
5172  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
5173  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
5174  if (gps->flag & GP_STROKE_SELECT) {
5175  int i;
5176  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
5177  pt->flag &= ~GP_SPOINT_SELECT;
5178  }
5179 
5180  gps->flag &= ~GP_STROKE_SELECT;
5182  }
5183  }
5184  /* If not multi-edit, exit loop. */
5185  if (!is_multiedit) {
5186  break;
5187  }
5188  }
5189  }
5190 
5191  /* Select points */
5192  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
5193  if ((gpl->flag & GP_LAYER_LOCKED) || ((gpl->flag & GP_LAYER_HIDE))) {
5194  continue;
5195  }
5196 
5197  float diff_mat[4][4];
5198  BKE_gpencil_layer_transform_matrix_get(depsgraph, obact, gpl, diff_mat);
5199 
5200  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
5201  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
5202  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
5203  if (gpf == NULL) {
5204  continue;
5205  }
5206 
5207  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
5208  if (ED_gpencil_stroke_can_use(C, gps) == false) {
5209  continue;
5210  } /* check if the color is editable */
5211  if (ED_gpencil_stroke_material_editable(obact, gpl, gps) == false) {
5212  continue;
5213  }
5214  int tot_inside = 0;
5215  const int oldtot = gps->totpoints;
5216  for (int i = 0; i < gps->totpoints; i++) {
5217  pt = &gps->points[i];
5218  if ((pt->flag & GP_SPOINT_SELECT) || (pt->flag & GP_SPOINT_TAG)) {
5219  continue;
5220  }
5221  /* convert point coords to screen-space */
5222  const bool is_inside = is_inside_fn(gps, pt, &gsc, diff_mat, user_data);
5223  if (is_inside) {
5224  tot_inside++;
5225  changed = true;
5226  pt->flag |= GP_SPOINT_SELECT;
5227  gps->flag |= GP_STROKE_SELECT;
5229  float r_hita[3], r_hitb[3];
5230  if (gps->totpoints > 1) {
5232  gpd, gpl, gps, pt, true, true, scale, r_hita, r_hitb);
5233  }
5234  /* avoid infinite loops */
5235  if (gps->totpoints > oldtot) {
5236  break;
5237  }
5238  }
5239  }
5240  /* if mark all points inside lasso set to remove all stroke */
5241  if ((tot_inside == oldtot) || ((tot_inside == 1) && (oldtot == 2))) {
5242  for (int i = 0; i < gps->totpoints; i++) {
5243  pt = &gps->points[i];
5244  pt->flag |= GP_SPOINT_SELECT;
5245  }
5246  }
5247  }
5248  /* If not multi-edit, exit loop. */
5249  if (!is_multiedit) {
5250  break;
5251  }
5252  }
5253  }
5254  }
5255 
5256  /* Dissolve selected points. */
5257  LISTBASE_FOREACH (bGPDlayer *, gpl, &gpd->layers) {
5258  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
5259  bGPDframe *gpf_act = gpl->actframe;
5260  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
5261  gpl->actframe = gpf;
5262  LISTBASE_FOREACH_MUTABLE (bGPDstroke *, gps, &gpf->strokes) {
5263  if (gps->flag & GP_STROKE_SELECT) {
5264  gpencil_cutter_dissolve(gpd, gpl, gps, flat_caps);
5265  }
5266  }
5267  /* If not multi-edit, exit loop. */
5268  if (!is_multiedit) {
5269  break;
5270  }
5271  }
5272  gpl->actframe = gpf_act;
5273  }
5274 
5275  /* updates */
5276  if (changed) {
5280  }
5281 
5282  return OPERATOR_FINISHED;
5283 }
5284 
5286 {
5288 
5289  if (GPENCIL_PAINT_MODE(gpd)) {
5290  if (gpd->layers.first) {
5291  return true;
5292  }
5293  }
5294 
5295  return false;
5296 }
5297 
5299 {
5300  ScrArea *area = CTX_wm_area(C);
5301  /* sanity checks */
5302  if (area == NULL) {
5303  BKE_report(op->reports, RPT_ERROR, "No active area");
5304  return OPERATOR_CANCELLED;
5305  }
5306 
5307  struct GP_SelectLassoUserData data = {0};
5308  data.mcoords = WM_gesture_lasso_path_to_array(C, op, &data.mcoords_len);
5309 
5310  /* Sanity check. */
5311  if (data.mcoords == NULL) {
5312  return OPERATOR_PASS_THROUGH;
5313  }
5314 
5315  /* Compute boundbox of lasso (for faster testing later). */
5316  BLI_lasso_boundbox(&data.rect, data.mcoords, data.mcoords_len);
5317 
5319 
5320  MEM_freeN((void *)data.mcoords);
5321 
5322  return OPERATOR_FINISHED;
5323 }
5324 
5326 {
5327  /* identifiers */
5328  ot->name = "Stroke Cutter";
5329  ot->description = "Select section and cut";
5330  ot->idname = "GPENCIL_OT_stroke_cutter";
5331 
5332  /* callbacks */
5338 
5339  /* flag */
5341 
5342  /* properties */
5344 
5345  RNA_def_boolean(ot->srna, "flat_caps", 0, "Flat Caps", "");
5346 }
5347 
5348 bool ED_object_gpencil_exit(struct Main *bmain, Object *ob)
5349 {
5350  bool ok = false;
5351  if (ob) {
5352  bGPdata *gpd = (bGPdata *)ob->data;
5353 
5356 
5357  ob->restore_mode = ob->mode;
5360 
5361  /* Inform all CoW versions that we changed the mode. */
5363  ok = true;
5364  }
5365  return ok;
5366 }
5367 
5370 /* -------------------------------------------------------------------- */
5375 {
5377  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
5378  return false;
5379  }
5380  bGPdata *gpd = (bGPdata *)ob->data;
5381  if (gpd == NULL) {
5382  return false;
5383  }
5384 
5386 
5387  return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
5388 }
5389 
5391 {
5393  bGPdata *gpd = (bGPdata *)ob->data;
5394  const float threshold = RNA_float_get(op->ptr, "threshold");
5395  const bool unselected = RNA_boolean_get(op->ptr, "use_unselected");
5396 
5397  /* sanity checks */
5398  if (ELEM(NULL, gpd)) {
5399  return OPERATOR_CANCELLED;
5400  }
5401 
5402  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
5403 
5404  if (is_curve_edit) {
5405  /* TODO: merge curve points by distance */
5406  }
5407  else {
5408  /* Go through each editable selected stroke */
5409  GP_EDITABLE_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
5410  if (gps->flag & GP_STROKE_SELECT) {
5411  BKE_gpencil_stroke_merge_distance(gpd, gpf_, gps, threshold, unselected);
5412  }
5413  }
5414  GP_EDITABLE_STROKES_END(gpstroke_iter);
5415  }
5416 
5417  /* notifiers */
5420 
5421  return OPERATOR_FINISHED;
5422 }
5423 
5425 {
5426  PropertyRNA *prop;
5427 
5428  /* identifiers */
5429  ot->name = "Merge by Distance";
5430  ot->idname = "GPENCIL_OT_stroke_merge_by_distance";
5431  ot->description = "Merge points by distance";
5432 
5433  /* api callbacks */
5436 
5437  /* flags */
5439 
5440  /* properties */
5441  prop = RNA_def_float(ot->srna, "threshold", 0.001f, 0.0f, 100.0f, "Threshold", "", 0.0f, 100.0f);
5442  /* avoid re-using last var */
5444 
5445  prop = RNA_def_boolean(
5446  ot->srna, "use_unselected", 0, "Unselected", "Use whole stroke, not only selected points");
5448 }
5449 
5452 /* -------------------------------------------------------------------- */
5456 typedef enum eGP_NormalizeMode {
5460 
5462 {
5464  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
5465  return false;
5466  }
5467  bGPdata *gpd = (bGPdata *)ob->data;
5468  if (gpd == NULL) {
5469  return false;
5470  }
5471 
5473 
5474  return ((gpl != NULL) && (ob->mode == OB_MODE_EDIT_GPENCIL));
5475 }
5476 
5478 {
5479  uiLayout *layout = op->layout;
5480  uiLayout *row;
5481 
5482  const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode");
5483 
5484  uiLayoutSetPropSep(layout, true);
5485  uiLayoutSetPropDecorate(layout, false);
5486  row = uiLayoutRow(layout, true);
5487  uiItemR(row, op->ptr, "mode", 0, NULL, ICON_NONE);
5488 
5489  if (mode == GP_NORMALIZE_THICKNESS) {
5490  row = uiLayoutRow(layout, true);
5491  uiItemR(row, op->ptr, "value", 0, NULL, ICON_NONE);
5492  }
5493  else if (mode == GP_NORMALIZE_OPACITY) {
5494  row = uiLayoutRow(layout, true);
5495  uiItemR(row, op->ptr, "factor", 0, NULL, ICON_NONE);
5496  }
5497 }
5498 
5500 {
5502 
5503  /* Sanity checks. */
5504  if (ELEM(NULL, gpd)) {
5505  return OPERATOR_CANCELLED;
5506  }
5507 
5508  const eGP_NormalizeMode mode = RNA_enum_get(op->ptr, "mode");
5509  const int value = RNA_int_get(op->ptr, "value");
5510  const float factor = RNA_float_get(op->ptr, "factor");
5511 
5512  /* Go through each editable + selected stroke. */
5513  const bool is_multiedit = (bool)GPENCIL_MULTIEDIT_SESSIONS_ON(gpd);
5514  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
5515 
5516  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
5517  bGPDframe *init_gpf = (is_multiedit) ? gpl->frames.first : gpl->actframe;
5518 
5519  for (bGPDframe *gpf = init_gpf; gpf; gpf = gpf->next) {
5520  if ((gpf == gpl->actframe) || ((gpf->flag & GP_FRAME_SELECT) && (is_multiedit))) {
5521 
5522  if (gpf == NULL) {
5523  continue;
5524  }
5525 
5526  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
5527 
5528  /* Skip strokes that are invalid for current view. */
5529  if (ED_gpencil_stroke_can_use(C, gps) == false) {
5530  continue;
5531  }
5532  bool is_curve_ready = (gps->editcurve != NULL);
5533  bool selected = (is_curve_edit && is_curve_ready) ?
5534  (gps->editcurve->flag & GP_CURVE_SELECT) :
5535  (gps->flag & GP_STROKE_SELECT);
5536  if (!selected) {
5537  continue;
5538  }
5539 
5540  float stroke_thickness_inv = 1.0f / max_ii(gps->thickness, 1);
5541  /* Fill opacity need to be managed before. */
5542  if (mode == GP_NORMALIZE_OPACITY) {
5543  gps->fill_opacity_fac = factor;
5544  CLAMP(gps->fill_opacity_fac, 0.0f, 1.0f);
5545  }
5546 
5547  /* Loop all Polyline points. */
5548  if (!is_curve_edit || !is_curve_ready) {
5549  for (int i = 0; i < gps->totpoints; i++) {
5550  bGPDspoint *pt = &gps->points[i];
5551  if (mode == GP_NORMALIZE_THICKNESS) {
5552  pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f);
5553  }
5554  else if (mode == GP_NORMALIZE_OPACITY) {
5555  pt->strength = factor;
5556  CLAMP(pt->strength, 0.0f, 1.0f);
5557  }
5558  }
5559  }
5560  else {
5561  /* Loop all Bezier points. */
5562  for (int i = 0; i < gps->editcurve->tot_curve_points; i++) {
5563  bGPDcurve_point *gpc_pt = &gps->editcurve->curve_points[i];
5564  if (mode == GP_NORMALIZE_THICKNESS) {
5565  gpc_pt->pressure = max_ff((float)value * stroke_thickness_inv, 0.0f);
5566  }
5567  else if (mode == GP_NORMALIZE_OPACITY) {
5568  gpc_pt->strength = factor;
5569  CLAMP(gpc_pt->strength, 0.0f, 1.0f);
5570  }
5571  }
5572 
5573  gps->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
5575  }
5576  }
5577  /* If not multi-edit, exit loop. */
5578  if (!is_multiedit) {
5579  break;
5580  }
5581  }
5582  }
5583  }
5584  CTX_DATA_END;
5585 
5586  /* notifiers */
5589 
5590  return OPERATOR_FINISHED;
5591 }
5592 
5594 {
5595  static const EnumPropertyItem prop_gpencil_normalize_modes[] = {
5597  "THICKNESS",
5598  0,
5599  "Thickness",
5600  "Normalizes the stroke thickness by making all points use the same thickness value"},
5602  "OPACITY",
5603  0,
5604  "Opacity",
5605  "Normalizes the stroke opacity by making all points use the same opacity value"},
5606  {0, NULL, 0, NULL, NULL},
5607  };
5608 
5609  /* identifiers */
5610  ot->name = "Normalize Stroke";
5611  ot->idname = "GPENCIL_OT_stroke_normalize";
5612  ot->description = "Normalize stroke attributes";
5613 
5614  /* api callbacks */
5618 
5619  /* flags */
5621 
5622  /* props */
5623  ot->prop = RNA_def_enum(
5624  ot->srna, "mode", prop_gpencil_normalize_modes, 0, "Mode", "Attribute to be normalized");
5625  RNA_def_float(ot->srna, "factor", 1.0f, 0.0f, 1.0f, "Factor", "", 0.0f, 1.0f);
5626  RNA_def_int(ot->srna, "value", 10, 0, 1000, "Value", "Value", 0, 1000);
5627 }
5628 
void BKE_brush_gpencil_paint_presets(struct Main *bmain, struct ToolSettings *ts, bool reset)
Definition: brush.cc:1308
void BKE_brush_gpencil_weight_presets(struct Main *bmain, struct ToolSettings *ts, bool reset)
Definition: brush.cc:1529
void BKE_brush_gpencil_sculpt_presets(struct Main *bmain, struct ToolSettings *ts, bool reset)
Definition: brush.cc:1455
void BKE_brush_gpencil_vertex_presets(struct Main *bmain, struct ToolSettings *ts, bool reset)
Definition: brush.cc:1409
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
struct Base * CTX_data_active_base(const bContext *C)
Definition: context.c:1358
#define CTX_DATA_BEGIN(C, Type, instance, member)
Definition: BKE_context.h:269
struct ViewLayer * CTX_data_view_layer(const bContext *C)
Definition: context.c:1100
struct bGPDlayer * CTX_data_active_gpencil_layer(const bContext *C)
Definition: context.c:1450
struct Depsgraph * CTX_data_ensure_evaluated_depsgraph(const bContext *C)
Definition: context.c:1528
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1353
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:784
#define CTX_DATA_COUNT(C, member)
Definition: BKE_context.h:290
struct wmMsgBus * CTX_wm_message_bus(const bContext *C)
Definition: context.c:770
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct Main * CTX_data_main(const bContext *C)
Definition: context.c:1074
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1282
#define CTX_DATA_END
Definition: BKE_context.h:278
support for deformation groups and hooks.
void BKE_defgroup_copy_list(struct ListBase *outbase, const struct ListBase *inbase)
void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1155
void BKE_gpencil_layer_active_set(struct bGPdata *gpd, struct bGPDlayer *active)
Definition: gpencil.c:1601
struct bGPDstroke * BKE_gpencil_stroke_duplicate(struct bGPDstroke *gps_src, bool dup_points, bool dup_curve)
Definition: gpencil.c:855
struct bGPDlayer * BKE_gpencil_layer_active_get(struct bGPdata *gpd)
Definition: gpencil.c:1558
void BKE_gpencil_free_stroke_weights(struct bGPDstroke *gps)
Definition: gpencil.c:361
int BKE_gpencil_object_material_ensure(struct Main *bmain, struct Object *ob, struct Material *material)
Definition: gpencil.c:1720
void BKE_gpencil_layer_mask_copy(const struct bGPDlayer *gpl_src, struct bGPDlayer *gpl_dst)
void BKE_gpencil_palette_ensure(struct Main *bmain, struct Scene *scene)
Definition: gpencil.c:2241
struct bGPDcurve * BKE_gpencil_stroke_editcurve_new(int tot_curve_points)
Definition: gpencil.c:821
void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps)
Definition: gpencil.c:1161
struct bGPDlayer * BKE_gpencil_layer_addnew(struct bGPdata *gpd, const char *name, bool setactive, bool add_to_header)
Definition: gpencil.c:621
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:1396
int BKE_gpencil_object_material_index_get(struct Object *ob, struct Material *ma)
Definition: gpencil.c:2199
struct bGPDframe * BKE_gpencil_layer_frame_find(struct bGPDlayer *gpl, int cframe)
Definition: gpencil.c:1216
void BKE_gpencil_free_stroke(struct bGPDstroke *gps)
Definition: gpencil.c:391
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1232
#define GPENCIL_STRENGTH_MIN
Definition: BKE_gpencil.h:324
struct bGPdata * BKE_gpencil_data_addnew(struct Main *bmain, const char name[])
Definition: gpencil.c:705
void BKE_gpencil_layer_transform_matrix_get(const struct Depsgraph *depsgraph, struct Object *obact, struct bGPDlayer *gpl, float diff_mat[4][4])
void BKE_gpencil_layer_mask_cleanup_all_layers(struct bGPdata *gpd)
Definition: gpencil.c:1525
void BKE_gpencil_stroke_weights_duplicate(struct bGPDstroke *gps_src, struct bGPDstroke *gps_dst)
Definition: gpencil.c:834
@ GP_GETFRAME_ADD_NEW
Definition: BKE_gpencil.h:341
@ GP_GETFRAME_USE_PREV
Definition: BKE_gpencil.h:338
void BKE_gpencil_layer_copy_settings(const struct bGPDlayer *gpl_src, struct bGPDlayer *gpl_dst)
void BKE_gpencil_stroke_editcurve_update(struct bGPdata *gpd, struct bGPDlayer *gpl, struct bGPDstroke *gps)
void BKE_gpencil_editcurve_subdivide(struct bGPDstroke *gps, int cuts)
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_stroke_flip(struct bGPDstroke *gps)
void BKE_gpencil_stroke_join(struct bGPDstroke *gps_a, struct bGPDstroke *gps_b, bool leave_gaps, bool fit_thickness, bool smooth)
bool BKE_gpencil_stroke_smooth_thickness(struct bGPDstroke *gps, int point_index, float influence, int iterations, struct bGPDstroke *r_gps)
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, const float sharp_threshold)
bool BKE_gpencil_stroke_smooth_strength(struct bGPDstroke *gps, int point_index, float influence, int iterations, struct bGPDstroke *r_gps)
void BKE_gpencil_stroke_simplify_fixed(struct bGPdata *gpd, struct bGPDstroke *gps)
bool BKE_gpencil_stroke_smooth_uv(struct bGPDstroke *gps, int point_index, float influence, int iterations, struct bGPDstroke *r_gps)
struct bGPDstroke * BKE_gpencil_stroke_delete_tagged_points(struct bGPdata *gpd, struct bGPDframe *gpf, struct bGPDstroke *gps, struct bGPDstroke *next_stroke, int tag_flags, bool select, bool flat_cap, int limit)
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)
void BKE_gpencil_stroke_merge_distance(struct bGPdata *gpd, struct bGPDframe *gpf, struct bGPDstroke *gps, float threshold, bool use_unselected)
bool BKE_gpencil_stroke_smooth_point(struct bGPDstroke *gps, int point_index, float influence, int iterations, bool smooth_caps, bool keep_shape, struct bGPDstroke *r_gps)
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:313
char * BKE_id_to_unique_string_key(const struct ID *id)
Definition: lib_id.c:1879
General operations, lookup, etc. for materials.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
Definition: material.c:805
bool BKE_object_material_slot_remove(struct Main *bmain, struct Object *ob)
Definition: material.c:1248
struct Material * BKE_object_material_get(struct Object *ob, short act)
Definition: material.c:687
bool BKE_object_material_slot_used(struct Object *object, short actcol)
Definition: material.c:452
struct Material * BKE_gpencil_material(struct Object *ob, short act)
Definition: material.c:795
General operations, lookup, etc. for blender objects.
bool BKE_paint_ensure(struct ToolSettings *ts, struct Paint **r_paint)
Definition: paint.c:1042
void BKE_paint_toolslots_brush_validate(struct Main *bmain, struct Paint *paint)
void BKE_reportf(ReportList *reports, eReportType type, const char *format,...) ATTR_PRINTF_FORMAT(3
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
void BKE_scene_graph_update_for_newframe(struct Depsgraph *depsgraph)
Definition: scene.cc:2728
#define BLI_assert(a)
Definition: BLI_assert.h:46
BLI_INLINE void * BLI_ghashIterator_getKey(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:298
bool BLI_ghash_haskey(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:822
BLI_INLINE void * BLI_ghashIterator_getValue(GHashIterator *ghi) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.h:302
GHash * BLI_ghash_str_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
#define GHASH_ITER(gh_iter_, ghash_)
Definition: BLI_ghash.h:321
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
void * BLI_ghash_lookup(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:734
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:710
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:863
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:755
bool BLI_lasso_is_point_inside(const int mcoords[][2], unsigned int mcoords_len, int sx, int sy, int error_value)
Definition: lasso_2d.c:38
void BLI_lasso_boundbox(struct rcti *rect, const int mcoords[][2], unsigned int mcoords_len)
Definition: lasso_2d.c:15
BLI_INLINE bool BLI_listbase_is_empty(const struct ListBase *lb)
Definition: BLI_listbase.h:269
void BLI_addhead(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:60
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void BLI_freelinkN(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:239
#define LISTBASE_FOREACH_MUTABLE(type, var, list)
Definition: BLI_listbase.h:354
BLI_INLINE void BLI_listbase_clear(struct ListBase *lb)
Definition: BLI_listbase.h:273
void BLI_insertlinkafter(struct ListBase *listbase, void *vprevlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:301
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:265
void BLI_addtail(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:80
void BLI_remlink(struct ListBase *listbase, void *vlink) ATTR_NONNULL(1)
Definition: listbase.c:100
void BLI_insertlinkbefore(struct ListBase *listbase, void *vnextlink, void *vnewlink) ATTR_NONNULL(1)
Definition: listbase.c:340
void * BLI_findlink(const struct ListBase *listbase, int number) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
void * BLI_findstring(const struct ListBase *listbase, const char *id, int offset) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
MINLINE float max_ff(float a, float b)
MINLINE int max_ii(int a, int b)
MINLINE float interpf(float a, float b, float t)
#define M_PI
Definition: BLI_math_base.h:20
void invert_m4_m4_safe(float Ainv[4][4], const float A[4][4])
Definition: math_matrix.c:3153
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
MINLINE void copy_v4_v4(float r[4], const float a[4])
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:867
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])
MINLINE void mul_v3_fl(float r[3], float f)
MINLINE void copy_v3_v3(float r[3], const float a[3])
void interp_v4_v4v4(float r[4], const float a[4], const float b[4], float t)
Definition: math_vector.c:38
void interp_v3_v3v3(float r[3], const float a[3], const float b[3], float t)
Definition: math_vector.c:29
void mid_v3_v3v3(float r[3], const float a[3], const float b[3])
Definition: math_vector.c:237
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, int x, int y)
size_t size_t char * BLI_sprintfN(const char *__restrict format,...) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1) ATTR_MALLOC ATTR_PRINTF_FORMAT(1
char * BLI_strncpy(char *__restrict dst, const char *__restrict src, size_t maxncpy) ATTR_NONNULL()
Definition: string.c:64
#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:35
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:771
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:834
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:791
#define BEZT_DESEL_ALL(bezt)
#define BEZT_SEL_IDX(bezt, i)
#define MAX_NAME
Definition: DNA_defs.h:48
@ 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
@ 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:52
@ GP_REPROJECT_VIEW
Definition: ED_gpencil.h:58
@ GP_REPROJECT_CURSOR
Definition: ED_gpencil.h:62
@ GP_REPROJECT_SIDE
Definition: ED_gpencil.h:55
@ GP_REPROJECT_TOP
Definition: ED_gpencil.h:56
@ GP_REPROJECT_FRONT
Definition: ED_gpencil.h:54
@ GP_REPROJECT_SURFACE
Definition: ED_gpencil.h:60
#define IS_AUTOKEY_ON(scene)
struct Base * ED_object_add_duplicate(struct Main *bmain, struct Scene *scene, struct ViewLayer *view_layer, struct Base *base, eDupli_ID_Flags dupflag)
Definition: object_add.cc:3632
void ED_object_posemode_set_for_weight_paint(struct bContext *C, struct Main *bmain, struct Object *ob, bool is_mode_set)
Definition: object_modes.c:357
void ED_outliner_select_sync_from_object_tag(struct bContext *C)
bool ED_operator_view3d_active(struct bContext *C)
Definition: screen_ops.c:225
SnapObjectContext * ED_transform_snap_object_context_create(struct Scene *scene, int flag)
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:901
_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 Output data from inside of a node group A color picker Mix two input colors RGB to Convert a color s luminance to a grayscale value Generate a normal vector and a dot product Bright Control the brightness and contrast of the input color Vector Map an input vectors to used to fine tune the interpolation of the input Camera Retrieve information about the camera and how it relates to the current shading point s position CLAMP
@ PROP_SKIP_SAVE
Definition: RNA_types.h:218
@ PROP_HIDDEN
Definition: RNA_types.h:216
#define C
Definition: RandGen.cpp:25
void uiLayoutSetPropSep(uiLayout *layout, bool is_sep)
uiLayout * uiLayoutRow(uiLayout *layout, bool align)
void uiItemR(uiLayout *layout, struct PointerRNA *ptr, const char *propname, int flag, const char *name, int icon)
void uiLayoutSetPropDecorate(uiLayout *layout, bool is_sep)
#define V2D_IS_CLIPPED
Definition: UI_view2d.h:25
@ OPTYPE_DEPENDS_ON_CURSOR
Definition: WM_types.h:184
@ OPTYPE_UNDO
Definition: WM_types.h:148
@ OPTYPE_REGISTER
Definition: WM_types.h:146
#define NC_GEOM
Definition: WM_types.h:343
#define ND_DRAW
Definition: WM_types.h:410
#define ND_DATA
Definition: WM_types.h:456
#define ND_GPENCIL_EDITMODE
Definition: WM_types.h:451
#define ND_MODE
Definition: WM_types.h:393
#define NC_SCENE
Definition: WM_types.h:328
#define ND_TOOLSETTINGS
Definition: WM_types.h:397
#define NA_EDITED
Definition: WM_types.h:523
#define ND_SELECT
Definition: WM_types.h:455
#define NC_GPENCIL
Definition: WM_types.h:349
@ WM_OP_EXEC_DEFAULT
Definition: WM_types.h:208
#define ND_SPACE_VIEW3D
Definition: WM_types.h:471
#define NC_OBJECT
Definition: WM_types.h:329
#define NC_SPACE
Definition: WM_types.h:342
#define NA_SELECTED
Definition: WM_types.h:528
ATTR_WARN_UNUSED_RESULT const BMVert const BMEdge * e
unsigned int U
Definition: btGjkEpa3.h:78
Scene scene
const Depsgraph * depsgraph
void * user_data
int len
Definition: draw_manager.c:108
static bool is_inside(int x, int y, int cols, int rows)
Definition: filesel.c:706
static bool gpencil_sculptmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:456
void ED_gpencil_strokes_copybuf_free(void)
void GPENCIL_OT_recalc_geometry(wmOperatorType *ot)
void GPENCIL_OT_snap_to_cursor(wmOperatorType *ot)
static void layer_new_name_get(bGPdata *gpd, char *rname)
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:677
static int gpencil_stroke_simplify_exec(bContext *C, wmOperator *op)
static bool annotation_actframe_delete_poll(bContext *C)
eGP_NormalizeMode
@ GP_NORMALIZE_OPACITY
@ GP_NORMALIZE_THICKNESS
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:428
void GPENCIL_OT_stroke_simplify_fixed(wmOperatorType *ot)
void GPENCIL_OT_copy(wmOperatorType *ot)
static GHash * gpencil_strokes_copypastebuf_colors
static void gpencil_duplicate_points(bGPdata *gpd, const bGPDstroke *gps, ListBase *new_strokes, const char *layername)
Definition: gpencil_edit.c:832
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:271
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:350
static bool gpencil_weightmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:565
void GPENCIL_OT_stroke_flip(wmOperatorType *ot)
static bool gpencil_strokes_edit3d_poll(bContext *C)
Definition: gpencil_edit.c:103
static int gpencil_editmode_toggle_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:152
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 int gpencil_move_to_layer_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
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)
@ GP_STROKE_CYCLIC_OPEN
@ GP_STROKE_CYCLIC_CLOSE
@ GP_STROKE_CYCLIC_TOGGLE
static int gpencil_duplicate_exec(bContext *C, wmOperator *op)
Definition: gpencil_edit.c:911
static void gpencil_smooth_stroke(bContext *C, wmOperator *op)
static void gpencil_stroke_normalize_ui(bContext *UNUSED(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:649
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:686
static bool gpencil_editmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:118
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:90
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:231
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:760
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:260
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)
void GPENCIL_OT_stroke_normalize(wmOperatorType *ot)
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:466
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 bool gpencil_stroke_normalize_poll(bContext *C)
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:810
static bool gpencil_paintmode_toggle_poll(bContext *C)
Definition: gpencil_edit.c:340
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:575
@ GP_STROKE_CAPS_TOGGLE_START
@ GP_STROKE_CAPS_TOGGLE_END
@ GP_STROKE_CAPS_TOGGLE_BOTH
@ GP_STROKE_CAPS_TOGGLE_DEFAULT
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:313
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:788
void GPENCIL_OT_sculptmode_toggle(wmOperatorType *ot)
Definition: gpencil_edit.c:541
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:134
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)
static int gpencil_stroke_normalize_exec(bContext *C, wmOperator *op)
#define GP_EDITABLE_CURVES_END(gpstroke_iter)
#define GP_EDITABLE_CURVES_BEGIN(gpstroke_iter, C, gpl, gps, gpc)
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)
@ GP_STROKE_JOIN
@ GP_STROKE_JOINCOPY
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
ccl_gpu_kernel_postfix ccl_global float int int int int float threshold
ccl_gpu_kernel_postfix ccl_global float int int int int float bool int offset
void *(* MEM_malloc_arrayN)(size_t len, size_t size, const char *str)
Definition: mallocn.c:34
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
void *(* MEM_dupallocN)(const void *vmemh)
Definition: mallocn.c:28
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:31
void *(* MEM_mallocN)(size_t len, const char *str)
Definition: mallocn.c:33
static ulong * next
#define G(x, y, z)
#define floorf(x)
Definition: metal/compat.h:224
static void area(int d1, int d2, int e1, int e2, float weights[2])
T length(const vec_base< T, Size > &a)
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:1000
bool RNA_property_is_set(PointerRNA *ptr, PropertyRNA *prop)
Definition: rna_access.c:5271
PropertyRNA * RNA_struct_find_property(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:717
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4910
void RNA_property_string_get(PointerRNA *ptr, PropertyRNA *prop, char *value)
Definition: rna_access.c:3149
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4957
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4863
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:5004
void RNA_property_string_set(PointerRNA *ptr, PropertyRNA *prop, const char *value)
Definition: rna_access.c:3239
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:3836
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3493
PropertyRNA * RNA_def_string(StructOrFunctionRNA *cont_, const char *identifier, const char *default_value, int maxlen, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3687
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1490
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:3597
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:3783
static const int steps
Definition: sky_nishita.cpp:19
#define min(a, b)
Definition: sort.c:35
struct Object * object
float vec[3][3]
struct BrushGpencilSettings * gpencil_settings
const int(* mcoords)[2]
char name[66]
Definition: DNA_ID.h:378
void * last
Definition: DNA_listBase.h:31
void * first
Definition: DNA_listBase.h:31
struct MDeformWeight * dw
Definition: BKE_main.h:121
ListBase materials
Definition: BKE_main.h:174
int restore_mode
void * data
struct Brush * brush
struct ToolSettings * toolsettings
struct RenderData r
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 vertex_group_names
ListBase layers
int vertex_group_active_index
bGPDframe * gpf
bGPDstroke * gps
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:919
const char * name
Definition: WM_types.h:888
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:935
const char * idname
Definition: WM_types.h:890
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:943
bool(* poll_property)(const struct bContext *C, struct wmOperator *op, const PropertyRNA *prop) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:949
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:927
struct StructRNA * srna
Definition: WM_types.h:969
const char * description
Definition: WM_types.h:893
void(* ui)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:954
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
PropertyRNA * prop
Definition: WM_types.h:981
struct ReportList * reports
struct uiLayout * layout
struct PointerRNA * ptr
float max
void WM_main_add_notifier(unsigned int type, void *reference)
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
int WM_operator_name_call(bContext *C, const char *opstring, wmOperatorCallContext context, PointerRNA *properties, const wmEvent *event)
wmOperatorType * ot
Definition: wm_files.c:3479
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_operator_props_dialog_popup(bContext *C, wmOperator *op, int width)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
void WM_toolsystem_update_from_context_view3d(bContext *C)