Blender  V3.3
gpencil_select.c
Go to the documentation of this file.
1 /* SPDX-License-Identifier: GPL-2.0-or-later
2  * Copyright 2014 Blender Foundation. */
3 
8 #include <math.h>
9 #include <stddef.h>
10 #include <stdio.h>
11 #include <stdlib.h>
12 #include <string.h>
13 
14 #include "MEM_guardedalloc.h"
15 
16 #include "BLI_blenlib.h"
17 #include "BLI_ghash.h"
18 #include "BLI_lasso_2d.h"
19 #include "BLI_math_vector.h"
20 #include "BLI_rand.h"
21 #include "BLI_utildefines.h"
22 
23 #include "DNA_gpencil_types.h"
24 #include "DNA_material_types.h"
25 #include "DNA_object_types.h"
26 #include "DNA_scene_types.h"
27 #include "DNA_screen_types.h"
28 #include "DNA_space_types.h"
29 
30 #include "BKE_context.h"
31 #include "BKE_gpencil.h"
32 #include "BKE_gpencil_curve.h"
33 #include "BKE_gpencil_geom.h"
34 #include "BKE_material.h"
35 #include "BKE_report.h"
36 
37 #include "UI_interface.h"
38 #include "UI_resources.h"
39 
40 #include "WM_api.h"
41 #include "WM_types.h"
42 
43 #include "RNA_access.h"
44 #include "RNA_define.h"
45 
46 #include "UI_view2d.h"
47 
48 #include "ED_gpencil.h"
49 #include "ED_select_utils.h"
50 #include "ED_view3d.h"
51 
52 #include "DEG_depsgraph.h"
53 #include "DEG_depsgraph_query.h"
54 
55 #include "gpencil_intern.h"
56 
57 /* -------------------------------------------------------------------- */
61 /* Convert sculpt mask mode to Select mode */
63 {
65  return GP_SELECTMODE_POINT;
66  }
68  return GP_SELECTMODE_STROKE;
69  }
71  return GP_SELECTMODE_SEGMENT;
72  }
73  return GP_SELECTMODE_POINT;
74 }
75 
76 /* Convert vertex mask mode to Select mode */
78 {
80  return GP_SELECTMODE_POINT;
81  }
83  return GP_SELECTMODE_STROKE;
84  }
86  return GP_SELECTMODE_SEGMENT;
87  }
88  return GP_SELECTMODE_POINT;
89 }
90 
92 {
95 
96  if (GPENCIL_SCULPT_MODE(gpd)) {
98  return false;
99  }
100  }
101 
102  if (GPENCIL_VERTEX_MODE(gpd)) {
104  return false;
105  }
106  }
107 
108  /* We just need some visible strokes,
109  * and to be in edit-mode or other modes only to catch event. */
110  if (GPENCIL_ANY_MODE(gpd)) {
111  /* TODO: include a check for visible strokes? */
112  if (gpd->layers.first) {
113  return true;
114  }
115  }
116 
117  return false;
118 }
119 
121  const float diff_mat[4][4],
122  const float co[3],
123  int r_co[2])
124 {
125  float parent_co[3];
126  mul_v3_m4v3(parent_co, diff_mat, co);
127  int screen_co[2];
129  region, parent_co, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
130  V3D_PROJ_RET_OK) {
131  if (!ELEM(V2D_IS_CLIPPED, screen_co[0], screen_co[1])) {
132  copy_v2_v2_int(r_co, screen_co);
133  return true;
134  }
135  }
136  r_co[0] = V2D_IS_CLIPPED;
137  r_co[1] = V2D_IS_CLIPPED;
138  return false;
139 }
140 
141 /* helper to deselect all selected strokes/points */
143 {
144  /* Set selection index to 0. */
146  bGPdata *gpd = ob->data;
147  gpd->select_last_index = 0;
148 
149  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
150  /* deselect stroke and its points if selected */
151  if (gps->flag & GP_STROKE_SELECT) {
152  bGPDspoint *pt;
153  int i;
154 
155  /* deselect points */
156  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
157  pt->flag &= ~GP_SPOINT_SELECT;
158  }
159 
160  /* deselect stroke itself too */
161  gps->flag &= ~GP_STROKE_SELECT;
163  }
164 
165  /* deselect curve and curve points */
166  if (gps->editcurve != NULL) {
167  bGPDcurve *gpc = gps->editcurve;
168  for (int j = 0; j < gpc->tot_curve_points; j++) {
169  bGPDcurve_point *gpc_pt = &gpc->curve_points[j];
170  BezTriple *bezt = &gpc_pt->bezt;
171  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
172  BEZT_DESEL_ALL(bezt);
173  }
174 
175  gpc->flag &= ~GP_CURVE_SELECT;
176  }
177  }
178  CTX_DATA_END;
179 }
180 
181 static void select_all_stroke_points(bGPdata *gpd, bGPDstroke *gps, bool select)
182 {
183  for (int i = 0; i < gps->totpoints; i++) {
184  bGPDspoint *pt = &gps->points[i];
185  if (select) {
186  pt->flag |= GP_SPOINT_SELECT;
187  }
188  else {
189  pt->flag &= ~GP_SPOINT_SELECT;
190  }
191  }
192 
193  if (select) {
194  gps->flag |= GP_STROKE_SELECT;
196  }
197  else {
198  gps->flag &= ~GP_STROKE_SELECT;
200  }
201 }
202 
203 static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
204 {
205  for (int i = 0; i < gpc->tot_curve_points; i++) {
206  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
207  BezTriple *bezt = &gpc_pt->bezt;
208  if (deselect == false) {
209  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
210  BEZT_SEL_ALL(bezt);
211  }
212  else {
213  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
214  BEZT_DESEL_ALL(bezt);
215  }
216  }
217 
218  if (deselect == false) {
219  gpc->flag |= GP_CURVE_SELECT;
220  gps->flag |= GP_STROKE_SELECT;
222  }
223  else {
224  gpc->flag &= ~GP_CURVE_SELECT;
225  gps->flag &= ~GP_STROKE_SELECT;
227  }
228 }
229 
232 /* -------------------------------------------------------------------- */
237 {
239 
240  /* We just need some visible strokes,
241  * and to be in edit-mode or other modes only to catch event. */
242  if (GPENCIL_ANY_MODE(gpd)) {
243  if (gpd->layers.first) {
244  return true;
245  }
246  }
247 
248  return false;
249 }
250 
252 {
254  int action = RNA_enum_get(op->ptr, "action");
255  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
256 
257  if (gpd == NULL) {
258  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
259  return OPERATOR_CANCELLED;
260  }
261 
262  /* If not edit/sculpt mode, the event has been caught but not processed. */
263  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
264  return OPERATOR_CANCELLED;
265  }
266 
267  /* For sculpt mode, if mask is disable, only allows deselect */
268  if (GPENCIL_SCULPT_MODE(gpd)) {
270  if ((!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) && (action != SEL_DESELECT)) {
271  return OPERATOR_CANCELLED;
272  }
273  }
274 
275  if (is_curve_edit) {
277  }
278  else {
280  }
281 
282  /* updates */
284 
285  /* copy on write tag is needed, or else no refresh happens */
287 
290  return OPERATOR_FINISHED;
291 }
292 
294 {
295  /* identifiers */
296  ot->name = "(De)select All Strokes";
297  ot->idname = "GPENCIL_OT_select_all";
298  ot->description = "Change selection of all Grease Pencil strokes currently visible";
299 
300  /* callbacks */
303 
304  /* flags */
306 
308 }
309 
312 /* -------------------------------------------------------------------- */
317 {
319  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
320 
321  if (gpd == NULL) {
322  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
323  return OPERATOR_CANCELLED;
324  }
325 
326  /* If not edit/sculpt mode, the event has been caught but not processed. */
327  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
328  return OPERATOR_CANCELLED;
329  }
330 
331  if (is_curve_edit) {
332  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
333  {
334  if (gpc->flag & GP_CURVE_SELECT) {
335  for (int i = 0; i < gpc->tot_curve_points; i++) {
336  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
337  BezTriple *bezt = &gpc_pt->bezt;
338  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
339  BEZT_SEL_ALL(bezt);
340  }
341  }
342  }
343  GP_EDITABLE_CURVES_END(gps_iter);
344  }
345  else {
346  /* select all points in selected strokes */
347  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
348  if (gps->flag & GP_STROKE_SELECT) {
349  bGPDspoint *pt;
350  int i;
351 
352  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
353  pt->flag |= GP_SPOINT_SELECT;
354  }
355  }
356  }
357  CTX_DATA_END;
358  }
359 
360  /* updates */
362 
363  /* copy on write tag is needed, or else no refresh happens */
365 
368  return OPERATOR_FINISHED;
369 }
370 
372 {
373  /* identifiers */
374  ot->name = "Select Linked";
375  ot->idname = "GPENCIL_OT_select_linked";
376  ot->description = "Select all points in same strokes as already selected points";
377 
378  /* callbacks */
381 
382  /* flags */
384 }
385 
388 /* -------------------------------------------------------------------- */
393 {
395  const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends");
396  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
397 
398  if (gpd == NULL) {
399  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
400  return OPERATOR_CANCELLED;
401  }
402 
403  /* If not edit/sculpt mode, the event has been caught but not processed. */
404  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
405  return OPERATOR_CANCELLED;
406  }
407 
408  bool changed = false;
409  if (is_curve_edit) {
410  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
411  {
412  if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
413  int idx = 0;
414  int start = 0;
415  if (unselect_ends) {
416  start = 1;
417  }
418 
419  for (int i = start; i < gpc->tot_curve_points; i++) {
420  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
421  if ((idx % 2) == 0) {
422  gpc_pt->flag |= GP_SPOINT_SELECT;
423  BEZT_SEL_ALL(&gpc_pt->bezt);
424  }
425  else {
426  gpc_pt->flag &= ~GP_SPOINT_SELECT;
427  BEZT_DESEL_ALL(&gpc_pt->bezt);
428  }
429  idx++;
430  }
431 
432  if (unselect_ends) {
433  bGPDcurve_point *gpc_pt = &gpc->curve_points[0];
434  gpc_pt->flag &= ~GP_SPOINT_SELECT;
435  BEZT_DESEL_ALL(&gpc_pt->bezt);
436 
437  gpc_pt = &gpc->curve_points[gpc->tot_curve_points - 1];
438  gpc_pt->flag &= ~GP_SPOINT_SELECT;
439  BEZT_DESEL_ALL(&gpc_pt->bezt);
440  }
441 
443  changed = true;
444  }
445  }
446  GP_EDITABLE_CURVES_END(gps_iter);
447  }
448  else {
449  /* select all points in selected strokes */
450  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
451  if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
452  bGPDspoint *pt;
453  int row = 0;
454  int start = 0;
455  if (unselect_ends) {
456  start = 1;
457  }
458 
459  for (int i = start; i < gps->totpoints; i++) {
460  pt = &gps->points[i];
461  if ((row % 2) == 0) {
462  pt->flag |= GP_SPOINT_SELECT;
463  }
464  else {
465  pt->flag &= ~GP_SPOINT_SELECT;
466  }
467  row++;
468  }
469 
470  /* unselect start and end points */
471  if (unselect_ends) {
472  pt = &gps->points[0];
473  pt->flag &= ~GP_SPOINT_SELECT;
474 
475  pt = &gps->points[gps->totpoints - 1];
476  pt->flag &= ~GP_SPOINT_SELECT;
477  }
478 
479  changed = true;
480  }
481  }
482  CTX_DATA_END;
483  }
484 
485  if (changed) {
486  /* updates */
488 
489  /* copy on write tag is needed, or else no refresh happens */
491 
494  }
495 
496  return OPERATOR_FINISHED;
497 }
498 
500 {
501  /* identifiers */
502  ot->name = "Alternated";
503  ot->idname = "GPENCIL_OT_select_alternate";
504  ot->description = "Select alternative points in same strokes as already selected points";
505 
506  /* callbacks */
509 
510  /* flags */
512 
513  /* properties */
515  "unselect_ends",
516  false,
517  "Unselect Ends",
518  "Do not select the first and last point of the stroke");
519 }
520 
523 /* -------------------------------------------------------------------- */
528 {
532  if ((gpd == NULL) || (GPENCIL_NONE_EDIT_MODE(gpd))) {
533  return OPERATOR_CANCELLED;
534  }
535 
536  const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends");
537  const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
538  const float randfac = RNA_float_get(op->ptr, "ratio");
540  const int start = (unselect_ends) ? 1 : 0;
541  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
542 
543  int selectmode;
544  if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
546  }
547  else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
549  }
550  else {
551  selectmode = ts->gpencil_selectmode_edit;
552  }
553 
554  bool changed = false;
555  int seed_iter = seed;
556  int stroke_idx = 0;
557 
558  if (is_curve_edit) {
559  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
560  {
561  /* Only apply to unselected strokes (if select). */
562  if (select) {
563  if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) {
564  continue;
565  }
566  }
567  else {
568  if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) {
569  continue;
570  }
571  }
572 
573  /* Different seed by stroke. */
574  seed_iter += gps->totpoints + stroke_idx;
575  stroke_idx++;
576 
577  if (selectmode == GP_SELECTMODE_STROKE) {
578  RNG *rng = BLI_rng_new(seed_iter);
579  const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints;
580  bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false;
581  select_stroke ^= select;
582  /* Curve function has select parameter inverted. */
583  select_all_curve_points(gpd, gps, gps->editcurve, !select_stroke);
584  changed = true;
585  BLI_rng_free(rng);
586  }
587  else {
588  int elem_map_len = 0;
589  bGPDcurve_point **elem_map = MEM_mallocN(sizeof(*elem_map) * gpc->tot_curve_points,
590  __func__);
591  bGPDcurve_point *ptc;
592  for (int i = start; i < gpc->tot_curve_points; i++) {
593  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
594  elem_map[elem_map_len++] = gpc_pt;
595  }
596 
597  BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
598  const int count_select = elem_map_len * randfac;
599  for (int i = 0; i < count_select; i++) {
600  ptc = elem_map[i];
601  if (select) {
602  ptc->flag |= GP_SPOINT_SELECT;
603  BEZT_SEL_ALL(&ptc->bezt);
604  }
605  else {
606  ptc->flag &= ~GP_SPOINT_SELECT;
607  BEZT_DESEL_ALL(&ptc->bezt);
608  }
609  }
610  MEM_freeN(elem_map);
611 
612  /* unselect start and end points */
613  if (unselect_ends) {
614  bGPDcurve_point *gpc_pt = &gpc->curve_points[0];
615  gpc_pt->flag &= ~GP_SPOINT_SELECT;
616  BEZT_DESEL_ALL(&gpc_pt->bezt);
617 
618  gpc_pt = &gpc->curve_points[gpc->tot_curve_points - 1];
619  gpc_pt->flag &= ~GP_SPOINT_SELECT;
620  BEZT_DESEL_ALL(&gpc_pt->bezt);
621  }
622 
624  }
625 
626  changed = true;
627  }
628  GP_EDITABLE_CURVES_END(gps_iter);
629  }
630  else {
631  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
632  /* Only apply to unselected strokes (if select). */
633  if (select) {
634  if ((gps->flag & GP_STROKE_SELECT) || (gps->totpoints == 0)) {
635  continue;
636  }
637  }
638  else {
639  if (((gps->flag & GP_STROKE_SELECT) == 0) || (gps->totpoints == 0)) {
640  continue;
641  }
642  }
643 
644  /* Different seed by stroke. */
645  seed_iter += gps->totpoints + stroke_idx;
646  stroke_idx++;
647 
648  if (selectmode == GP_SELECTMODE_STROKE) {
649  RNG *rng = BLI_rng_new(seed_iter);
650  const unsigned int j = BLI_rng_get_uint(rng) % gps->totpoints;
651  bool select_stroke = ((gps->totpoints * randfac) <= j) ? true : false;
652  select_stroke ^= select;
653  select_all_stroke_points(gpd, gps, select_stroke);
654  changed = true;
655  BLI_rng_free(rng);
656  }
657  else {
658  int elem_map_len = 0;
659  bGPDspoint **elem_map = MEM_mallocN(sizeof(*elem_map) * gps->totpoints, __func__);
660  bGPDspoint *pt;
661  for (int i = start; i < gps->totpoints; i++) {
662  pt = &gps->points[i];
663  elem_map[elem_map_len++] = pt;
664  }
665 
666  BLI_array_randomize(elem_map, sizeof(*elem_map), elem_map_len, seed_iter);
667  const int count_select = elem_map_len * randfac;
668  for (int i = 0; i < count_select; i++) {
669  pt = elem_map[i];
670  if (select) {
671  pt->flag |= GP_SPOINT_SELECT;
672  }
673  else {
674  pt->flag &= ~GP_SPOINT_SELECT;
675  }
676  }
677  MEM_freeN(elem_map);
678 
679  /* unselect start and end points */
680  if (unselect_ends) {
681  pt = &gps->points[0];
682  pt->flag &= ~GP_SPOINT_SELECT;
683 
684  pt = &gps->points[gps->totpoints - 1];
685  pt->flag &= ~GP_SPOINT_SELECT;
686  }
687 
689  }
690 
691  changed = true;
692  }
693  CTX_DATA_END;
694  }
695 
696  if (changed) {
697  /* updates */
699 
700  /* copy on write tag is needed, or else no refresh happens */
702 
705  }
706 
707  return OPERATOR_FINISHED;
708 }
709 
711 {
712  /* identifiers */
713  ot->name = "Random";
714  ot->idname = "GPENCIL_OT_select_random";
715  ot->description = "Select random points for non selected strokes";
716 
717  /* callbacks */
720 
721  /* flags */
723 
724  /* properties */
727  "unselect_ends",
728  false,
729  "Unselect Ends",
730  "Do not select the first and last point of the stroke");
731 }
732 
735 /* -------------------------------------------------------------------- */
739 typedef enum eGP_SelectGrouped {
740  /* Select strokes in the same layer */
742 
743  /* Select strokes with the same color */
745 
746  /* TODO: All with same prefix -
747  * Useful for isolating all layers for a particular character for instance. */
748  /* TODO: All with same appearance - color/opacity/volumetric/fills ? */
750 
751 /* ----------------------------------- */
752 
753 /* On each visible layer, check for selected strokes - if found, select all others */
755 {
758  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
759 
760  bool changed = false;
761  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
763  bGPDstroke *gps;
764  bool found = false;
765 
766  if (gpf == NULL) {
767  continue;
768  }
769 
770  /* Search for a selected stroke */
771  for (gps = gpf->strokes.first; gps; gps = gps->next) {
772  if (ED_gpencil_stroke_can_use(C, gps)) {
773  if (gps->flag & GP_STROKE_SELECT) {
774  found = true;
775  break;
776  }
777  }
778  }
779 
780  /* Select all if found */
781  if (found) {
782  if (is_curve_edit) {
783  for (gps = gpf->strokes.first; gps; gps = gps->next) {
784  if (gps->editcurve != NULL && ED_gpencil_stroke_can_use(C, gps)) {
785  bGPDcurve *gpc = gps->editcurve;
786  for (int i = 0; i < gpc->tot_curve_points; i++) {
787  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
788  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
789  BEZT_SEL_ALL(&gpc_pt->bezt);
790  }
791  gpc->flag |= GP_CURVE_SELECT;
792  gps->flag |= GP_STROKE_SELECT;
794 
795  changed = true;
796  }
797  }
798  }
799  else {
800  for (gps = gpf->strokes.first; gps; gps = gps->next) {
801  if (ED_gpencil_stroke_can_use(C, gps)) {
802  bGPDspoint *pt;
803  int i;
804 
805  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
806  pt->flag |= GP_SPOINT_SELECT;
807  }
808 
809  gps->flag |= GP_STROKE_SELECT;
811 
812  changed = true;
813  }
814  }
815  }
816  }
817  }
818  CTX_DATA_END;
819 
820  return changed;
821 }
822 
823 /* Select all strokes with same colors as selected ones */
825 {
827  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
828  /* First, build set containing all the colors of selected strokes */
829  GSet *selected_colors = BLI_gset_int_new("GP Selected Colors");
830 
831  bool changed = false;
832 
833  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
834  if (gps->flag & GP_STROKE_SELECT) {
835  /* add instead of insert here, otherwise the uniqueness check gets skipped,
836  * and we get many duplicate entries...
837  */
838  BLI_gset_add(selected_colors, POINTER_FROM_INT(gps->mat_nr));
839  }
840  }
841  CTX_DATA_END;
842 
843  /* Second, select any visible stroke that uses these colors */
844  if (is_curve_edit) {
845  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
846  if (gps->editcurve != NULL &&
847  BLI_gset_haskey(selected_colors, POINTER_FROM_INT(gps->mat_nr))) {
848  bGPDcurve *gpc = gps->editcurve;
849  for (int i = 0; i < gpc->tot_curve_points; i++) {
850  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
851  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
852  BEZT_SEL_ALL(&gpc_pt->bezt);
853  }
854  gpc->flag |= GP_CURVE_SELECT;
855  gps->flag |= GP_STROKE_SELECT;
857 
858  changed = true;
859  }
860  }
861  CTX_DATA_END;
862  }
863  else {
864  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
865  if (BLI_gset_haskey(selected_colors, POINTER_FROM_INT(gps->mat_nr))) {
866  /* select this stroke */
867  bGPDspoint *pt;
868  int i;
869 
870  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
871  pt->flag |= GP_SPOINT_SELECT;
872  }
873 
874  gps->flag |= GP_STROKE_SELECT;
876 
877  changed = true;
878  }
879  }
880  CTX_DATA_END;
881  }
882 
883  /* Free memory. */
884  if (selected_colors != NULL) {
885  BLI_gset_free(selected_colors, NULL);
886  }
887 
888  return changed;
889 }
890 
891 /* ----------------------------------- */
892 
894 {
895  eGP_SelectGrouped mode = RNA_enum_get(op->ptr, "type");
897  /* If not edit/sculpt mode, the event has been caught but not processed. */
898  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
899  return OPERATOR_CANCELLED;
900  }
901 
902  bool changed = false;
903 
904  switch (mode) {
905  case GP_SEL_SAME_LAYER:
906  changed = gpencil_select_same_layer(C);
907  break;
909  changed = gpencil_select_same_material(C);
910  break;
911 
912  default:
913  BLI_assert_msg(0, "unhandled select grouped gpencil mode");
914  break;
915  }
916 
917  if (changed) {
918  /* updates */
920 
921  /* copy on write tag is needed, or else no refresh happens */
923 
926  }
927  return OPERATOR_FINISHED;
928 }
929 
931 {
933  {GP_SEL_SAME_LAYER, "LAYER", 0, "Layer", "Shared layers"},
934  {GP_SEL_SAME_MATERIAL, "MATERIAL", 0, "Material", "Shared materials"},
935  {0, NULL, 0, NULL, NULL},
936  };
937 
938  /* identifiers */
939  ot->name = "Select Grouped";
940  ot->idname = "GPENCIL_OT_select_grouped";
941  ot->description = "Select all strokes with similar characteristics";
942 
943  /* callbacks */
947 
948  /* flags */
950 
951  /* props */
952  ot->prop = RNA_def_enum(
953  ot->srna, "type", prop_select_grouped_types, GP_SEL_SAME_LAYER, "Type", "");
954 }
955 
958 /* -------------------------------------------------------------------- */
963 {
965  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
966 
967  /* If not edit/sculpt mode, the event has been caught but not processed. */
968  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
969  return OPERATOR_CANCELLED;
970  }
971 
972  const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes");
973  const bool extend = RNA_boolean_get(op->ptr, "extend");
974 
975  bool changed = false;
976  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
977  /* skip stroke if we're only manipulating selected strokes */
978  if (only_selected && !(gps->flag & GP_STROKE_SELECT)) {
979  continue;
980  }
981 
982  /* select first point */
983  BLI_assert(gps->totpoints >= 1);
984 
985  if (is_curve_edit) {
986  if (gps->editcurve != NULL) {
987  bGPDcurve *gpc = gps->editcurve;
989  BEZT_SEL_ALL(&gpc->curve_points[0].bezt);
990  gpc->flag |= GP_CURVE_SELECT;
991  gps->flag |= GP_STROKE_SELECT;
993 
994  if ((extend == false) && (gps->totpoints > 1)) {
995  for (int i = 1; i < gpc->tot_curve_points; i++) {
996  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
997  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
998  BEZT_DESEL_ALL(&gpc_pt->bezt);
999  }
1000  }
1001  changed = true;
1002  }
1003  }
1004  else {
1005  gps->points->flag |= GP_SPOINT_SELECT;
1006  gps->flag |= GP_STROKE_SELECT;
1008 
1009  /* deselect rest? */
1010  if ((extend == false) && (gps->totpoints > 1)) {
1011  /* start from index 1, to skip the first point that we'd just selected... */
1012  bGPDspoint *pt = &gps->points[1];
1013  int i = 1;
1014 
1015  for (; i < gps->totpoints; i++, pt++) {
1016  pt->flag &= ~GP_SPOINT_SELECT;
1017  }
1018  }
1019  changed = true;
1020  }
1021  }
1022  CTX_DATA_END;
1023 
1024  if (changed) {
1025  /* updates */
1027 
1028  /* copy on write tag is needed, or else no refresh happens */
1030 
1033  }
1034 
1035  return OPERATOR_FINISHED;
1036 }
1037 
1039 {
1040  /* identifiers */
1041  ot->name = "Select First";
1042  ot->idname = "GPENCIL_OT_select_first";
1043  ot->description = "Select first point in Grease Pencil strokes";
1044 
1045  /* callbacks */
1048 
1049  /* flags */
1051 
1052  /* properties */
1054  "only_selected_strokes",
1055  false,
1056  "Selected Strokes Only",
1057  "Only select the first point of strokes that already have points selected");
1058 
1060  "extend",
1061  false,
1062  "Extend",
1063  "Extend selection instead of deselecting all other selected points");
1064 }
1065 
1068 /* -------------------------------------------------------------------- */
1073 {
1075  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1076 
1077  /* If not edit/sculpt mode, the event has been caught but not processed. */
1078  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
1079  return OPERATOR_CANCELLED;
1080  }
1081 
1082  const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes");
1083  const bool extend = RNA_boolean_get(op->ptr, "extend");
1084 
1085  bool changed = false;
1086  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
1087  /* skip stroke if we're only manipulating selected strokes */
1088  if (only_selected && !(gps->flag & GP_STROKE_SELECT)) {
1089  continue;
1090  }
1091 
1092  /* select last point */
1093  BLI_assert(gps->totpoints >= 1);
1094 
1095  if (is_curve_edit) {
1096  if (gps->editcurve != NULL) {
1097  bGPDcurve *gpc = gps->editcurve;
1100  gpc->flag |= GP_CURVE_SELECT;
1101  gps->flag |= GP_STROKE_SELECT;
1103  if ((extend == false) && (gps->totpoints > 1)) {
1104  for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
1105  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1106  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1107  BEZT_DESEL_ALL(&gpc_pt->bezt);
1108  }
1109  }
1110  changed = true;
1111  }
1112  }
1113  else {
1114  gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT;
1115  gps->flag |= GP_STROKE_SELECT;
1117 
1118  /* deselect rest? */
1119  if ((extend == false) && (gps->totpoints > 1)) {
1120  /* don't include the last point... */
1121  bGPDspoint *pt = gps->points;
1122  int i = 0;
1123 
1124  for (; i < gps->totpoints - 1; i++, pt++) {
1125  pt->flag &= ~GP_SPOINT_SELECT;
1126  }
1127  }
1128 
1129  changed = true;
1130  }
1131  }
1132  CTX_DATA_END;
1133 
1134  if (changed) {
1135  /* updates */
1137 
1138  /* copy on write tag is needed, or else no refresh happens */
1140 
1143  }
1144 
1145  return OPERATOR_FINISHED;
1146 }
1147 
1149 {
1150  /* identifiers */
1151  ot->name = "Select Last";
1152  ot->idname = "GPENCIL_OT_select_last";
1153  ot->description = "Select last point in Grease Pencil strokes";
1154 
1155  /* callbacks */
1158 
1159  /* flags */
1161 
1162  /* properties */
1164  "only_selected_strokes",
1165  false,
1166  "Selected Strokes Only",
1167  "Only select the last point of strokes that already have points selected");
1168 
1170  "extend",
1171  false,
1172  "Extend",
1173  "Extend selection instead of deselecting all other selected points");
1174 }
1175 
1178 /* -------------------------------------------------------------------- */
1183 {
1185  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1186  /* If not edit/sculpt mode, the event has been caught but not processed. */
1187  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
1188  return OPERATOR_CANCELLED;
1189  }
1190 
1191  bool changed = false;
1192  if (is_curve_edit) {
1193  GP_EDITABLE_STROKES_BEGIN (gp_iter, C, gpl, gps) {
1194  if (gps->editcurve != NULL && gps->flag & GP_STROKE_SELECT) {
1195  bGPDcurve *editcurve = gps->editcurve;
1196 
1197  bool prev_sel = false;
1198  for (int i = 0; i < editcurve->tot_curve_points; i++) {
1199  bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
1200  BezTriple *bezt = &gpc_pt->bezt;
1201  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1202  /* selected point - just set flag for next point */
1203  prev_sel = true;
1204  }
1205  else {
1206  /* unselected point - expand selection if previous was selected... */
1207  if (prev_sel) {
1208  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1209  BEZT_SEL_ALL(bezt);
1210  changed = true;
1211  }
1212  prev_sel = false;
1213  }
1214  }
1215 
1216  prev_sel = false;
1217  for (int i = editcurve->tot_curve_points - 1; i >= 0; i--) {
1218  bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
1219  BezTriple *bezt = &gpc_pt->bezt;
1220  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1221  prev_sel = true;
1222  }
1223  else {
1224  /* unselected point - expand selection if previous was selected... */
1225  if (prev_sel) {
1226  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1227  BEZT_SEL_ALL(bezt);
1228  changed = true;
1229  }
1230  prev_sel = false;
1231  }
1232  }
1233  }
1234  }
1235  GP_EDITABLE_STROKES_END(gp_iter);
1236  }
1237  else {
1238  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
1239  if (gps->flag & GP_STROKE_SELECT) {
1240  bGPDspoint *pt;
1241  int i;
1242  bool prev_sel;
1243 
1244  /* First Pass: Go in forward order,
1245  * expanding selection if previous was selected (pre changes).
1246  * - This pass covers the "after" edges of selection islands
1247  */
1248  prev_sel = false;
1249  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1250  if (pt->flag & GP_SPOINT_SELECT) {
1251  /* selected point - just set flag for next point */
1252  prev_sel = true;
1253  }
1254  else {
1255  /* unselected point - expand selection if previous was selected... */
1256  if (prev_sel) {
1257  pt->flag |= GP_SPOINT_SELECT;
1258  changed = true;
1259  }
1260  prev_sel = false;
1261  }
1262  }
1263 
1264  /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
1265  * - This pass covers the "before" edges of selection islands
1266  */
1267  prev_sel = false;
1268  for (pt -= 1; i > 0; i--, pt--) {
1269  if (pt->flag & GP_SPOINT_SELECT) {
1270  prev_sel = true;
1271  }
1272  else {
1273  /* unselected point - expand selection if previous was selected... */
1274  if (prev_sel) {
1275  pt->flag |= GP_SPOINT_SELECT;
1276  changed = true;
1277  }
1278  prev_sel = false;
1279  }
1280  }
1281  }
1282  }
1283  CTX_DATA_END;
1284  }
1285 
1286  if (changed) {
1287  /* updates */
1289 
1290  /* copy on write tag is needed, or else no refresh happens */
1292 
1295  }
1296 
1297  return OPERATOR_FINISHED;
1298 }
1299 
1301 {
1302  /* identifiers */
1303  ot->name = "Select More";
1304  ot->idname = "GPENCIL_OT_select_more";
1305  ot->description = "Grow sets of selected Grease Pencil points";
1306 
1307  /* callbacks */
1310 
1311  /* flags */
1313 }
1314 
1317 /* -------------------------------------------------------------------- */
1322 {
1324  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1325 
1326  /* If not edit/sculpt mode, the event has been caught but not processed. */
1327  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
1328  return OPERATOR_CANCELLED;
1329  }
1330 
1331  bool changed = false;
1332  if (is_curve_edit) {
1333  GP_EDITABLE_STROKES_BEGIN (gp_iter, C, gpl, gps) {
1334  if (gps->editcurve != NULL && gps->flag & GP_STROKE_SELECT) {
1335  bGPDcurve *editcurve = gps->editcurve;
1336  int i;
1337 
1338  bool prev_sel = false;
1339  for (i = 0; i < editcurve->tot_curve_points; i++) {
1340  bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
1341  BezTriple *bezt = &gpc_pt->bezt;
1342  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1343  /* shrink if previous wasn't selected */
1344  if (prev_sel == false) {
1345  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1346  BEZT_DESEL_ALL(bezt);
1347  changed = true;
1348  }
1349  prev_sel = true;
1350  }
1351  else {
1352  /* mark previous as being unselected - and hence, is trigger for shrinking */
1353  prev_sel = false;
1354  }
1355  }
1356 
1357  /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
1358  * - This pass covers the "before" edges of selection islands
1359  */
1360  prev_sel = false;
1361  for (i = editcurve->tot_curve_points - 1; i > 0; i--) {
1362  bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
1363  BezTriple *bezt = &gpc_pt->bezt;
1364  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1365  /* shrink if previous wasn't selected */
1366  if (prev_sel == false) {
1367  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1368  BEZT_DESEL_ALL(bezt);
1369  changed = true;
1370  }
1371  prev_sel = true;
1372  }
1373  else {
1374  /* mark previous as being unselected - and hence, is trigger for shrinking */
1375  prev_sel = false;
1376  }
1377  }
1378  }
1379  }
1380  GP_EDITABLE_STROKES_END(gp_iter);
1381  }
1382  else {
1383  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
1384  if (gps->flag & GP_STROKE_SELECT) {
1385  bGPDspoint *pt;
1386  int i;
1387  bool prev_sel;
1388 
1389  /* First Pass: Go in forward order, shrinking selection
1390  * if previous was not selected (pre changes).
1391  * - This pass covers the "after" edges of selection islands
1392  */
1393  prev_sel = false;
1394  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1395  if (pt->flag & GP_SPOINT_SELECT) {
1396  /* shrink if previous wasn't selected */
1397  if (prev_sel == false) {
1398  pt->flag &= ~GP_SPOINT_SELECT;
1399  changed = true;
1400  }
1401  prev_sel = true;
1402  }
1403  else {
1404  /* mark previous as being unselected - and hence, is trigger for shrinking */
1405  prev_sel = false;
1406  }
1407  }
1408 
1409  /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
1410  * - This pass covers the "before" edges of selection islands
1411  */
1412  prev_sel = false;
1413  for (pt -= 1; i > 0; i--, pt--) {
1414  if (pt->flag & GP_SPOINT_SELECT) {
1415  /* shrink if previous wasn't selected */
1416  if (prev_sel == false) {
1417  pt->flag &= ~GP_SPOINT_SELECT;
1418  changed = true;
1419  }
1420  prev_sel = true;
1421  }
1422  else {
1423  /* mark previous as being unselected - and hence, is trigger for shrinking */
1424  prev_sel = false;
1425  }
1426  }
1427  }
1428  }
1429  CTX_DATA_END;
1430  }
1431 
1432  if (changed) {
1433  /* updates */
1435 
1436  /* copy on write tag is needed, or else no refresh happens */
1438 
1441  }
1442 
1443  return OPERATOR_FINISHED;
1444 }
1445 
1447 {
1448  /* identifiers */
1449  ot->name = "Select Less";
1450  ot->idname = "GPENCIL_OT_select_less";
1451  ot->description = "Shrink sets of selected Grease Pencil points";
1452 
1453  /* callbacks */
1456 
1457  /* flags */
1459 }
1460 
1463 /* -------------------------------------------------------------------- */
1475  bGPDlayer *gpl,
1476  bGPDstroke *gps,
1477  GP_SpaceConversion *gsc,
1478  const int mx,
1479  const int my,
1480  const int radius,
1481  const bool select,
1482  rcti *rect,
1483  const float diff_mat[4][4],
1484  const int selectmode,
1485  const float scale,
1486  const bool is_curve_edit)
1487 {
1488  bGPDspoint *pt = NULL;
1489  int x0 = 0, y0 = 0;
1490  int i;
1491  bool changed = false;
1492  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
1493  bGPDspoint *pt_active = NULL;
1494  bool hit = false;
1495 
1496  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1497  pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1498 
1499  bGPDspoint pt_temp;
1500  gpencil_point_to_parent_space(pt, diff_mat, &pt_temp);
1501  gpencil_point_to_xy(gsc, gps, &pt_temp, &x0, &y0);
1502 
1503  /* do boundbox check first */
1504  if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
1505  /* only check if point is inside */
1506  if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) {
1507  hit = true;
1508 
1509  /* change selection */
1510  if (select) {
1511  pt_active->flag |= GP_SPOINT_SELECT;
1512  gps_active->flag |= GP_STROKE_SELECT;
1513  BKE_gpencil_stroke_select_index_set(gpd, gps_active);
1514  }
1515  else {
1516  pt_active->flag &= ~GP_SPOINT_SELECT;
1517  gps_active->flag &= ~GP_STROKE_SELECT;
1519  }
1520  changed = true;
1521  /* if stroke mode, don't check more points */
1522  if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
1523  break;
1524  }
1525 
1526  /* Expand selection to segment. */
1527  if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select) && (pt_active != NULL)) {
1528  float r_hita[3], r_hitb[3];
1529  bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
1531  gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
1532  }
1533  }
1534  }
1535  }
1536 
1537  /* If stroke mode expand selection. */
1538  if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
1539  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1540  pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1541  if (pt_active != NULL) {
1542  if (select) {
1543  pt_active->flag |= GP_SPOINT_SELECT;
1544  }
1545  else {
1546  pt_active->flag &= ~GP_SPOINT_SELECT;
1547  }
1548  }
1549  }
1550  }
1551 
1552  /* If curve edit mode, generate the curve. */
1553  if (is_curve_edit && hit && gps_active->editcurve == NULL) {
1554  BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
1555  gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
1556  /* Select all curve points. */
1557  select_all_curve_points(gpd, gps_active, gps_active->editcurve, false);
1558  BKE_gpencil_stroke_geometry_update(gpd, gps_active);
1559  changed = true;
1560  }
1561 
1562  /* Ensure that stroke selection is in sync with its points. */
1563  BKE_gpencil_stroke_sync_selection(gpd, gps_active);
1564 
1565  return changed;
1566 }
1567 
1569  bGPDstroke *gps,
1570  bGPDcurve *gpc,
1571  const int mx,
1572  const int my,
1573  const int radius,
1574  const bool select,
1575  rcti *rect,
1576  const float diff_mat[4][4],
1577  const int selectmode)
1578 {
1579  ARegion *region = CTX_wm_region(C);
1580  View3D *v3d = CTX_wm_view3d(C);
1582  bGPdata *gpd = ob->data;
1583 
1584  const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
1585 
1586  bool hit = false;
1587  for (int i = 0; i < gpc->tot_curve_points; i++) {
1588  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1589  BezTriple *bezt = &gpc_pt->bezt;
1590 
1591  if (bezt->hide == 1) {
1592  continue;
1593  }
1594 
1595  const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
1596  (!only_selected || BEZT_ISSEL_ANY(bezt));
1597 
1598  /* If the handles are not visible only check control point (vec[1]). */
1599  int from = (!handles_visible) ? 1 : 0;
1600  int to = (!handles_visible) ? 2 : 3;
1601 
1602  for (int j = from; j < to; j++) {
1603  float parent_co[3];
1604  mul_v3_m4v3(parent_co, diff_mat, bezt->vec[j]);
1605  int screen_co[2];
1606  /* do 2d projection */
1608  region, parent_co, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) !=
1609  V3D_PROJ_RET_OK) {
1610  continue;
1611  }
1612 
1613  /* view and bounding box test */
1614  if (ELEM(V2D_IS_CLIPPED, screen_co[0], screen_co[1]) &&
1615  !BLI_rcti_isect_pt(rect, screen_co[0], screen_co[1])) {
1616  continue;
1617  }
1618 
1619  /* test inside circle */
1620  int dist_x = screen_co[0] - mx;
1621  int dist_y = screen_co[1] - my;
1622  int dist = dist_x * dist_x + dist_y * dist_y;
1623  if (dist <= radius * radius) {
1624  hit = true;
1625  /* change selection */
1626  if (select) {
1627  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1628  BEZT_SEL_IDX(bezt, j);
1629  }
1630  else {
1631  BEZT_DESEL_IDX(bezt, j);
1632  if (!BEZT_ISSEL_ANY(bezt)) {
1633  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1634  }
1635  }
1636  }
1637  }
1638  }
1639 
1640  /* select the entire curve */
1641  if (hit && (selectmode == GP_SELECTMODE_STROKE)) {
1642  for (int i = 0; i < gpc->tot_curve_points; i++) {
1643  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1644  BezTriple *bezt = &gpc_pt->bezt;
1645 
1646  if (select) {
1647  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1648  BEZT_SEL_ALL(bezt);
1649  }
1650  else {
1651  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1652  BEZT_DESEL_ALL(bezt);
1653  }
1654  }
1655  }
1656 
1658 
1659  return hit;
1660 }
1661 
1663 {
1667  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1668 
1669  int selectmode;
1670  if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
1672  }
1673  else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
1675  }
1676  else {
1677  selectmode = ts->gpencil_selectmode_edit;
1678  }
1679 
1680  const float scale = ts->gp_sculpt.isect_threshold;
1681 
1682  /* If not edit/sculpt mode, the event has been caught but not processed. */
1683  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
1684  return OPERATOR_CANCELLED;
1685  }
1686 
1687  ScrArea *area = CTX_wm_area(C);
1688 
1689  const int mx = RNA_int_get(op->ptr, "x");
1690  const int my = RNA_int_get(op->ptr, "y");
1691  const int radius = RNA_int_get(op->ptr, "radius");
1692 
1693  /* sanity checks */
1694  if (area == NULL) {
1695  BKE_report(op->reports, RPT_ERROR, "No active area");
1696  return OPERATOR_CANCELLED;
1697  }
1698 
1699  const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
1701  const bool select = (sel_op != SEL_OP_SUB);
1702 
1703  bool changed = false;
1704  /* For bounding `rect` around circle (for quickly intersection testing). */
1705  rcti rect = {0};
1706  rect.xmin = mx - radius;
1707  rect.ymin = my - radius;
1708  rect.xmax = mx + radius;
1709  rect.ymax = my + radius;
1710 
1711  if (is_curve_edit) {
1712  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1714  changed = true;
1715  }
1716 
1717  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
1718  {
1719  changed |= gpencil_do_curve_circle_sel(
1720  C, gps, gpc, mx, my, radius, select, &rect, gps_iter.diff_mat, selectmode);
1721  }
1722  GP_EDITABLE_CURVES_END(gps_iter);
1723  }
1724 
1725  if (changed == false) {
1726  GP_SpaceConversion gsc = {NULL};
1727  /* init space conversion stuff */
1729 
1730  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1732  changed = true;
1733  }
1734 
1735  /* find visible strokes, and select if hit */
1736  GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
1737  changed |= gpencil_stroke_do_circle_sel(gpd,
1738  gpl,
1739  gps,
1740  &gsc,
1741  mx,
1742  my,
1743  radius,
1744  select,
1745  &rect,
1746  gpstroke_iter.diff_mat,
1747  selectmode,
1748  scale,
1749  is_curve_edit);
1750  }
1751  GP_EVALUATED_STROKES_END(gpstroke_iter);
1752  }
1753 
1754  /* updates */
1755  if (changed) {
1757 
1758  /* copy on write tag is needed, or else no refresh happens */
1760 
1763  }
1764 
1765  return OPERATOR_FINISHED;
1766 }
1767 
1769 {
1770  /* identifiers */
1771  ot->name = "Circle Select";
1772  ot->description = "Select Grease Pencil strokes using brush selection";
1773  ot->idname = "GPENCIL_OT_select_circle";
1774 
1775  /* callbacks */
1781 
1782  /* flags */
1784 
1785  /* properties */
1788 }
1789 
1792 /* -------------------------------------------------------------------- */
1799 typedef struct GP_SelectUserData {
1800  int mx, my, radius;
1801  /* Bounding box rect */
1803  const int (*lasso_coords)[2];
1806 
1807 typedef bool (*GPencilTestFn)(ARegion *region,
1808  const float diff_mat[4][4],
1809  const float pt[3],
1811 
1812 #if 0
1813 static bool gpencil_stroke_fill_isect_rect(ARegion *region,
1814  bGPDstroke *gps,
1815  const float diff_mat[4][4],
1816  rcti rect)
1817 {
1818  int min[2] = {-INT_MAX, -INT_MAX};
1819  int max[2] = {INT_MAX, INT_MAX};
1820 
1821  int(*points2d)[2] = MEM_callocN(sizeof(int[2]) * gps->totpoints, __func__);
1822 
1823  for (int i = 0; i < gps->totpoints; i++) {
1824  bGPDspoint *pt = &gps->points[i];
1825  int *pt2d = points2d[i];
1826 
1827  int screen_co[2];
1828  gpencil_3d_point_to_screen_space(region, diff_mat, &pt->x, screen_co);
1829  DO_MINMAX2(screen_co, min, max);
1830 
1831  copy_v2_v2_int(pt2d, screen_co);
1832  }
1833 
1834  bool hit = false;
1835  /* check bounding box */
1836  rcti bb = {min[0], max[0], min[1], max[1]};
1837  if (BLI_rcti_isect(&rect, &bb, NULL)) {
1838  for (int i = 0; i < gps->tot_triangles; i++) {
1839  bGPDtriangle *tri = &gps->triangles[i];
1840  int pt1[2], pt2[2], pt3[2];
1841  int tri_min[2] = {-INT_MAX, -INT_MAX};
1842  int tri_max[2] = {INT_MAX, INT_MAX};
1843 
1844  copy_v2_v2_int(pt1, points2d[tri->verts[0]]);
1845  copy_v2_v2_int(pt2, points2d[tri->verts[1]]);
1846  copy_v2_v2_int(pt3, points2d[tri->verts[2]]);
1847 
1848  DO_MINMAX2(pt1, tri_min, tri_max);
1849  DO_MINMAX2(pt2, tri_min, tri_max);
1850  DO_MINMAX2(pt3, tri_min, tri_max);
1851 
1852  rcti tri_bb = {tri_min[0], tri_max[0], tri_min[1], tri_max[1]};
1853  /* Case 1: triangle is entirely inside box selection */
1854  /* (XXX: Can this even happen with no point inside the box?) */
1855  if (BLI_rcti_inside_rcti(&tri_bb, &rect)) {
1856  hit = true;
1857  break;
1858  }
1859 
1860  /* Case 2: rectangle intersects sides of triangle */
1861  if (BLI_rcti_isect_segment(&rect, pt1, pt2) || BLI_rcti_isect_segment(&rect, pt2, pt3) ||
1862  BLI_rcti_isect_segment(&rect, pt3, pt1)) {
1863  hit = true;
1864  break;
1865  }
1866 
1867  /* TODO: Case 3: rectangle is inside the triangle */
1868  }
1869  }
1870 
1871  MEM_freeN(points2d);
1872  return hit;
1873 }
1874 #endif
1875 
1877  Object *ob,
1878  GPencilTestFn is_inside_fn,
1879  rcti UNUSED(box),
1881  const bool strokemode,
1882  const eSelectOp sel_op)
1883 {
1884  ARegion *region = CTX_wm_region(C);
1885  View3D *v3d = CTX_wm_view3d(C);
1886  bGPdata *gpd = ob->data;
1887  const bool handle_only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
1888  const bool handle_all = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
1889 
1890  bool hit = false;
1891  bool changed = false;
1892  bool whole = false;
1893 
1894  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
1895  {
1896  bool any_select = false;
1897  for (int i = 0; i < gpc->tot_curve_points; i++) {
1898  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1899  BezTriple *bezt = &gpc_pt->bezt;
1900 
1901  if (bezt->hide == 1) {
1902  continue;
1903  }
1904 
1905  const bool handles_visible = (handle_all || (handle_only_selected &&
1906  (gpc_pt->flag & GP_CURVE_POINT_SELECT)));
1907 
1908  if (handles_visible) {
1909  for (int j = 0; j < 3; j++) {
1910  const bool is_select = BEZT_ISSEL_IDX(bezt, j);
1911  bool is_inside = is_inside_fn(region, gps_iter.diff_mat, bezt->vec[j], user_data);
1912  if (strokemode) {
1913  if (is_inside) {
1914  hit = true;
1915  any_select = true;
1916  break;
1917  }
1918  }
1919  else {
1920  const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1921  if (sel_op_result != -1) {
1922  if (sel_op_result) {
1923  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1924  BEZT_SEL_IDX(bezt, j);
1925  any_select = true;
1926  }
1927  else {
1928  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1929  BEZT_DESEL_IDX(bezt, j);
1930  }
1931  changed = true;
1932  hit = true;
1933  }
1934  else {
1935  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1936  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1937  BEZT_DESEL_IDX(bezt, j);
1938  }
1939  }
1940  }
1941  }
1942  }
1943  /* If the handles are not visible only check ctrl point (vec[1]). */
1944  else {
1945  const bool is_select = bezt->f2;
1946  bool is_inside = is_inside_fn(region, gps_iter.diff_mat, bezt->vec[1], user_data);
1947  if (strokemode) {
1948  if (is_inside) {
1949  hit = true;
1950  any_select = true;
1951  }
1952  }
1953  else {
1954  const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1955  if (sel_op_result != -1) {
1956  if (sel_op_result) {
1957  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1958  bezt->f2 |= SELECT;
1959  any_select = true;
1960  }
1961  else {
1962  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1963  bezt->f2 &= ~SELECT;
1964  }
1965  changed = true;
1966  hit = true;
1967  }
1968  else {
1969  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1970  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1971  bezt->f2 &= ~SELECT;
1972  }
1973  }
1974  }
1975  }
1976  }
1977 
1978  /* TODO: Fix selection for filled in curves. */
1979 #if 0
1980  if (!hit) {
1981  /* check if we selected the inside of a filled curve */
1982  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
1983  if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
1984  continue;
1985  }
1986 
1987  whole = gpencil_stroke_fill_isect_rect(region, gps, gps_iter.diff_mat, box);
1988  }
1989 #endif
1990  /* select the entire curve */
1991  if (strokemode || whole) {
1992  const int sel_op_result = ED_select_op_action_deselected(sel_op, any_select, hit || whole);
1993  if (sel_op_result != -1) {
1994  for (int i = 0; i < gpc->tot_curve_points; i++) {
1995  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1996  BezTriple *bezt = &gpc_pt->bezt;
1997 
1998  if (sel_op_result) {
1999  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
2000  BEZT_SEL_ALL(bezt);
2001  }
2002  else {
2003  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
2004  BEZT_DESEL_ALL(bezt);
2005  }
2006  }
2007 
2008  if (sel_op_result) {
2009  gpc->flag |= GP_CURVE_SELECT;
2010  }
2011  else {
2012  gpc->flag &= ~GP_CURVE_SELECT;
2013  }
2014  changed = true;
2015  }
2016  }
2017 
2019  }
2020  GP_EDITABLE_CURVES_END(gps_iter);
2021 
2022  return changed;
2023 }
2024 
2026  Object *ob,
2027  bGPdata *gpd,
2028  GPencilTestFn is_inside_fn,
2029  rcti box,
2031  const bool strokemode,
2032  const bool segmentmode,
2033  const eSelectOp sel_op,
2034  const float scale,
2035  const bool is_curve_edit)
2036 {
2037  GP_SpaceConversion gsc = {NULL};
2038  bool changed = false;
2039  /* init space conversion stuff */
2041 
2042  /* deselect all strokes first? */
2043  if (SEL_OP_USE_PRE_DESELECT(sel_op) || (GPENCIL_PAINT_MODE(gpd))) {
2044  /* Set selection index to 0. */
2045  gpd->select_last_index = 0;
2046 
2047  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
2048  bGPDspoint *pt;
2049  int i;
2050 
2051  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2052  pt->flag &= ~GP_SPOINT_SELECT;
2053  }
2054 
2055  gps->flag &= ~GP_STROKE_SELECT;
2057  }
2058  CTX_DATA_END;
2059 
2060  changed = true;
2061  }
2062 
2063  /* select/deselect points */
2064  GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
2065  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
2066  bool whole = false;
2067 
2068  bGPDspoint *pt;
2069  int i;
2070  bool hit = false;
2071  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2072  bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
2073 
2074  /* Convert point coords to screen-space. */
2075  const bool is_inside = is_inside_fn(
2076  gsc.region, gpstroke_iter.diff_mat, &pt_active->x, user_data);
2077  if (strokemode == false) {
2078  const bool is_select = (pt_active->flag & GP_SPOINT_SELECT) != 0;
2079  const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
2080  if (sel_op_result != -1) {
2081  SET_FLAG_FROM_TEST(pt_active->flag, sel_op_result, GP_SPOINT_SELECT);
2082  changed = true;
2083  hit = true;
2084 
2085  /* Expand selection to segment. */
2086  if (segmentmode) {
2087  bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
2088  float r_hita[3], r_hitb[3];
2090  gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
2091  }
2092  }
2093  }
2094  else {
2095  if (is_inside) {
2096  hit = true;
2097  break;
2098  }
2099  }
2100  }
2101 
2102  /* If nothing hit, check if the mouse is inside a filled stroke using the center or
2103  * Box or lasso area. */
2104  if (!hit) {
2105  /* Only check filled strokes. */
2106  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
2107  if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
2108  continue;
2109  }
2110  int mval[2];
2111  mval[0] = (box.xmax + box.xmin) / 2;
2112  mval[1] = (box.ymax + box.ymin) / 2;
2113 
2114  whole = ED_gpencil_stroke_point_is_inside(gps, &gsc, mval, gpstroke_iter.diff_mat);
2115  }
2116 
2117  /* if stroke mode expand selection. */
2118  if ((strokemode) || (whole)) {
2119  const bool is_select = BKE_gpencil_stroke_select_check(gps_active) || whole;
2120  const bool is_inside = hit || whole;
2121  const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
2122  if (sel_op_result != -1) {
2123  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2124  bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
2125 
2126  if (sel_op_result) {
2127  pt_active->flag |= GP_SPOINT_SELECT;
2128  }
2129  else {
2130  pt_active->flag &= ~GP_SPOINT_SELECT;
2131  }
2132  }
2133  changed = true;
2134  }
2135  }
2136 
2137  /* If curve edit mode, generate the curve. */
2138  if (is_curve_edit && (hit || whole) && gps_active->editcurve == NULL) {
2139  BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
2140  gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
2141  /* Select all curve points. */
2142  select_all_curve_points(gpd, gps_active, gps_active->editcurve, false);
2143  BKE_gpencil_stroke_geometry_update(gpd, gps_active);
2144  changed = true;
2145  }
2146 
2147  /* Ensure that stroke selection is in sync with its points */
2148  BKE_gpencil_stroke_sync_selection(gpd, gps_active);
2149  }
2150  GP_EVALUATED_STROKES_END(gpstroke_iter);
2151 
2152  return changed;
2153 }
2154 
2156  wmOperator *op,
2157  GPencilTestFn is_inside_fn,
2158  rcti box,
2160 {
2164  ScrArea *area = CTX_wm_area(C);
2165  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2166 
2167  int selectmode;
2168  if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
2170  }
2171  else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
2173  }
2174  else {
2175  selectmode = ts->gpencil_selectmode_edit;
2176  }
2177 
2178  const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) &&
2179  ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
2180  const bool segmentmode = ((selectmode == GP_SELECTMODE_SEGMENT) &&
2181  ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
2182 
2183  const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
2184  const float scale = ts->gp_sculpt.isect_threshold;
2185 
2186  bool changed = false;
2187 
2188  /* sanity checks */
2189  if (area == NULL) {
2190  BKE_report(op->reports, RPT_ERROR, "No active area");
2191  return OPERATOR_CANCELLED;
2192  }
2193 
2194  if (is_curve_edit) {
2195  changed = gpencil_generic_curve_select(
2196  C, ob, is_inside_fn, box, user_data, strokemode, sel_op);
2197  }
2198 
2199  if (changed == false) {
2200  changed = gpencil_generic_stroke_select(C,
2201  ob,
2202  gpd,
2203  is_inside_fn,
2204  box,
2205  user_data,
2206  strokemode,
2207  segmentmode,
2208  sel_op,
2209  scale,
2210  is_curve_edit);
2211  }
2212 
2213  /* if paint mode,delete selected points */
2214  if (GPENCIL_PAINT_MODE(gpd)) {
2216  changed = true;
2218  }
2219 
2220  /* updates */
2221  if (changed) {
2223 
2224  /* copy on write tag is needed, or else no refresh happens */
2226 
2229  }
2230  return OPERATOR_FINISHED;
2231 }
2232 
2235 /* -------------------------------------------------------------------- */
2239 static bool gpencil_test_box(ARegion *region,
2240  const float diff_mat[4][4],
2241  const float pt[3],
2243 {
2244  int co[2] = {0};
2245  if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
2246  return BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]);
2247  }
2248  return false;
2249 }
2250 
2252 {
2253  GP_SelectUserData data = {0};
2255  rcti rect = data.rect;
2256  return gpencil_generic_select_exec(C, op, gpencil_test_box, rect, &data);
2257 }
2258 
2260 {
2261  /* identifiers */
2262  ot->name = "Box Select";
2263  ot->description = "Select Grease Pencil strokes within a rectangular region";
2264  ot->idname = "GPENCIL_OT_select_box";
2265 
2266  /* callbacks */
2271 
2273 
2274  /* flags */
2276 
2277  /* properties */
2280 }
2281 
2284 /* -------------------------------------------------------------------- */
2288 static bool gpencil_test_lasso(ARegion *region,
2289  const float diff_mat[4][4],
2290  const float pt[3],
2292 {
2293  int co[2] = {0};
2294  if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
2295  /* test if in lasso boundbox + within the lasso noose */
2296  return (BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]) &&
2298  user_data->lasso_coords, user_data->lasso_coords_len, co[0], co[1], INT_MAX));
2299  }
2300  return false;
2301 }
2302 
2304 {
2305  struct GP_SelectUserData data = {0};
2306  data.lasso_coords = WM_gesture_lasso_path_to_array(C, op, &data.lasso_coords_len);
2307 
2308  /* Sanity check. */
2309  if (data.lasso_coords == NULL) {
2310  return OPERATOR_PASS_THROUGH;
2311  }
2312 
2313  /* Compute boundbox of lasso (for faster testing later). */
2314  BLI_lasso_boundbox(&data.rect, data.lasso_coords, data.lasso_coords_len);
2315 
2316  rcti rect = data.rect;
2318 
2319  MEM_freeN((void *)data.lasso_coords);
2320 
2321  return ret;
2322 }
2323 
2325 {
2326  ot->name = "Lasso Select Strokes";
2327  ot->description = "Select Grease Pencil strokes using lasso selection";
2328  ot->idname = "GPENCIL_OT_select_lasso";
2329 
2335 
2336  /* flags */
2338 
2339  /* properties */
2342 }
2343 
2346 /* -------------------------------------------------------------------- */
2351  const int mval[2],
2352  const int radius_squared,
2353  bGPDlayer **r_gpl,
2354  bGPDstroke **r_gps,
2355  bGPDcurve **r_gpc,
2356  bGPDcurve_point **r_pt,
2357  char *handle)
2358 {
2359  ARegion *region = CTX_wm_region(C);
2360  View3D *v3d = CTX_wm_view3d(C);
2361  const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
2362 
2363  int hit_distance = radius_squared;
2364 
2365  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
2366  {
2367  for (int i = 0; i < gpc->tot_curve_points; i++) {
2368  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
2369  BezTriple *bezt = &gpc_pt->bezt;
2370 
2371  if (bezt->hide == 1) {
2372  continue;
2373  }
2374 
2375  const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
2376  (!only_selected || BEZT_ISSEL_ANY(bezt));
2377 
2378  /* If the handles are not visible only check control point (vec[1]). */
2379  int from = (!handles_visible) ? 1 : 0;
2380  int to = (!handles_visible) ? 2 : 3;
2381 
2382  for (int j = from; j < to; j++) {
2383  int screen_co[2];
2384  if (gpencil_3d_point_to_screen_space(region, gps_iter.diff_mat, bezt->vec[j], screen_co)) {
2385  const int pt_distance = len_manhattan_v2v2_int(mval, screen_co);
2386 
2387  if (pt_distance <= radius_squared && pt_distance < hit_distance) {
2388  *r_gpl = gpl;
2389  *r_gps = gps;
2390  *r_gpc = gpc;
2391  *r_pt = gpc_pt;
2392  *handle = j;
2393  hit_distance = pt_distance;
2394  }
2395  }
2396  }
2397  }
2398  }
2399  GP_EDITABLE_CURVES_END(gps_iter);
2400 }
2401 
2403 {
2404  ScrArea *area = CTX_wm_area(C);
2408  const float scale = ts->gp_sculpt.isect_threshold;
2409  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2410 
2411  /* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */
2412  const float radius = 0.4f * U.widget_unit;
2413  const int radius_squared = (int)(radius * radius);
2414 
2415  const bool use_shift_extend = RNA_boolean_get(op->ptr, "use_shift_extend");
2416  bool extend = RNA_boolean_get(op->ptr, "extend") || use_shift_extend;
2417  bool deselect = RNA_boolean_get(op->ptr, "deselect");
2418  bool toggle = RNA_boolean_get(op->ptr, "toggle");
2419  bool whole = RNA_boolean_get(op->ptr, "entire_strokes");
2420  const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all") && !use_shift_extend;
2421 
2422  int mval[2] = {0};
2423  /* get mouse location */
2424  RNA_int_get_array(op->ptr, "location", mval);
2425 
2426  GP_SpaceConversion gsc = {NULL};
2427 
2428  bGPDlayer *hit_layer = NULL;
2429  bGPDstroke *hit_stroke = NULL;
2430  bGPDspoint *hit_point = NULL;
2431  bGPDcurve *hit_curve = NULL;
2432  bGPDcurve_point *hit_curve_point = NULL;
2433  char hit_curve_handle = 0;
2434  int hit_distance = radius_squared;
2435 
2436  /* sanity checks */
2437  if (area == NULL) {
2438  BKE_report(op->reports, RPT_ERROR, "No active area");
2439  return OPERATOR_CANCELLED;
2440  }
2441 
2442  /* if select mode is stroke, use whole stroke */
2443  if ((ob) && (ob->mode == OB_MODE_SCULPT_GPENCIL)) {
2446  }
2447  else if ((ob) && (ob->mode == OB_MODE_VERTEX_GPENCIL)) {
2450  }
2451  else {
2453  }
2454 
2455  if (is_curve_edit) {
2457  mval,
2458  radius_squared,
2459  &hit_layer,
2460  &hit_stroke,
2461  &hit_curve,
2462  &hit_curve_point,
2463  &hit_curve_handle);
2464  }
2465 
2466  if (hit_curve == NULL) {
2467  /* init space conversion stuff */
2469 
2470  /* First Pass: Find stroke point which gets hit */
2471  GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
2472  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
2473  bGPDspoint *pt;
2474  int i;
2475 
2476  /* firstly, check for hit-point */
2477  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2478  int xy[2];
2479 
2480  bGPDspoint pt2;
2481  gpencil_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
2482  gpencil_point_to_xy(&gsc, gps_active, &pt2, &xy[0], &xy[1]);
2483 
2484  /* do boundbox check first */
2485  if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
2486  const int pt_distance = len_manhattan_v2v2_int(mval, xy);
2487 
2488  /* check if point is inside */
2489  if (pt_distance <= radius_squared) {
2490  /* only use this point if it is a better match than the current hit - T44685 */
2491  if (pt_distance < hit_distance) {
2492  hit_layer = gpl;
2493  hit_stroke = gps_active;
2494  hit_point = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
2495  hit_distance = pt_distance;
2496  }
2497  }
2498  }
2499  }
2500  }
2501  GP_EVALUATED_STROKES_END(gpstroke_iter);
2502  }
2503 
2504  /* Abort if nothing hit... */
2505  if (!hit_curve && !hit_curve_point && !hit_point && !hit_stroke) {
2506 
2507  if (deselect_all) {
2508  /* since left mouse select change, deselect all if click outside any hit */
2510 
2511  /* copy on write tag is needed, or else no refresh happens */
2516 
2517  return OPERATOR_FINISHED;
2518  }
2519 
2520  return OPERATOR_CANCELLED;
2521  }
2522 
2523  /* select all handles if the click was on the curve but not on a handle */
2524  if (is_curve_edit && hit_point != NULL) {
2525  whole = true;
2526  hit_curve = hit_stroke->editcurve;
2527  }
2528 
2529  /* adjust selection behavior - for toggle option */
2530  if (toggle) {
2531  if (hit_curve_point != NULL) {
2532  BezTriple *bezt = &hit_curve_point->bezt;
2533  if ((bezt->f1 & SELECT) && (hit_curve_handle == 0)) {
2534  deselect = true;
2535  }
2536  if ((bezt->f2 & SELECT) && (hit_curve_handle == 1)) {
2537  deselect = true;
2538  }
2539  if ((bezt->f3 & SELECT) && (hit_curve_handle == 2)) {
2540  deselect = true;
2541  }
2542  }
2543  else {
2544  deselect = (hit_point->flag & GP_SPOINT_SELECT) != 0;
2545  }
2546  }
2547 
2548  /* If not extending selection, deselect everything else */
2549  if (extend == false) {
2551  }
2552 
2553  /* Perform selection operations... */
2554  if (whole) {
2555  /* Generate editcurve if it does not exist */
2556  if (is_curve_edit && hit_curve == NULL) {
2557  BKE_gpencil_stroke_editcurve_update(gpd, hit_layer, hit_stroke);
2558  hit_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
2559  BKE_gpencil_stroke_geometry_update(gpd, hit_stroke);
2560  hit_curve = hit_stroke->editcurve;
2561  }
2562  /* select all curve points */
2563  if (hit_curve != NULL) {
2564  select_all_curve_points(gpd, hit_stroke, hit_curve, deselect);
2565  }
2566  else {
2567  bGPDspoint *pt;
2568  int i;
2569 
2570  /* entire stroke's points */
2571  for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
2572  if (deselect == false) {
2573  pt->flag |= GP_SPOINT_SELECT;
2574  }
2575  else {
2576  pt->flag &= ~GP_SPOINT_SELECT;
2577  }
2578  }
2579 
2580  /* stroke too... */
2581  if (deselect == false) {
2582  hit_stroke->flag |= GP_STROKE_SELECT;
2583  BKE_gpencil_stroke_select_index_set(gpd, hit_stroke);
2584  }
2585  else {
2586  hit_stroke->flag &= ~GP_STROKE_SELECT;
2588  }
2589  }
2590  }
2591  else {
2592  /* just the point (and the stroke) */
2593  if (deselect == false) {
2594  if (hit_curve_point != NULL) {
2595  hit_curve_point->flag |= GP_CURVE_POINT_SELECT;
2596  BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
2597  hit_curve->flag |= GP_CURVE_SELECT;
2598  hit_stroke->flag |= GP_STROKE_SELECT;
2599  BKE_gpencil_stroke_select_index_set(gpd, hit_stroke);
2600  }
2601  else {
2602  /* we're adding selection, so selection must be true */
2603  hit_point->flag |= GP_SPOINT_SELECT;
2604  hit_stroke->flag |= GP_STROKE_SELECT;
2605  BKE_gpencil_stroke_select_index_set(gpd, hit_stroke);
2606 
2607  /* expand selection to segment */
2608  int selectmode;
2609  if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
2611  }
2612  else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
2614  }
2615  else {
2616  selectmode = ts->gpencil_selectmode_edit;
2617  }
2618 
2619  if (selectmode == GP_SELECTMODE_SEGMENT) {
2620  float r_hita[3], r_hitb[3];
2621  bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT);
2623  gpd, hit_layer, hit_stroke, hit_point, hit_select, false, scale, r_hita, r_hitb);
2624  }
2625  }
2626  }
2627  else {
2628  if (hit_curve_point != NULL) {
2629  BEZT_DESEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
2630  if (!BEZT_ISSEL_ANY(&hit_curve_point->bezt)) {
2631  hit_curve_point->flag &= ~GP_CURVE_POINT_SELECT;
2632  }
2633  BKE_gpencil_curve_sync_selection(gpd, hit_stroke);
2634  }
2635  else {
2636  /* deselect point */
2637  hit_point->flag &= ~GP_SPOINT_SELECT;
2638 
2639  /* ensure that stroke is selected correctly */
2640  BKE_gpencil_stroke_sync_selection(gpd, hit_stroke);
2641  }
2642  }
2643  }
2644 
2645  /* updates */
2646  if (hit_curve_point != NULL || hit_point != NULL) {
2648 
2649  /* copy on write tag is needed, or else no refresh happens */
2651 
2654  }
2655 
2657 }
2658 
2659 static int gpencil_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2660 {
2661  RNA_int_set_array(op->ptr, "location", event->mval);
2662 
2663  if (!RNA_struct_property_is_set(op->ptr, "use_shift_extend")) {
2664  RNA_boolean_set(op->ptr, "use_shift_extend", event->modifier & KM_SHIFT);
2665  }
2666 
2667  const int retval = gpencil_select_exec(C, op);
2668 
2669  return WM_operator_flag_only_pass_through_on_press(retval, event);
2670 }
2671 
2673 {
2674  PropertyRNA *prop;
2675 
2676  /* identifiers */
2677  ot->name = "Select";
2678  ot->description = "Select Grease Pencil strokes and/or stroke points";
2679  ot->idname = "GPENCIL_OT_select";
2680 
2681  /* callbacks */
2686 
2687  /* flag */
2688  ot->flag = OPTYPE_UNDO;
2689 
2690  /* properties */
2692 
2693  prop = RNA_def_boolean(ot->srna,
2694  "entire_strokes",
2695  false,
2696  "Entire Strokes",
2697  "Select entire strokes instead of just the nearest stroke vertex");
2699 
2700  prop = RNA_def_int_vector(ot->srna,
2701  "location",
2702  2,
2703  NULL,
2704  INT_MIN,
2705  INT_MAX,
2706  "Location",
2707  "Mouse location",
2708  INT_MIN,
2709  INT_MAX);
2711 
2712  prop = RNA_def_boolean(ot->srna, "use_shift_extend", false, "Extend", "");
2714 }
2715 
2716 /* Select by Vertex Color. */
2717 /* Helper to create a hash of colors. */
2719  Object *ob,
2720  const int threshold,
2721  GHash *hue_table)
2722 {
2723  const float range = pow(10, 5 - threshold);
2724  float hsv[3];
2725 
2726  /* Extract all colors. */
2727  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
2728  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2729  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2730  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2731  continue;
2732  }
2733  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
2734  continue;
2735  }
2736  if ((gps->flag & GP_STROKE_SELECT) == 0) {
2737  continue;
2738  }
2739 
2740  /* Read all points to get all colors selected. */
2741  bGPDspoint *pt;
2742  int i;
2743  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2744  if (((pt->flag & GP_SPOINT_SELECT) == 0) || (pt->vert_color[3] == 0.0f)) {
2745  continue;
2746  }
2747  /* Round Hue value. */
2748  rgb_to_hsv_compat_v(pt->vert_color, hsv);
2749  uint key = truncf(hsv[0] * range);
2750  if (!BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
2751  BLI_ghash_insert(hue_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
2752  }
2753  }
2754  }
2755  }
2756  }
2757  CTX_DATA_END;
2758 }
2759 
2761 {
2764  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
2765  return false;
2766  }
2767  bGPdata *gpd = (bGPdata *)ob->data;
2768 
2769  if (GPENCIL_VERTEX_MODE(gpd)) {
2771  return false;
2772  }
2773 
2774  /* Any data to use. */
2775  if (gpd->layers.first) {
2776  return true;
2777  }
2778  }
2779 
2780  return false;
2781 }
2782 
2784 {
2788 
2789  const float threshold = RNA_int_get(op->ptr, "threshold");
2790  const int selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
2791  const float range = pow(10, 5 - threshold);
2792 
2793  bool changed = false;
2794 
2795  /* Create a hash table with all selected colors. */
2796  GHash *hue_table = BLI_ghash_int_new(__func__);
2797  gpencil_selected_hue_table(C, ob, threshold, hue_table);
2798  if (BLI_ghash_len(hue_table) == 0) {
2799  BKE_report(op->reports, RPT_ERROR, "Select before some Vertex to use as a filter color");
2800  BLI_ghash_free(hue_table, NULL, NULL);
2801 
2802  return OPERATOR_CANCELLED;
2803  }
2804 
2805  /* Select any visible stroke that uses any of these colors. */
2806  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
2807  bGPDspoint *pt;
2808  int i;
2809  bool gps_selected = false;
2810  /* Check all stroke points. */
2811  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2812  if (pt->vert_color[3] == 0.0f) {
2813  continue;
2814  }
2815 
2816  /* Only check Hue to get value and saturation full ranges. */
2817  float hsv[3];
2818  /* Round Hue value. */
2819  rgb_to_hsv_compat_v(pt->vert_color, hsv);
2820  uint key = truncf(hsv[0] * range);
2821 
2822  if (BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
2823  pt->flag |= GP_SPOINT_SELECT;
2824  gps_selected = true;
2825  }
2826  }
2827 
2828  if (gps_selected) {
2829  gps->flag |= GP_STROKE_SELECT;
2831 
2832  /* Extend stroke selection. */
2833  if (selectmode == GP_SELECTMODE_STROKE) {
2834  bGPDspoint *pt1 = NULL;
2835 
2836  for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) {
2837  pt1->flag |= GP_SPOINT_SELECT;
2838  }
2839  }
2840  }
2841  }
2842  CTX_DATA_END;
2843 
2844  if (changed) {
2845  /* updates */
2847 
2848  /* copy on write tag is needed, or else no refresh happens */
2850 
2853  }
2854 
2855  /* Free memory. */
2856  if (hue_table != NULL) {
2857  BLI_ghash_free(hue_table, NULL, NULL);
2858  }
2859 
2860  return OPERATOR_FINISHED;
2861 }
2862 
2864 {
2865  PropertyRNA *prop;
2866 
2867  /* identifiers */
2868  ot->name = "Select Vertex Color";
2869  ot->idname = "GPENCIL_OT_select_vertex_color";
2870  ot->description = "Select all points with similar vertex color of current selected";
2871 
2872  /* callbacks */
2875 
2876  /* flags */
2878 
2879  /* properties */
2880  prop = RNA_def_int(
2881  ot->srna,
2882  "threshold",
2883  0,
2884  0,
2885  5,
2886  "Threshold",
2887  "Tolerance of the selection. Higher values select a wider range of similar colors",
2888  0,
2889  5);
2890  /* avoid re-using last var */
2892 }
2893 
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:738
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1090
#define CTX_DATA_BEGIN(C, Type, instance, member)
Definition: BKE_context.h:269
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
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:749
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1282
#define CTX_DATA_END
Definition: BKE_context.h:278
void BKE_gpencil_curve_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1120
void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1155
void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps)
Definition: gpencil.c:1161
bool BKE_gpencil_stroke_select_check(const struct bGPDstroke *gps)
struct bGPDframe * BKE_gpencil_layer_frame_get(struct bGPDlayer *gpl, int cframe, eGP_GetFrame_Mode addnew)
Definition: gpencil.c:1232
@ GP_GETFRAME_USE_PREV
Definition: BKE_gpencil.h:338
void BKE_gpencil_stroke_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1092
void BKE_gpencil_stroke_editcurve_update(struct bGPdata *gpd, struct bGPDlayer *gpl, struct bGPDstroke *gps)
void BKE_gpencil_stroke_geometry_update(struct bGPdata *gpd, struct bGPDstroke *gps)
General operations, lookup, etc. for materials.
struct MaterialGPencilStyle * BKE_gpencil_material_settings(struct Object *ob, short act)
Definition: material.c:805
void BKE_report(ReportList *reports, eReportType type, const char *message)
Definition: report.c:83
#define BLI_assert(a)
Definition: BLI_assert.h:46
#define BLI_assert_msg(a, msg)
Definition: BLI_assert.h:53
struct GSet GSet
Definition: BLI_ghash.h:340
bool BLI_ghash_haskey(const GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:822
GSet * BLI_gset_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
bool BLI_gset_haskey(const GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:1007
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
unsigned int BLI_ghash_len(const GHash *gh) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:705
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
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition: BLI_ghash.c:1037
bool BLI_gset_add(GSet *gs, void *key)
Definition: BLI_ghash.c:969
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
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:336
void rgb_to_hsv_compat_v(const float rgb[3], float r_hsv[3])
Definition: math_color.c:318
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:739
MINLINE void copy_v2_v2_int(int r[2], const int a[2])
MINLINE int len_manhattan_v2v2_int(const int a[2], const int b[2]) ATTR_WARN_UNUSED_RESULT
Random number functions.
void BLI_rng_free(struct RNG *rng) ATTR_NONNULL(1)
Definition: rand.cc:58
unsigned int BLI_rng_get_uint(struct RNG *rng) ATTR_WARN_UNUSED_RESULT ATTR_NONNULL(1)
Definition: rand.cc:83
struct RNG * BLI_rng_new(unsigned int seed)
Definition: rand.cc:39
void BLI_array_randomize(void *data, unsigned int elem_size, unsigned int elem_num, unsigned int seed)
Definition: rand.cc:188
bool BLI_rcti_isect_segment(const struct rcti *rect, const int s1[2], const int s2[2])
bool BLI_rcti_isect_pt(const struct rcti *rect, int x, int y)
bool BLI_rcti_isect(const struct rcti *src1, const struct rcti *src2, struct rcti *dest)
bool BLI_rcti_inside_rcti(const rcti *rct_a, const rcti *rct_b)
Definition: rct.c:197
unsigned int uint
Definition: BLI_sys_types.h:67
#define DO_MINMAX2(vec, min, max)
#define POINTER_FROM_INT(i)
#define UNUSED(x)
#define SET_FLAG_FROM_TEST(value, test, flag)
#define ELEM(...)
void DEG_id_tag_update(struct ID *id, int flag)
@ 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_IDX(bezt, i)
#define BEZT_ISSEL_IDX(bezt, i)
#define BEZT_SEL_ALL(bezt)
#define BEZT_ISSEL_ANY(bezt)
#define BEZT_DESEL_ALL(bezt)
#define BEZT_SEL_IDX(bezt, i)
#define GPENCIL_ANY_VERTEX_MASK(flag)
#define GPENCIL_VERTEX_MODE(gpd)
@ GP_CURVE_SELECT
#define GPENCIL_SCULPT_MODE(gpd)
@ GP_STROKE_NEEDS_CURVE_UPDATE
@ GP_STROKE_SELECT
#define GPENCIL_PAINT_MODE(gpd)
#define GPENCIL_NONE_EDIT_MODE(gpd)
#define GPENCIL_ANY_SCULPT_MASK(flag)
@ GP_DATA_STROKE_PAINTMODE
#define GPENCIL_ANY_MODE(gpd)
#define GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd)
@ GP_CURVE_POINT_SELECT
@ GP_SPOINT_SELECT
@ GP_MATERIAL_FILL_SHOW
@ OB_MODE_VERTEX_GPENCIL
@ OB_MODE_SCULPT_GPENCIL
Object is a sort of wrapper for general info.
@ OB_GPENCIL
@ GP_SELECTMODE_POINT
@ GP_SELECTMODE_SEGMENT
@ GP_SELECTMODE_STROKE
eGP_Sculpt_SelectMaskFlag
@ GP_SCULPT_MASK_SELECTMODE_POINT
@ GP_SCULPT_MASK_SELECTMODE_STROKE
@ GP_SCULPT_MASK_SELECTMODE_SEGMENT
@ GP_VERTEX_MASK_SELECTMODE_SEGMENT
@ GP_VERTEX_MASK_SELECTMODE_STROKE
@ GP_VERTEX_MASK_SELECTMODE_POINT
@ CURVE_HANDLE_NONE
@ CURVE_HANDLE_ALL
@ CURVE_HANDLE_SELECTED
@ OPERATOR_CANCELLED
@ OPERATOR_FINISHED
@ OPERATOR_PASS_THROUGH
int ED_select_op_action_deselected(eSelectOp sel_op, bool is_select, bool is_inside)
Definition: select_utils.c:38
eSelectOp ED_select_op_modal(eSelectOp sel_op, bool is_first)
Definition: select_utils.c:59
#define SEL_OP_USE_PRE_DESELECT(sel_op)
@ SEL_SELECT
@ SEL_DESELECT
void const char * ED_select_pick_get_name(struct wmOperatorType *ot, PointerRNA *ptr)
eSelectOp
@ SEL_OP_SUB
@ V3D_PROJ_RET_CLIP_WIN
Definition: ED_view3d.h:227
@ V3D_PROJ_RET_CLIP_BB
Definition: ED_view3d.h:225
@ V3D_PROJ_RET_OK
Definition: ED_view3d.h:217
eV3DProjStatus ED_view3d_project_int_global(const struct ARegion *region, const float co[3], int r_co[2], eV3DProjTest flag)
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition: RNA_types.h:218
@ PROP_HIDDEN
Definition: RNA_types.h:216
#define C
Definition: RandGen.cpp:25
#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_SELECT
Definition: WM_types.h:455
#define NC_GPENCIL
Definition: WM_types.h:349
@ KM_SHIFT
Definition: WM_types.h:238
#define NA_SELECTED
Definition: WM_types.h:528
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: avxb.h:154
unsigned int U
Definition: btGjkEpa3.h:78
static unsigned long seed
Definition: btSoftBody.h:39
#define SELECT
StackEntry * from
Scene scene
void * user_data
static bool is_inside(int x, int y, int cols, int rows)
Definition: filesel.c:706
int gpencil_delete_selected_point_wrap(bContext *C)
#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)
#define GP_EVALUATED_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)
#define GP_EDITABLE_STROKES_END(gpstroke_iter)
#define GP_EVALUATED_STROKES_END(gpstroke_iter)
void gpencil_point_to_xy(const GP_SpaceConversion *gsc, const struct bGPDstroke *gps, const struct bGPDspoint *pt, int *r_x, int *r_y)
static int gpencil_select_more_exec(bContext *C, wmOperator *UNUSED(op))
void GPENCIL_OT_select_more(wmOperatorType *ot)
void GPENCIL_OT_select_alternate(wmOperatorType *ot)
static bool gpencil_select_all_poll(bContext *C)
eGP_SelectGrouped
@ GP_SEL_SAME_MATERIAL
@ GP_SEL_SAME_LAYER
void GPENCIL_OT_select_grouped(wmOperatorType *ot)
struct GP_SelectUserData GP_SelectUserData
void GPENCIL_OT_select_circle(wmOperatorType *ot)
static int gpencil_generic_select_exec(bContext *C, wmOperator *op, GPencilTestFn is_inside_fn, rcti box, GP_SelectUserData *user_data)
void GPENCIL_OT_select_all(wmOperatorType *ot)
static bool gpencil_generic_stroke_select(bContext *C, Object *ob, bGPdata *gpd, GPencilTestFn is_inside_fn, rcti box, GP_SelectUserData *user_data, const bool strokemode, const bool segmentmode, const eSelectOp sel_op, const float scale, const bool is_curve_edit)
static int gpencil_select_random_exec(bContext *C, wmOperator *op)
static int gpencil_select_last_exec(bContext *C, wmOperator *op)
static bool gpencil_do_curve_circle_sel(bContext *C, bGPDstroke *gps, bGPDcurve *gpc, const int mx, const int my, const int radius, const bool select, rcti *rect, const float diff_mat[4][4], const int selectmode)
static bool gpencil_select_same_layer(bContext *C)
static int gpencil_select_all_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_select(wmOperatorType *ot)
static bool gpencil_test_lasso(ARegion *region, const float diff_mat[4][4], const float pt[3], GP_SelectUserData *user_data)
static int gpencil_lasso_select_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_select_vertex_color(wmOperatorType *ot)
bool(* GPencilTestFn)(ARegion *region, const float diff_mat[4][4], const float pt[3], GP_SelectUserData *user_data)
static void select_all_stroke_points(bGPdata *gpd, bGPDstroke *gps, bool select)
static bool gpencil_select_same_material(bContext *C)
static void gpencil_select_curve_point(bContext *C, const int mval[2], const int radius_squared, bGPDlayer **r_gpl, bGPDstroke **r_gps, bGPDcurve **r_gpc, bGPDcurve_point **r_pt, char *handle)
static bool gpencil_stroke_do_circle_sel(bGPdata *gpd, bGPDlayer *gpl, bGPDstroke *gps, GP_SpaceConversion *gsc, const int mx, const int my, const int radius, const bool select, rcti *rect, const float diff_mat[4][4], const int selectmode, const float scale, const bool is_curve_edit)
static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
void GPENCIL_OT_select_box(wmOperatorType *ot)
static int gpencil_select_first_exec(bContext *C, wmOperator *op)
static int gpencil_select_mode_from_sculpt(eGP_Sculpt_SelectMaskFlag mode)
static int gpencil_select_vertex_color_exec(bContext *C, wmOperator *op)
static void gpencil_selected_hue_table(bContext *C, Object *ob, const int threshold, GHash *hue_table)
static int gpencil_select_grouped_exec(bContext *C, wmOperator *op)
static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
static bool gpencil_select_vertex_color_poll(bContext *C)
static int gpencil_select_less_exec(bContext *C, wmOperator *UNUSED(op))
static int gpencil_box_select_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_select_random(wmOperatorType *ot)
void GPENCIL_OT_select_less(wmOperatorType *ot)
static void deselect_all_selected(bContext *C)
static bool gpencil_select_poll(bContext *C)
static int gpencil_select_mode_from_vertex(eGP_Sculpt_SelectMaskFlag mode)
static int gpencil_select_alternate_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_select_first(wmOperatorType *ot)
static bool gpencil_3d_point_to_screen_space(ARegion *region, const float diff_mat[4][4], const float co[3], int r_co[2])
static bool gpencil_generic_curve_select(bContext *C, Object *ob, GPencilTestFn is_inside_fn, rcti UNUSED(box), GP_SelectUserData *user_data, const bool strokemode, const eSelectOp sel_op)
void GPENCIL_OT_select_last(wmOperatorType *ot)
static int gpencil_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
static bool gpencil_test_box(ARegion *region, const float diff_mat[4][4], const float pt[3], GP_SelectUserData *user_data)
static int gpencil_select_linked_exec(bContext *C, wmOperator *op)
static int gpencil_select_exec(bContext *C, wmOperator *op)
void GPENCIL_OT_select_lasso(wmOperatorType *ot)
void GPENCIL_OT_select_linked(wmOperatorType *ot)
void ED_gpencil_select_toggle_all(bContext *C, int action)
bool ED_gpencil_stroke_material_editable(Object *ob, const bGPDlayer *gpl, const bGPDstroke *gps)
void ED_gpencil_select_curve_toggle_all(bContext *C, int action)
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])
bool ED_gpencil_stroke_point_is_inside(const bGPDstroke *gps, const GP_SpaceConversion *gsc, const int mval[2], const float diff_mat[4][4])
bool ED_gpencil_stroke_can_use(const bContext *C, const bGPDstroke *gps)
bGPdata * ED_gpencil_data_get_active(const bContext *C)
ccl_gpu_kernel_postfix ccl_global float int int int int float threshold
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:27
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
ccl_device_inline float3 pow(float3 v, float e)
Definition: math_float3.h:533
static void area(int d1, int d2, int e1, int e2, float weights[2])
static const EnumPropertyItem prop_select_grouped_types[]
return ret
void RNA_int_set_array(PointerRNA *ptr, const char *name, const int *values)
Definition: rna_access.c:4945
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
Definition: rna_access.c:4933
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:4874
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4910
float RNA_float_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:4957
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:5301
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
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_int_vector(StructOrFunctionRNA *cont_, const char *identifier, int len, const int *default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3623
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
#define min(a, b)
Definition: sort.c:35
uint8_t f3
float vec[3][3]
uint8_t f1
uint8_t f2
const int(* lasso_coords)[2]
struct ARegion * region
void * first
Definition: DNA_listBase.h:31
void * data
Definition: rand.cc:33
struct RenderData r
char gpencil_selectmode_vertex
char gpencil_selectmode_edit
char gpencil_selectmode_sculpt
struct GP_Sculpt_Settings gp_sculpt
View3DOverlay overlay
bGPDcurve_point * curve_points
ListBase strokes
struct bGPDspoint * pt_orig
bGPDspoint_Runtime runtime
float vert_color[4]
struct bGPDstroke * gps_orig
bGPDspoint * points
bGPDtriangle * triangles
bGPDstroke_Runtime runtime
struct bGPDcurve * editcurve
struct bGPDstroke * next
unsigned int verts[3]
int select_last_index
ListBase layers
int ymin
Definition: DNA_vec_types.h:64
int ymax
Definition: DNA_vec_types.h:64
int xmin
Definition: DNA_vec_types.h:63
int xmax
Definition: DNA_vec_types.h:63
int mval[2]
Definition: WM_types.h:684
uint8_t modifier
Definition: WM_types.h:693
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
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
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:903
PropertyRNA * prop
Definition: WM_types.h:981
const char *(* get_name)(struct wmOperatorType *, struct PointerRNA *)
Definition: WM_types.h:960
struct ReportList * reports
struct PointerRNA * ptr
float max
int xy[2]
Definition: wm_draw.c:135
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3479
bool WM_gesture_is_modal_first(const wmGesture *gesture)
Definition: wm_gesture.c:108
void WM_gesture_box_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_invoke(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_circle_invoke(bContext *C, wmOperator *op, const wmEvent *event)
void WM_gesture_circle_cancel(bContext *C, wmOperator *op)
int WM_gesture_box_modal(bContext *C, wmOperator *op, const wmEvent *event)
int WM_gesture_circle_modal(bContext *C, wmOperator *op, const wmEvent *event)
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]
void WM_operator_properties_border_to_rcti(struct wmOperator *op, rcti *rect)
int WM_operator_properties_select_random_seed_increment_get(wmOperator *op)
void WM_operator_properties_gesture_box(wmOperatorType *ot)
void WM_operator_properties_select_operation_simple(wmOperatorType *ot)
void WM_operator_properties_select_operation(wmOperatorType *ot)
void WM_operator_properties_gesture_lasso(wmOperatorType *ot)
void WM_operator_properties_select_random(wmOperatorType *ot)
void WM_operator_properties_gesture_circle(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operator_properties_mouse_select(wmOperatorType *ot)
int WM_operator_flag_only_pass_through_on_press(int retval, const struct wmEvent *event)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))