Blender  V2.93
gpencil_select.c
Go to the documentation of this file.
1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2014, Blender Foundation
17  * This is a new part of Blender
18  */
19 
24 #include <math.h>
25 #include <stddef.h>
26 #include <stdio.h>
27 #include <stdlib.h>
28 #include <string.h>
29 
30 #include "MEM_guardedalloc.h"
31 
32 #include "BLI_blenlib.h"
33 #include "BLI_ghash.h"
34 #include "BLI_lasso_2d.h"
35 #include "BLI_math_vector.h"
36 #include "BLI_utildefines.h"
37 
38 #include "DNA_gpencil_types.h"
39 #include "DNA_material_types.h"
40 #include "DNA_object_types.h"
41 #include "DNA_scene_types.h"
42 #include "DNA_screen_types.h"
43 #include "DNA_space_types.h"
44 
45 #include "BKE_context.h"
46 #include "BKE_gpencil.h"
47 #include "BKE_gpencil_curve.h"
48 #include "BKE_gpencil_geom.h"
49 #include "BKE_material.h"
50 #include "BKE_report.h"
51 
52 #include "UI_interface.h"
53 #include "UI_resources.h"
54 
55 #include "WM_api.h"
56 #include "WM_types.h"
57 
58 #include "RNA_access.h"
59 #include "RNA_define.h"
60 
61 #include "UI_view2d.h"
62 
63 #include "ED_gpencil.h"
64 #include "ED_select_utils.h"
65 #include "ED_view3d.h"
66 
67 #include "DEG_depsgraph.h"
68 #include "DEG_depsgraph_query.h"
69 
70 #include "gpencil_intern.h"
71 
72 /* -------------------------------------------------------------------- */
76 /* Convert sculpt 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 
91 /* Convert vertex mask mode to Select mode */
93 {
95  return GP_SELECTMODE_POINT;
96  }
98  return GP_SELECTMODE_STROKE;
99  }
101  return GP_SELECTMODE_SEGMENT;
102  }
103  return GP_SELECTMODE_POINT;
104 }
105 
107 {
110 
111  if (GPENCIL_SCULPT_MODE(gpd)) {
113  return false;
114  }
115  }
116 
117  if (GPENCIL_VERTEX_MODE(gpd)) {
119  return false;
120  }
121  }
122 
123  /* We just need some visible strokes,
124  * and to be in edit-mode or other modes only to catch event. */
125  if (GPENCIL_ANY_MODE(gpd)) {
126  /* TODO: include a check for visible strokes? */
127  if (gpd->layers.first) {
128  return true;
129  }
130  }
131 
132  return false;
133 }
134 
136  const float diff_mat[4][4],
137  const float co[3],
138  int r_co[2])
139 {
140  float parent_co[3];
141  mul_v3_m4v3(parent_co, diff_mat, co);
142  int screen_co[2];
144  region, parent_co, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) ==
145  V3D_PROJ_RET_OK) {
146  if (!ELEM(V2D_IS_CLIPPED, screen_co[0], screen_co[1])) {
147  copy_v2_v2_int(r_co, screen_co);
148  return true;
149  }
150  }
151  r_co[0] = V2D_IS_CLIPPED;
152  r_co[1] = V2D_IS_CLIPPED;
153  return false;
154 }
155 
156 /* helper to deselect all selected strokes/points */
158 {
159  /* Set selection index to 0. */
161  bGPdata *gpd = ob->data;
162  gpd->select_last_index = 0;
163 
164  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
165  /* deselect stroke and its points if selected */
166  if (gps->flag & GP_STROKE_SELECT) {
167  bGPDspoint *pt;
168  int i;
169 
170  /* deselect points */
171  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
172  pt->flag &= ~GP_SPOINT_SELECT;
173  }
174 
175  /* deselect stroke itself too */
176  gps->flag &= ~GP_STROKE_SELECT;
178  }
179 
180  /* deselect curve and curve points */
181  if (gps->editcurve != NULL) {
182  bGPDcurve *gpc = gps->editcurve;
183  for (int j = 0; j < gpc->tot_curve_points; j++) {
184  bGPDcurve_point *gpc_pt = &gpc->curve_points[j];
185  BezTriple *bezt = &gpc_pt->bezt;
186  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
187  BEZT_DESEL_ALL(bezt);
188  }
189 
190  gpc->flag &= ~GP_CURVE_SELECT;
191  }
192  }
193  CTX_DATA_END;
194 }
195 
196 static void select_all_curve_points(bGPdata *gpd, bGPDstroke *gps, bGPDcurve *gpc, bool deselect)
197 {
198  for (int i = 0; i < gpc->tot_curve_points; i++) {
199  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
200  BezTriple *bezt = &gpc_pt->bezt;
201  if (deselect == false) {
202  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
203  BEZT_SEL_ALL(bezt);
204  }
205  else {
206  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
207  BEZT_DESEL_ALL(bezt);
208  }
209  }
210 
211  if (deselect == false) {
212  gpc->flag |= GP_CURVE_SELECT;
213  gps->flag |= GP_STROKE_SELECT;
215  }
216  else {
217  gpc->flag &= ~GP_CURVE_SELECT;
218  gps->flag &= ~GP_STROKE_SELECT;
220  }
221 }
222 
225 /* -------------------------------------------------------------------- */
229 {
231 
232  /* We just need some visible strokes,
233  * and to be in edit-mode or other modes only to catch event. */
234  if (GPENCIL_ANY_MODE(gpd)) {
235  if (gpd->layers.first) {
236  return true;
237  }
238  }
239 
240  return false;
241 }
242 
244 {
246  int action = RNA_enum_get(op->ptr, "action");
247  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
248 
249  if (gpd == NULL) {
250  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
251  return OPERATOR_CANCELLED;
252  }
253 
254  /* If not edit/sculpt mode, the event has been caught but not processed. */
255  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
256  return OPERATOR_CANCELLED;
257  }
258 
259  /* For sculpt mode, if mask is disable, only allows deselect */
260  if (GPENCIL_SCULPT_MODE(gpd)) {
262  if ((!(GPENCIL_ANY_SCULPT_MASK(ts->gpencil_selectmode_sculpt))) && (action != SEL_DESELECT)) {
263  return OPERATOR_CANCELLED;
264  }
265  }
266 
267  if (is_curve_edit) {
269  }
270  else {
272  }
273 
274  /* updates */
276 
277  /* copy on write tag is needed, or else no refresh happens */
279 
282  return OPERATOR_FINISHED;
283 }
284 
286 {
287  /* identifiers */
288  ot->name = "(De)select All Strokes";
289  ot->idname = "GPENCIL_OT_select_all";
290  ot->description = "Change selection of all Grease Pencil strokes currently visible";
291 
292  /* callbacks */
295 
296  /* flags */
298 
300 }
301 
304 /* -------------------------------------------------------------------- */
309 {
311  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
312 
313  if (gpd == NULL) {
314  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
315  return OPERATOR_CANCELLED;
316  }
317 
318  /* If not edit/sculpt mode, the event has been caught but not processed. */
319  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
320  return OPERATOR_CANCELLED;
321  }
322 
323  if (is_curve_edit) {
324  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
325  {
326  if (gpc->flag & GP_CURVE_SELECT) {
327  for (int i = 0; i < gpc->tot_curve_points; i++) {
328  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
329  BezTriple *bezt = &gpc_pt->bezt;
330  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
331  BEZT_SEL_ALL(bezt);
332  }
333  }
334  }
335  GP_EDITABLE_CURVES_END(gps_iter);
336  }
337  else {
338  /* select all points in selected strokes */
339  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
340  if (gps->flag & GP_STROKE_SELECT) {
341  bGPDspoint *pt;
342  int i;
343 
344  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
345  pt->flag |= GP_SPOINT_SELECT;
346  }
347  }
348  }
349  CTX_DATA_END;
350  }
351 
352  /* updates */
354 
355  /* copy on write tag is needed, or else no refresh happens */
357 
360  return OPERATOR_FINISHED;
361 }
362 
364 {
365  /* identifiers */
366  ot->name = "Select Linked";
367  ot->idname = "GPENCIL_OT_select_linked";
368  ot->description = "Select all points in same strokes as already selected points";
369 
370  /* callbacks */
373 
374  /* flags */
376 }
377 
380 /* -------------------------------------------------------------------- */
385 {
387  const bool unselect_ends = RNA_boolean_get(op->ptr, "unselect_ends");
388  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
389 
390  if (gpd == NULL) {
391  BKE_report(op->reports, RPT_ERROR, "No Grease Pencil data");
392  return OPERATOR_CANCELLED;
393  }
394 
395  /* If not edit/sculpt mode, the event has been caught but not processed. */
396  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
397  return OPERATOR_CANCELLED;
398  }
399 
400  bool changed = false;
401  if (is_curve_edit) {
402  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
403  {
404  if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
405  int idx = 0;
406  int start = 0;
407  if (unselect_ends) {
408  start = 1;
409  }
410 
411  for (int i = start; i < gpc->tot_curve_points; i++) {
412  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
413  if ((idx % 2) == 0) {
414  gpc_pt->flag |= GP_SPOINT_SELECT;
415  BEZT_SEL_ALL(&gpc_pt->bezt);
416  }
417  else {
418  gpc_pt->flag &= ~GP_SPOINT_SELECT;
419  BEZT_DESEL_ALL(&gpc_pt->bezt);
420  }
421  idx++;
422  }
423 
424  if (unselect_ends) {
425  bGPDcurve_point *gpc_pt = &gpc->curve_points[0];
426  gpc_pt->flag &= ~GP_SPOINT_SELECT;
427  BEZT_DESEL_ALL(&gpc_pt->bezt);
428 
429  gpc_pt = &gpc->curve_points[gpc->tot_curve_points - 1];
430  gpc_pt->flag &= ~GP_SPOINT_SELECT;
431  BEZT_DESEL_ALL(&gpc_pt->bezt);
432  }
433 
435  changed = true;
436  }
437  }
438  GP_EDITABLE_CURVES_END(gps_iter);
439  }
440  else {
441  /* select all points in selected strokes */
442  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
443  if ((gps->flag & GP_STROKE_SELECT) && (gps->totpoints > 1)) {
444  bGPDspoint *pt;
445  int row = 0;
446  int start = 0;
447  if (unselect_ends) {
448  start = 1;
449  }
450 
451  for (int i = start; i < gps->totpoints; i++) {
452  pt = &gps->points[i];
453  if ((row % 2) == 0) {
454  pt->flag |= GP_SPOINT_SELECT;
455  }
456  else {
457  pt->flag &= ~GP_SPOINT_SELECT;
458  }
459  row++;
460  }
461 
462  /* unselect start and end points */
463  if (unselect_ends) {
464  pt = &gps->points[0];
465  pt->flag &= ~GP_SPOINT_SELECT;
466 
467  pt = &gps->points[gps->totpoints - 1];
468  pt->flag &= ~GP_SPOINT_SELECT;
469  }
470 
471  changed = true;
472  }
473  }
474  CTX_DATA_END;
475  }
476 
477  if (changed) {
478  /* updates */
480 
481  /* copy on write tag is needed, or else no refresh happens */
483 
486  }
487 
488  return OPERATOR_FINISHED;
489 }
490 
492 {
493  /* identifiers */
494  ot->name = "Alternated";
495  ot->idname = "GPENCIL_OT_select_alternate";
496  ot->description = "Select alternative points in same strokes as already selected points";
497 
498  /* callbacks */
501 
502  /* flags */
504 
505  /* properties */
507  "unselect_ends",
508  false,
509  "Unselect Ends",
510  "Do not select the first and last point of the stroke");
511 }
512 
515 /* -------------------------------------------------------------------- */
519 typedef enum eGP_SelectGrouped {
520  /* Select strokes in the same layer */
522 
523  /* Select strokes with the same color */
525 
526  /* TODO: All with same prefix -
527  * Useful for isolating all layers for a particular character for instance. */
528  /* TODO: All with same appearance - color/opacity/volumetric/fills ? */
530 
531 /* ----------------------------------- */
532 
533 /* On each visible layer, check for selected strokes - if found, select all others */
535 {
538  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
539 
540  bool changed = false;
541  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
543  bGPDstroke *gps;
544  bool found = false;
545 
546  if (gpf == NULL) {
547  continue;
548  }
549 
550  /* Search for a selected stroke */
551  for (gps = gpf->strokes.first; gps; gps = gps->next) {
552  if (ED_gpencil_stroke_can_use(C, gps)) {
553  if (gps->flag & GP_STROKE_SELECT) {
554  found = true;
555  break;
556  }
557  }
558  }
559 
560  /* Select all if found */
561  if (found) {
562  if (is_curve_edit) {
563  for (gps = gpf->strokes.first; gps; gps = gps->next) {
564  if (gps->editcurve != NULL && ED_gpencil_stroke_can_use(C, gps)) {
565  bGPDcurve *gpc = gps->editcurve;
566  for (int i = 0; i < gpc->tot_curve_points; i++) {
567  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
568  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
569  BEZT_SEL_ALL(&gpc_pt->bezt);
570  }
571  gpc->flag |= GP_CURVE_SELECT;
572  gps->flag |= GP_STROKE_SELECT;
574 
575  changed = true;
576  }
577  }
578  }
579  else {
580  for (gps = gpf->strokes.first; gps; gps = gps->next) {
581  if (ED_gpencil_stroke_can_use(C, gps)) {
582  bGPDspoint *pt;
583  int i;
584 
585  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
586  pt->flag |= GP_SPOINT_SELECT;
587  }
588 
589  gps->flag |= GP_STROKE_SELECT;
591 
592  changed = true;
593  }
594  }
595  }
596  }
597  }
598  CTX_DATA_END;
599 
600  return changed;
601 }
602 
603 /* Select all strokes with same colors as selected ones */
605 {
607  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
608  /* First, build set containing all the colors of selected strokes */
609  GSet *selected_colors = BLI_gset_str_new("GP Selected Colors");
610 
611  bool changed = false;
612 
613  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
614  if (gps->flag & GP_STROKE_SELECT) {
615  /* add instead of insert here, otherwise the uniqueness check gets skipped,
616  * and we get many duplicate entries...
617  */
618  BLI_gset_add(selected_colors, &gps->mat_nr);
619  }
620  }
621  CTX_DATA_END;
622 
623  /* Second, select any visible stroke that uses these colors */
624  if (is_curve_edit) {
625  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
626  if (gps->editcurve != NULL && BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
627  bGPDcurve *gpc = gps->editcurve;
628  for (int i = 0; i < gpc->tot_curve_points; i++) {
629  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
630  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
631  BEZT_SEL_ALL(&gpc_pt->bezt);
632  }
633  gpc->flag |= GP_CURVE_SELECT;
634  gps->flag |= GP_STROKE_SELECT;
636 
637  changed = true;
638  }
639  }
640  CTX_DATA_END;
641  }
642  else {
643  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
644  if (BLI_gset_haskey(selected_colors, &gps->mat_nr)) {
645  /* select this stroke */
646  bGPDspoint *pt;
647  int i;
648 
649  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
650  pt->flag |= GP_SPOINT_SELECT;
651  }
652 
653  gps->flag |= GP_STROKE_SELECT;
655 
656  changed = true;
657  }
658  }
659  CTX_DATA_END;
660  }
661 
662  /* Free memory. */
663  if (selected_colors != NULL) {
664  BLI_gset_free(selected_colors, NULL);
665  }
666 
667  return changed;
668 }
669 
670 /* ----------------------------------- */
671 
673 {
674  eGP_SelectGrouped mode = RNA_enum_get(op->ptr, "type");
676  /* If not edit/sculpt mode, the event has been caught but not processed. */
677  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
678  return OPERATOR_CANCELLED;
679  }
680 
681  bool changed = false;
682 
683  switch (mode) {
684  case GP_SEL_SAME_LAYER:
685  changed = gpencil_select_same_layer(C);
686  break;
688  changed = gpencil_select_same_material(C);
689  break;
690 
691  default:
692  BLI_assert(!"unhandled select grouped gpencil mode");
693  break;
694  }
695 
696  if (changed) {
697  /* updates */
699 
700  /* copy on write tag is needed, or else no refresh happens */
702 
705  }
706  return OPERATOR_FINISHED;
707 }
708 
710 {
712  {GP_SEL_SAME_LAYER, "LAYER", 0, "Layer", "Shared layers"},
713  {GP_SEL_SAME_MATERIAL, "MATERIAL", 0, "Material", "Shared materials"},
714  {0, NULL, 0, NULL, NULL},
715  };
716 
717  /* identifiers */
718  ot->name = "Select Grouped";
719  ot->idname = "GPENCIL_OT_select_grouped";
720  ot->description = "Select all strokes with similar characteristics";
721 
722  /* callbacks */
726 
727  /* flags */
729 
730  /* props */
731  ot->prop = RNA_def_enum(
732  ot->srna, "type", prop_select_grouped_types, GP_SEL_SAME_LAYER, "Type", "");
733 }
734 
737 /* -------------------------------------------------------------------- */
742 {
744  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
745 
746  /* If not edit/sculpt mode, the event has been caught but not processed. */
747  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
748  return OPERATOR_CANCELLED;
749  }
750 
751  const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes");
752  const bool extend = RNA_boolean_get(op->ptr, "extend");
753 
754  bool changed = false;
755  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
756  /* skip stroke if we're only manipulating selected strokes */
757  if (only_selected && !(gps->flag & GP_STROKE_SELECT)) {
758  continue;
759  }
760 
761  /* select first point */
762  BLI_assert(gps->totpoints >= 1);
763 
764  if (is_curve_edit) {
765  if (gps->editcurve != NULL) {
766  bGPDcurve *gpc = gps->editcurve;
768  BEZT_SEL_ALL(&gpc->curve_points[0].bezt);
769  gpc->flag |= GP_CURVE_SELECT;
770  gps->flag |= GP_STROKE_SELECT;
772 
773  if ((extend == false) && (gps->totpoints > 1)) {
774  for (int i = 1; i < gpc->tot_curve_points; i++) {
775  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
776  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
777  BEZT_DESEL_ALL(&gpc_pt->bezt);
778  }
779  }
780  changed = true;
781  }
782  }
783  else {
784  gps->points->flag |= GP_SPOINT_SELECT;
785  gps->flag |= GP_STROKE_SELECT;
787 
788  /* deselect rest? */
789  if ((extend == false) && (gps->totpoints > 1)) {
790  /* start from index 1, to skip the first point that we'd just selected... */
791  bGPDspoint *pt = &gps->points[1];
792  int i = 1;
793 
794  for (; i < gps->totpoints; i++, pt++) {
795  pt->flag &= ~GP_SPOINT_SELECT;
796  }
797  }
798  changed = true;
799  }
800  }
801  CTX_DATA_END;
802 
803  if (changed) {
804  /* updates */
806 
807  /* copy on write tag is needed, or else no refresh happens */
809 
812  }
813 
814  return OPERATOR_FINISHED;
815 }
816 
818 {
819  /* identifiers */
820  ot->name = "Select First";
821  ot->idname = "GPENCIL_OT_select_first";
822  ot->description = "Select first point in Grease Pencil strokes";
823 
824  /* callbacks */
827 
828  /* flags */
830 
831  /* properties */
833  "only_selected_strokes",
834  false,
835  "Selected Strokes Only",
836  "Only select the first point of strokes that already have points selected");
837 
839  "extend",
840  false,
841  "Extend",
842  "Extend selection instead of deselecting all other selected points");
843 }
844 
847 /* -------------------------------------------------------------------- */
852 {
854  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
855 
856  /* If not edit/sculpt mode, the event has been caught but not processed. */
857  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
858  return OPERATOR_CANCELLED;
859  }
860 
861  const bool only_selected = RNA_boolean_get(op->ptr, "only_selected_strokes");
862  const bool extend = RNA_boolean_get(op->ptr, "extend");
863 
864  bool changed = false;
865  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
866  /* skip stroke if we're only manipulating selected strokes */
867  if (only_selected && !(gps->flag & GP_STROKE_SELECT)) {
868  continue;
869  }
870 
871  /* select last point */
872  BLI_assert(gps->totpoints >= 1);
873 
874  if (is_curve_edit) {
875  if (gps->editcurve != NULL) {
876  bGPDcurve *gpc = gps->editcurve;
879  gpc->flag |= GP_CURVE_SELECT;
880  gps->flag |= GP_STROKE_SELECT;
882  if ((extend == false) && (gps->totpoints > 1)) {
883  for (int i = 0; i < gpc->tot_curve_points - 1; i++) {
884  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
885  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
886  BEZT_DESEL_ALL(&gpc_pt->bezt);
887  }
888  }
889  changed = true;
890  }
891  }
892  else {
893  gps->points[gps->totpoints - 1].flag |= GP_SPOINT_SELECT;
894  gps->flag |= GP_STROKE_SELECT;
896 
897  /* deselect rest? */
898  if ((extend == false) && (gps->totpoints > 1)) {
899  /* don't include the last point... */
900  bGPDspoint *pt = gps->points;
901  int i = 0;
902 
903  for (; i < gps->totpoints - 1; i++, pt++) {
904  pt->flag &= ~GP_SPOINT_SELECT;
905  }
906  }
907 
908  changed = true;
909  }
910  }
911  CTX_DATA_END;
912 
913  if (changed) {
914  /* updates */
916 
917  /* copy on write tag is needed, or else no refresh happens */
919 
922  }
923 
924  return OPERATOR_FINISHED;
925 }
926 
928 {
929  /* identifiers */
930  ot->name = "Select Last";
931  ot->idname = "GPENCIL_OT_select_last";
932  ot->description = "Select last point in Grease Pencil strokes";
933 
934  /* callbacks */
937 
938  /* flags */
940 
941  /* properties */
943  "only_selected_strokes",
944  false,
945  "Selected Strokes Only",
946  "Only select the last point of strokes that already have points selected");
947 
949  "extend",
950  false,
951  "Extend",
952  "Extend selection instead of deselecting all other selected points");
953 }
954 
957 /* -------------------------------------------------------------------- */
962 {
964  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
965  /* If not edit/sculpt mode, the event has been caught but not processed. */
966  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
967  return OPERATOR_CANCELLED;
968  }
969 
970  bool changed = false;
971  if (is_curve_edit) {
972  GP_EDITABLE_STROKES_BEGIN (gp_iter, C, gpl, gps) {
973  if (gps->editcurve != NULL && gps->flag & GP_STROKE_SELECT) {
974  bGPDcurve *editcurve = gps->editcurve;
975 
976  bool prev_sel = false;
977  for (int i = 0; i < editcurve->tot_curve_points; i++) {
978  bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
979  BezTriple *bezt = &gpc_pt->bezt;
980  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
981  /* selected point - just set flag for next point */
982  prev_sel = true;
983  }
984  else {
985  /* unselected point - expand selection if previous was selected... */
986  if (prev_sel) {
987  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
988  BEZT_SEL_ALL(bezt);
989  changed = true;
990  }
991  prev_sel = false;
992  }
993  }
994 
995  prev_sel = false;
996  for (int i = editcurve->tot_curve_points - 1; i >= 0; i--) {
997  bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
998  BezTriple *bezt = &gpc_pt->bezt;
999  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1000  prev_sel = true;
1001  }
1002  else {
1003  /* unselected point - expand selection if previous was selected... */
1004  if (prev_sel) {
1005  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1006  BEZT_SEL_ALL(bezt);
1007  changed = true;
1008  }
1009  prev_sel = false;
1010  }
1011  }
1012  }
1013  }
1014  GP_EDITABLE_STROKES_END(gp_iter);
1015  }
1016  else {
1017  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
1018  if (gps->flag & GP_STROKE_SELECT) {
1019  bGPDspoint *pt;
1020  int i;
1021  bool prev_sel;
1022 
1023  /* First Pass: Go in forward order,
1024  * expanding selection if previous was selected (pre changes).
1025  * - This pass covers the "after" edges of selection islands
1026  */
1027  prev_sel = false;
1028  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1029  if (pt->flag & GP_SPOINT_SELECT) {
1030  /* selected point - just set flag for next point */
1031  prev_sel = true;
1032  }
1033  else {
1034  /* unselected point - expand selection if previous was selected... */
1035  if (prev_sel) {
1036  pt->flag |= GP_SPOINT_SELECT;
1037  changed = true;
1038  }
1039  prev_sel = false;
1040  }
1041  }
1042 
1043  /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
1044  * - This pass covers the "before" edges of selection islands
1045  */
1046  prev_sel = false;
1047  for (pt -= 1; i > 0; i--, pt--) {
1048  if (pt->flag & GP_SPOINT_SELECT) {
1049  prev_sel = true;
1050  }
1051  else {
1052  /* unselected point - expand selection if previous was selected... */
1053  if (prev_sel) {
1054  pt->flag |= GP_SPOINT_SELECT;
1055  changed = true;
1056  }
1057  prev_sel = false;
1058  }
1059  }
1060  }
1061  }
1062  CTX_DATA_END;
1063  }
1064 
1065  if (changed) {
1066  /* updates */
1068 
1069  /* copy on write tag is needed, or else no refresh happens */
1071 
1074  }
1075 
1076  return OPERATOR_FINISHED;
1077 }
1078 
1080 {
1081  /* identifiers */
1082  ot->name = "Select More";
1083  ot->idname = "GPENCIL_OT_select_more";
1084  ot->description = "Grow sets of selected Grease Pencil points";
1085 
1086  /* callbacks */
1089 
1090  /* flags */
1092 }
1093 
1096 /* -------------------------------------------------------------------- */
1101 {
1103  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1104 
1105  /* If not edit/sculpt mode, the event has been caught but not processed. */
1106  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
1107  return OPERATOR_CANCELLED;
1108  }
1109 
1110  bool changed = false;
1111  if (is_curve_edit) {
1112  GP_EDITABLE_STROKES_BEGIN (gp_iter, C, gpl, gps) {
1113  if (gps->editcurve != NULL && gps->flag & GP_STROKE_SELECT) {
1114  bGPDcurve *editcurve = gps->editcurve;
1115  int i;
1116 
1117  bool prev_sel = false;
1118  for (i = 0; i < editcurve->tot_curve_points; i++) {
1119  bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
1120  BezTriple *bezt = &gpc_pt->bezt;
1121  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1122  /* shrink if previous wasn't selected */
1123  if (prev_sel == false) {
1124  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1125  BEZT_DESEL_ALL(bezt);
1126  changed = true;
1127  }
1128  prev_sel = true;
1129  }
1130  else {
1131  /* mark previous as being unselected - and hence, is trigger for shrinking */
1132  prev_sel = false;
1133  }
1134  }
1135 
1136  /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
1137  * - This pass covers the "before" edges of selection islands
1138  */
1139  prev_sel = false;
1140  for (i = editcurve->tot_curve_points - 1; i > 0; i--) {
1141  bGPDcurve_point *gpc_pt = &editcurve->curve_points[i];
1142  BezTriple *bezt = &gpc_pt->bezt;
1143  if (gpc_pt->flag & GP_CURVE_POINT_SELECT) {
1144  /* shrink if previous wasn't selected */
1145  if (prev_sel == false) {
1146  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1147  BEZT_DESEL_ALL(bezt);
1148  changed = true;
1149  }
1150  prev_sel = true;
1151  }
1152  else {
1153  /* mark previous as being unselected - and hence, is trigger for shrinking */
1154  prev_sel = false;
1155  }
1156  }
1157  }
1158  }
1159  GP_EDITABLE_STROKES_END(gp_iter);
1160  }
1161  else {
1162  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
1163  if (gps->flag & GP_STROKE_SELECT) {
1164  bGPDspoint *pt;
1165  int i;
1166  bool prev_sel;
1167 
1168  /* First Pass: Go in forward order, shrinking selection
1169  * if previous was not selected (pre changes).
1170  * - This pass covers the "after" edges of selection islands
1171  */
1172  prev_sel = false;
1173  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1174  if (pt->flag & GP_SPOINT_SELECT) {
1175  /* shrink if previous wasn't selected */
1176  if (prev_sel == false) {
1177  pt->flag &= ~GP_SPOINT_SELECT;
1178  changed = true;
1179  }
1180  prev_sel = true;
1181  }
1182  else {
1183  /* mark previous as being unselected - and hence, is trigger for shrinking */
1184  prev_sel = false;
1185  }
1186  }
1187 
1188  /* Second Pass: Go in reverse order, doing the same as before (except in opposite order)
1189  * - This pass covers the "before" edges of selection islands
1190  */
1191  prev_sel = false;
1192  for (pt -= 1; i > 0; i--, pt--) {
1193  if (pt->flag & GP_SPOINT_SELECT) {
1194  /* shrink if previous wasn't selected */
1195  if (prev_sel == false) {
1196  pt->flag &= ~GP_SPOINT_SELECT;
1197  changed = true;
1198  }
1199  prev_sel = true;
1200  }
1201  else {
1202  /* mark previous as being unselected - and hence, is trigger for shrinking */
1203  prev_sel = false;
1204  }
1205  }
1206  }
1207  }
1208  CTX_DATA_END;
1209  }
1210 
1211  if (changed) {
1212  /* updates */
1214 
1215  /* copy on write tag is needed, or else no refresh happens */
1217 
1220  }
1221 
1222  return OPERATOR_FINISHED;
1223 }
1224 
1226 {
1227  /* identifiers */
1228  ot->name = "Select Less";
1229  ot->idname = "GPENCIL_OT_select_less";
1230  ot->description = "Shrink sets of selected Grease Pencil points";
1231 
1232  /* callbacks */
1235 
1236  /* flags */
1238 }
1239 
1242 /* -------------------------------------------------------------------- */
1254  bGPDlayer *gpl,
1255  bGPDstroke *gps,
1256  GP_SpaceConversion *gsc,
1257  const int mx,
1258  const int my,
1259  const int radius,
1260  const bool select,
1261  rcti *rect,
1262  const float diff_mat[4][4],
1263  const int selectmode,
1264  const float scale,
1265  const bool is_curve_edit)
1266 {
1267  bGPDspoint *pt = NULL;
1268  int x0 = 0, y0 = 0;
1269  int i;
1270  bool changed = false;
1271  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
1272  bGPDspoint *pt_active = NULL;
1273  bool hit = false;
1274 
1275  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1276  pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1277 
1278  bGPDspoint pt_temp;
1279  gpencil_point_to_parent_space(pt, diff_mat, &pt_temp);
1280  gpencil_point_to_xy(gsc, gps, &pt_temp, &x0, &y0);
1281 
1282  /* do boundbox check first */
1283  if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
1284  /* only check if point is inside */
1285  if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) {
1286  hit = true;
1287 
1288  /* change selection */
1289  if (select) {
1290  pt_active->flag |= GP_SPOINT_SELECT;
1291  gps_active->flag |= GP_STROKE_SELECT;
1292  BKE_gpencil_stroke_select_index_set(gpd, gps_active);
1293  }
1294  else {
1295  pt_active->flag &= ~GP_SPOINT_SELECT;
1296  gps_active->flag &= ~GP_STROKE_SELECT;
1298  }
1299  changed = true;
1300  /* if stroke mode, don't check more points */
1301  if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
1302  break;
1303  }
1304 
1305  /* Expand selection to segment. */
1306  if ((hit) && (selectmode == GP_SELECTMODE_SEGMENT) && (select) && (pt_active != NULL)) {
1307  float r_hita[3], r_hitb[3];
1308  bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
1310  gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
1311  }
1312  }
1313  }
1314  }
1315 
1316  /* If stroke mode expand selection. */
1317  if ((hit) && (selectmode == GP_SELECTMODE_STROKE)) {
1318  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1319  pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1320  if (pt_active != NULL) {
1321  if (select) {
1322  pt_active->flag |= GP_SPOINT_SELECT;
1323  }
1324  else {
1325  pt_active->flag &= ~GP_SPOINT_SELECT;
1326  }
1327  }
1328  }
1329  }
1330 
1331  /* If curve edit mode, generate the curve. */
1332  if (is_curve_edit && hit && gps_active->editcurve == NULL) {
1333  BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
1334  gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
1335  /* Select all curve points. */
1336  select_all_curve_points(gpd, gps_active, gps_active->editcurve, false);
1337  BKE_gpencil_stroke_geometry_update(gpd, gps_active);
1338  changed = true;
1339  }
1340 
1341  /* Ensure that stroke selection is in sync with its points. */
1342  BKE_gpencil_stroke_sync_selection(gpd, gps_active);
1343 
1344  return changed;
1345 }
1346 
1348  bGPDstroke *gps,
1349  bGPDcurve *gpc,
1350  const int mx,
1351  const int my,
1352  const int radius,
1353  const bool select,
1354  rcti *rect,
1355  const float diff_mat[4][4],
1356  const int selectmode)
1357 {
1358  ARegion *region = CTX_wm_region(C);
1359  View3D *v3d = CTX_wm_view3d(C);
1361  bGPdata *gpd = ob->data;
1362 
1363  const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
1364 
1365  bool hit = false;
1366  for (int i = 0; i < gpc->tot_curve_points; i++) {
1367  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1368  BezTriple *bezt = &gpc_pt->bezt;
1369 
1370  if (bezt->hide == 1) {
1371  continue;
1372  }
1373 
1374  const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
1375  (!only_selected || BEZT_ISSEL_ANY(bezt));
1376 
1377  /* if the handles are not visible only check ctrl point (vec[1])*/
1378  int from = (!handles_visible) ? 1 : 0;
1379  int to = (!handles_visible) ? 2 : 3;
1380 
1381  for (int j = from; j < to; j++) {
1382  float parent_co[3];
1383  mul_v3_m4v3(parent_co, diff_mat, bezt->vec[j]);
1384  int screen_co[2];
1385  /* do 2d projection */
1387  region, parent_co, screen_co, V3D_PROJ_RET_CLIP_BB | V3D_PROJ_RET_CLIP_WIN) !=
1388  V3D_PROJ_RET_OK) {
1389  continue;
1390  }
1391 
1392  /* view and bounding box test */
1393  if (ELEM(V2D_IS_CLIPPED, screen_co[0], screen_co[1]) &&
1394  !BLI_rcti_isect_pt(rect, screen_co[0], screen_co[1])) {
1395  continue;
1396  }
1397 
1398  /* test inside circle */
1399  int dist_x = screen_co[0] - mx;
1400  int dist_y = screen_co[1] - my;
1401  int dist = dist_x * dist_x + dist_y * dist_y;
1402  if (dist <= radius * radius) {
1403  hit = true;
1404  /* change selection */
1405  if (select) {
1406  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1407  BEZT_SEL_IDX(bezt, j);
1408  }
1409  else {
1410  BEZT_DESEL_IDX(bezt, j);
1411  if (!BEZT_ISSEL_ANY(bezt)) {
1412  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1413  }
1414  }
1415  }
1416  }
1417  }
1418 
1419  /* select the entire curve */
1420  if (hit && (selectmode == GP_SELECTMODE_STROKE)) {
1421  for (int i = 0; i < gpc->tot_curve_points; i++) {
1422  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1423  BezTriple *bezt = &gpc_pt->bezt;
1424 
1425  if (select) {
1426  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1427  BEZT_SEL_ALL(bezt);
1428  }
1429  else {
1430  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1431  BEZT_DESEL_ALL(bezt);
1432  }
1433  }
1434  }
1435 
1437 
1438  return hit;
1439 }
1440 
1442 {
1446  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1447 
1448  int selectmode;
1449  if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
1451  }
1452  else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
1454  }
1455  else {
1456  selectmode = ts->gpencil_selectmode_edit;
1457  }
1458 
1459  const float scale = ts->gp_sculpt.isect_threshold;
1460 
1461  /* If not edit/sculpt mode, the event has been caught but not processed. */
1462  if (GPENCIL_NONE_EDIT_MODE(gpd)) {
1463  return OPERATOR_CANCELLED;
1464  }
1465 
1466  ScrArea *area = CTX_wm_area(C);
1467 
1468  const int mx = RNA_int_get(op->ptr, "x");
1469  const int my = RNA_int_get(op->ptr, "y");
1470  const int radius = RNA_int_get(op->ptr, "radius");
1471 
1472  /* sanity checks */
1473  if (area == NULL) {
1474  BKE_report(op->reports, RPT_ERROR, "No active area");
1475  return OPERATOR_CANCELLED;
1476  }
1477 
1478  const eSelectOp sel_op = ED_select_op_modal(RNA_enum_get(op->ptr, "mode"),
1480  const bool select = (sel_op != SEL_OP_SUB);
1481 
1482  bool changed = false;
1483  /* for bounding rect around circle (for quicky intersection testing) */
1484  rcti rect = {0};
1485  rect.xmin = mx - radius;
1486  rect.ymin = my - radius;
1487  rect.xmax = mx + radius;
1488  rect.ymax = my + radius;
1489 
1490  if (is_curve_edit) {
1491  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1493  changed = true;
1494  }
1495 
1496  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
1497  {
1498  changed |= gpencil_do_curve_circle_sel(
1499  C, gps, gpc, mx, my, radius, select, &rect, gps_iter.diff_mat, selectmode);
1500  }
1501  GP_EDITABLE_CURVES_END(gps_iter);
1502  }
1503 
1504  if (changed == false) {
1505  GP_SpaceConversion gsc = {NULL};
1506  /* init space conversion stuff */
1508 
1509  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1511  changed = true;
1512  }
1513 
1514  /* find visible strokes, and select if hit */
1515  GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
1516  changed |= gpencil_stroke_do_circle_sel(gpd,
1517  gpl,
1518  gps,
1519  &gsc,
1520  mx,
1521  my,
1522  radius,
1523  select,
1524  &rect,
1525  gpstroke_iter.diff_mat,
1526  selectmode,
1527  scale,
1528  is_curve_edit);
1529  }
1530  GP_EVALUATED_STROKES_END(gpstroke_iter);
1531  }
1532 
1533  /* updates */
1534  if (changed) {
1536 
1537  /* copy on write tag is needed, or else no refresh happens */
1539 
1542  }
1543 
1544  return OPERATOR_FINISHED;
1545 }
1546 
1548 {
1549  /* identifiers */
1550  ot->name = "Circle Select";
1551  ot->description = "Select Grease Pencil strokes using brush selection";
1552  ot->idname = "GPENCIL_OT_select_circle";
1553 
1554  /* callbacks */
1560 
1561  /* flags */
1563 
1564  /* properties */
1567 }
1568 
1571 /* -------------------------------------------------------------------- */
1578 typedef struct GP_SelectUserData {
1579  int mx, my, radius;
1580  /* Bounding box rect */
1582  const int (*lasso_coords)[2];
1585 
1586 typedef bool (*GPencilTestFn)(ARegion *region,
1587  const float diff_mat[4][4],
1588  const float pt[3],
1590 
1591 #if 0
1592 static bool gpencil_stroke_fill_isect_rect(ARegion *region,
1593  bGPDstroke *gps,
1594  const float diff_mat[4][4],
1595  rcti rect)
1596 {
1597  int min[2] = {-INT_MAX, -INT_MAX};
1598  int max[2] = {INT_MAX, INT_MAX};
1599 
1600  int(*points2d)[2] = MEM_callocN(sizeof(int[2]) * gps->totpoints, __func__);
1601 
1602  for (int i = 0; i < gps->totpoints; i++) {
1603  bGPDspoint *pt = &gps->points[i];
1604  int *pt2d = points2d[i];
1605 
1606  int screen_co[2];
1607  gpencil_3d_point_to_screen_space(region, diff_mat, &pt->x, screen_co);
1608  DO_MINMAX2(screen_co, min, max);
1609 
1610  copy_v2_v2_int(pt2d, screen_co);
1611  }
1612 
1613  bool hit = false;
1614  /* check bounding box */
1615  rcti bb = {min[0], max[0], min[1], max[1]};
1616  if (BLI_rcti_isect(&rect, &bb, NULL)) {
1617  for (int i = 0; i < gps->tot_triangles; i++) {
1618  bGPDtriangle *tri = &gps->triangles[i];
1619  int pt1[2], pt2[2], pt3[2];
1620  int tri_min[2] = {-INT_MAX, -INT_MAX};
1621  int tri_max[2] = {INT_MAX, INT_MAX};
1622 
1623  copy_v2_v2_int(pt1, points2d[tri->verts[0]]);
1624  copy_v2_v2_int(pt2, points2d[tri->verts[1]]);
1625  copy_v2_v2_int(pt3, points2d[tri->verts[2]]);
1626 
1627  DO_MINMAX2(pt1, tri_min, tri_max);
1628  DO_MINMAX2(pt2, tri_min, tri_max);
1629  DO_MINMAX2(pt3, tri_min, tri_max);
1630 
1631  rcti tri_bb = {tri_min[0], tri_max[0], tri_min[1], tri_max[1]};
1632  /* Case 1: triangle is entirely inside box selection */
1633  /* (XXX: Can this even happen with no point inside the box?) */
1634  if (BLI_rcti_inside_rcti(&tri_bb, &rect)) {
1635  hit = true;
1636  break;
1637  }
1638 
1639  /* Case 2: rectangle intersects sides of triangle */
1640  if (BLI_rcti_isect_segment(&rect, pt1, pt2) || BLI_rcti_isect_segment(&rect, pt2, pt3) ||
1641  BLI_rcti_isect_segment(&rect, pt3, pt1)) {
1642  hit = true;
1643  break;
1644  }
1645 
1646  /* TODO: Case 3: rectangle is inside the triangle */
1647  }
1648  }
1649 
1650  MEM_freeN(points2d);
1651  return hit;
1652 }
1653 #endif
1654 
1656  Object *ob,
1657  GPencilTestFn is_inside_fn,
1658  rcti UNUSED(box),
1660  const bool strokemode,
1661  const eSelectOp sel_op)
1662 {
1663  ARegion *region = CTX_wm_region(C);
1664  View3D *v3d = CTX_wm_view3d(C);
1665  bGPdata *gpd = ob->data;
1666  const bool handle_only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
1667  const bool handle_all = (v3d->overlay.handle_display == CURVE_HANDLE_ALL);
1668 
1669  bool hit = false;
1670  bool changed = false;
1671  bool whole = false;
1672 
1673  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
1674  {
1675  bool any_select = false;
1676  for (int i = 0; i < gpc->tot_curve_points; i++) {
1677  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1678  BezTriple *bezt = &gpc_pt->bezt;
1679 
1680  if (bezt->hide == 1) {
1681  continue;
1682  }
1683 
1684  const bool handles_visible = (handle_all || (handle_only_selected &&
1685  (gpc_pt->flag & GP_CURVE_POINT_SELECT)));
1686 
1687  if (handles_visible) {
1688  for (int j = 0; j < 3; j++) {
1689  const bool is_select = BEZT_ISSEL_IDX(bezt, j);
1690  bool is_inside = is_inside_fn(region, gps_iter.diff_mat, bezt->vec[j], user_data);
1691  if (strokemode) {
1692  if (is_inside) {
1693  hit = true;
1694  any_select = true;
1695  break;
1696  }
1697  }
1698  else {
1699  const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1700  if (sel_op_result != -1) {
1701  if (sel_op_result) {
1702  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1703  BEZT_SEL_IDX(bezt, j);
1704  any_select = true;
1705  }
1706  else {
1707  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1708  BEZT_DESEL_IDX(bezt, j);
1709  }
1710  changed = true;
1711  hit = true;
1712  }
1713  else {
1714  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1715  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1716  BEZT_DESEL_IDX(bezt, j);
1717  }
1718  }
1719  }
1720  }
1721  }
1722  /* if the handles are not visible only check ctrl point (vec[1])*/
1723  else {
1724  const bool is_select = bezt->f2;
1725  bool is_inside = is_inside_fn(region, gps_iter.diff_mat, bezt->vec[1], user_data);
1726  if (strokemode) {
1727  if (is_inside) {
1728  hit = true;
1729  any_select = true;
1730  }
1731  }
1732  else {
1733  const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1734  if (sel_op_result != -1) {
1735  if (sel_op_result) {
1736  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1737  bezt->f2 |= SELECT;
1738  any_select = true;
1739  }
1740  else {
1741  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1742  bezt->f2 &= ~SELECT;
1743  }
1744  changed = true;
1745  hit = true;
1746  }
1747  else {
1748  if (SEL_OP_USE_PRE_DESELECT(sel_op)) {
1749  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1750  bezt->f2 &= ~SELECT;
1751  }
1752  }
1753  }
1754  }
1755  }
1756 
1757  /* TODO: Fix selection for filled in curves. */
1758 #if 0
1759  if (!hit) {
1760  /* check if we selected the inside of a filled curve */
1761  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
1762  if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
1763  continue;
1764  }
1765 
1766  whole = gpencil_stroke_fill_isect_rect(region, gps, gps_iter.diff_mat, box);
1767  }
1768 #endif
1769  /* select the entire curve */
1770  if (strokemode || whole) {
1771  const int sel_op_result = ED_select_op_action_deselected(sel_op, any_select, hit || whole);
1772  if (sel_op_result != -1) {
1773  for (int i = 0; i < gpc->tot_curve_points; i++) {
1774  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
1775  BezTriple *bezt = &gpc_pt->bezt;
1776 
1777  if (sel_op_result) {
1778  gpc_pt->flag |= GP_CURVE_POINT_SELECT;
1779  BEZT_SEL_ALL(bezt);
1780  }
1781  else {
1782  gpc_pt->flag &= ~GP_CURVE_POINT_SELECT;
1783  BEZT_DESEL_ALL(bezt);
1784  }
1785  }
1786 
1787  if (sel_op_result) {
1788  gpc->flag |= GP_CURVE_SELECT;
1789  }
1790  else {
1791  gpc->flag &= ~GP_CURVE_SELECT;
1792  }
1793  changed = true;
1794  }
1795  }
1796 
1798  }
1799  GP_EDITABLE_CURVES_END(gps_iter);
1800 
1801  return changed;
1802 }
1803 
1805  Object *ob,
1806  bGPdata *gpd,
1807  GPencilTestFn is_inside_fn,
1808  rcti box,
1810  const bool strokemode,
1811  const bool segmentmode,
1812  const eSelectOp sel_op,
1813  const float scale,
1814  const bool is_curve_edit)
1815 {
1816  GP_SpaceConversion gsc = {NULL};
1817  bool changed = false;
1818  /* init space conversion stuff */
1820 
1821  /* deselect all strokes first? */
1822  if (SEL_OP_USE_PRE_DESELECT(sel_op) || (GPENCIL_PAINT_MODE(gpd))) {
1823  /* Set selection index to 0. */
1824  gpd->select_last_index = 0;
1825 
1826  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
1827  bGPDspoint *pt;
1828  int i;
1829 
1830  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1831  pt->flag &= ~GP_SPOINT_SELECT;
1832  }
1833 
1834  gps->flag &= ~GP_STROKE_SELECT;
1836  }
1837  CTX_DATA_END;
1838 
1839  changed = true;
1840  }
1841 
1842  /* select/deselect points */
1843  GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
1844  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
1845  bool whole = false;
1846 
1847  bGPDspoint *pt;
1848  int i;
1849  bool hit = false;
1850  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1851  bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1852 
1853  /* convert point coords to screenspace */
1854  const bool is_inside = is_inside_fn(gsc.region, gpstroke_iter.diff_mat, &pt->x, user_data);
1855  if (strokemode == false) {
1856  const bool is_select = (pt_active->flag & GP_SPOINT_SELECT) != 0;
1857  const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1858  if (sel_op_result != -1) {
1859  SET_FLAG_FROM_TEST(pt_active->flag, sel_op_result, GP_SPOINT_SELECT);
1860  changed = true;
1861  hit = true;
1862 
1863  /* Expand selection to segment. */
1864  if (segmentmode) {
1865  bool hit_select = (bool)(pt_active->flag & GP_SPOINT_SELECT);
1866  float r_hita[3], r_hitb[3];
1868  gpd, gpl, gps_active, pt_active, hit_select, false, scale, r_hita, r_hitb);
1869  }
1870  }
1871  }
1872  else {
1873  if (is_inside) {
1874  hit = true;
1875  break;
1876  }
1877  }
1878  }
1879 
1880  /* If nothing hit, check if the mouse is inside a filled stroke using the center or
1881  * Box or lasso area. */
1882  if (!hit) {
1883  /* Only check filled strokes. */
1884  MaterialGPencilStyle *gp_style = BKE_gpencil_material_settings(ob, gps->mat_nr + 1);
1885  if ((gp_style->flag & GP_MATERIAL_FILL_SHOW) == 0) {
1886  continue;
1887  }
1888  int mval[2];
1889  mval[0] = (box.xmax + box.xmin) / 2;
1890  mval[1] = (box.ymax + box.ymin) / 2;
1891 
1892  whole = ED_gpencil_stroke_point_is_inside(gps, &gsc, mval, gpstroke_iter.diff_mat);
1893  }
1894 
1895  /* if stroke mode expand selection. */
1896  if ((strokemode) || (whole)) {
1897  const bool is_select = BKE_gpencil_stroke_select_check(gps_active) || whole;
1898  const bool is_inside = hit || whole;
1899  const int sel_op_result = ED_select_op_action_deselected(sel_op, is_select, is_inside);
1900  if (sel_op_result != -1) {
1901  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
1902  bGPDspoint *pt_active = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
1903 
1904  if (sel_op_result) {
1905  pt_active->flag |= GP_SPOINT_SELECT;
1906  }
1907  else {
1908  pt_active->flag &= ~GP_SPOINT_SELECT;
1909  }
1910  }
1911  changed = true;
1912  }
1913  }
1914 
1915  /* If curve edit mode, generate the curve. */
1916  if (is_curve_edit && (hit || whole) && gps_active->editcurve == NULL) {
1917  BKE_gpencil_stroke_editcurve_update(gpd, gpl, gps_active);
1918  gps_active->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
1919  /* Select all curve points. */
1920  select_all_curve_points(gpd, gps_active, gps_active->editcurve, false);
1921  BKE_gpencil_stroke_geometry_update(gpd, gps_active);
1922  changed = true;
1923  }
1924 
1925  /* Ensure that stroke selection is in sync with its points */
1926  BKE_gpencil_stroke_sync_selection(gpd, gps_active);
1927  }
1928  GP_EVALUATED_STROKES_END(gpstroke_iter);
1929 
1930  return changed;
1931 }
1932 
1934  wmOperator *op,
1935  GPencilTestFn is_inside_fn,
1936  rcti box,
1938 {
1942  ScrArea *area = CTX_wm_area(C);
1943  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
1944 
1945  int selectmode;
1946  if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
1948  }
1949  else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
1951  }
1952  else {
1953  selectmode = ts->gpencil_selectmode_edit;
1954  }
1955 
1956  const bool strokemode = ((selectmode == GP_SELECTMODE_STROKE) &&
1957  ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
1958  const bool segmentmode = ((selectmode == GP_SELECTMODE_SEGMENT) &&
1959  ((gpd->flag & GP_DATA_STROKE_PAINTMODE) == 0));
1960 
1961  const eSelectOp sel_op = RNA_enum_get(op->ptr, "mode");
1962  const float scale = ts->gp_sculpt.isect_threshold;
1963 
1964  bool changed = false;
1965 
1966  /* sanity checks */
1967  if (area == NULL) {
1968  BKE_report(op->reports, RPT_ERROR, "No active area");
1969  return OPERATOR_CANCELLED;
1970  }
1971 
1972  if (is_curve_edit) {
1973  changed = gpencil_generic_curve_select(
1974  C, ob, is_inside_fn, box, user_data, strokemode, sel_op);
1975  }
1976 
1977  if (changed == false) {
1978  changed = gpencil_generic_stroke_select(C,
1979  ob,
1980  gpd,
1981  is_inside_fn,
1982  box,
1983  user_data,
1984  strokemode,
1985  segmentmode,
1986  sel_op,
1987  scale,
1988  is_curve_edit);
1989  }
1990 
1991  /* if paint mode,delete selected points */
1992  if (GPENCIL_PAINT_MODE(gpd)) {
1994  changed = true;
1996  }
1997 
1998  /* updates */
1999  if (changed) {
2001 
2002  /* copy on write tag is needed, or else no refresh happens */
2004 
2007  }
2008  return OPERATOR_FINISHED;
2009 }
2010 
2013 /* -------------------------------------------------------------------- */
2017 static bool gpencil_test_box(ARegion *region,
2018  const float diff_mat[4][4],
2019  const float pt[3],
2021 {
2022  int co[2] = {0};
2023  if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
2024  return BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]);
2025  }
2026  return false;
2027 }
2028 
2030 {
2031  GP_SelectUserData data = {0};
2033  rcti rect = data.rect;
2034  return gpencil_generic_select_exec(C, op, gpencil_test_box, rect, &data);
2035 }
2036 
2038 {
2039  /* identifiers */
2040  ot->name = "Box Select";
2041  ot->description = "Select Grease Pencil strokes within a rectangular region";
2042  ot->idname = "GPENCIL_OT_select_box";
2043 
2044  /* callbacks */
2049 
2051 
2052  /* flags */
2054 
2055  /* properties */
2058 }
2059 
2062 /* -------------------------------------------------------------------- */
2066 static bool gpencil_test_lasso(ARegion *region,
2067  const float diff_mat[4][4],
2068  const float pt[3],
2070 {
2071  int co[2] = {0};
2072  if (gpencil_3d_point_to_screen_space(region, diff_mat, pt, co)) {
2073  /* test if in lasso boundbox + within the lasso noose */
2074  return (BLI_rcti_isect_pt(&user_data->rect, co[0], co[1]) &&
2076  user_data->lasso_coords, user_data->lasso_coords_len, co[0], co[1], INT_MAX));
2077  }
2078  return false;
2079 }
2080 
2082 {
2083  struct GP_SelectUserData data = {0};
2084  data.lasso_coords = WM_gesture_lasso_path_to_array(C, op, &data.lasso_coords_len);
2085 
2086  /* Sanity check. */
2087  if (data.lasso_coords == NULL) {
2088  return OPERATOR_PASS_THROUGH;
2089  }
2090 
2091  /* Compute boundbox of lasso (for faster testing later). */
2092  BLI_lasso_boundbox(&data.rect, data.lasso_coords, data.lasso_coords_len);
2093 
2094  rcti rect = data.rect;
2096 
2097  MEM_freeN((void *)data.lasso_coords);
2098 
2099  return ret;
2100 }
2101 
2103 {
2104  ot->name = "Lasso Select Strokes";
2105  ot->description = "Select Grease Pencil strokes using lasso selection";
2106  ot->idname = "GPENCIL_OT_select_lasso";
2107 
2113 
2114  /* flags */
2115  ot->flag = OPTYPE_UNDO;
2116 
2117  /* properties */
2120 }
2121 
2124 /* -------------------------------------------------------------------- */
2129  const int mval[2],
2130  const int radius_squared,
2131  bGPDlayer **r_gpl,
2132  bGPDstroke **r_gps,
2133  bGPDcurve **r_gpc,
2134  bGPDcurve_point **r_pt,
2135  char *handle)
2136 {
2137  ARegion *region = CTX_wm_region(C);
2138  View3D *v3d = CTX_wm_view3d(C);
2139  const bool only_selected = (v3d->overlay.handle_display == CURVE_HANDLE_SELECTED);
2140 
2141  int hit_distance = radius_squared;
2142 
2143  GP_EDITABLE_CURVES_BEGIN(gps_iter, C, gpl, gps, gpc)
2144  {
2145  for (int i = 0; i < gpc->tot_curve_points; i++) {
2146  bGPDcurve_point *gpc_pt = &gpc->curve_points[i];
2147  BezTriple *bezt = &gpc_pt->bezt;
2148 
2149  if (bezt->hide == 1) {
2150  continue;
2151  }
2152 
2153  const bool handles_visible = (v3d->overlay.handle_display != CURVE_HANDLE_NONE) &&
2154  (!only_selected || BEZT_ISSEL_ANY(bezt));
2155 
2156  /* if the handles are not visible only check ctrl point (vec[1])*/
2157  int from = (!handles_visible) ? 1 : 0;
2158  int to = (!handles_visible) ? 2 : 3;
2159 
2160  for (int j = from; j < to; j++) {
2161  int screen_co[2];
2162  if (gpencil_3d_point_to_screen_space(region, gps_iter.diff_mat, bezt->vec[j], screen_co)) {
2163  const int pt_distance = len_manhattan_v2v2_int(mval, screen_co);
2164 
2165  if (pt_distance <= radius_squared && pt_distance < hit_distance) {
2166  *r_gpl = gpl;
2167  *r_gps = gps;
2168  *r_gpc = gpc;
2169  *r_pt = gpc_pt;
2170  *handle = j;
2171  hit_distance = pt_distance;
2172  }
2173  }
2174  }
2175  }
2176  }
2177  GP_EDITABLE_CURVES_END(gps_iter);
2178 }
2179 
2181 {
2182  ScrArea *area = CTX_wm_area(C);
2186  const float scale = ts->gp_sculpt.isect_threshold;
2187  const bool is_curve_edit = (bool)GPENCIL_CURVE_EDIT_SESSIONS_ON(gpd);
2188 
2189  /* "radius" is simply a threshold (screen space) to make it easier to test with a tolerance */
2190  const float radius = 0.4f * U.widget_unit;
2191  const int radius_squared = (int)(radius * radius);
2192 
2193  const bool use_shift_extend = RNA_boolean_get(op->ptr, "use_shift_extend");
2194  bool extend = RNA_boolean_get(op->ptr, "extend") || use_shift_extend;
2195  bool deselect = RNA_boolean_get(op->ptr, "deselect");
2196  bool toggle = RNA_boolean_get(op->ptr, "toggle");
2197  bool whole = RNA_boolean_get(op->ptr, "entire_strokes");
2198  const bool deselect_all = RNA_boolean_get(op->ptr, "deselect_all") && !use_shift_extend;
2199 
2200  int mval[2] = {0};
2201  /* get mouse location */
2202  RNA_int_get_array(op->ptr, "location", mval);
2203 
2204  GP_SpaceConversion gsc = {NULL};
2205 
2206  bGPDlayer *hit_layer = NULL;
2207  bGPDstroke *hit_stroke = NULL;
2208  bGPDspoint *hit_point = NULL;
2209  bGPDcurve *hit_curve = NULL;
2210  bGPDcurve_point *hit_curve_point = NULL;
2211  char hit_curve_handle = 0;
2212  int hit_distance = radius_squared;
2213 
2214  /* sanity checks */
2215  if (area == NULL) {
2216  BKE_report(op->reports, RPT_ERROR, "No active area");
2217  return OPERATOR_CANCELLED;
2218  }
2219 
2220  /* if select mode is stroke, use whole stroke */
2221  if ((ob) && (ob->mode == OB_MODE_SCULPT_GPENCIL)) {
2224  }
2225  else if ((ob) && (ob->mode == OB_MODE_VERTEX_GPENCIL)) {
2228  }
2229  else {
2230  whole |= (bool)(ts->gpencil_selectmode_edit == GP_SELECTMODE_STROKE);
2231  }
2232 
2233  if (is_curve_edit) {
2235  mval,
2236  radius_squared,
2237  &hit_layer,
2238  &hit_stroke,
2239  &hit_curve,
2240  &hit_curve_point,
2241  &hit_curve_handle);
2242  }
2243 
2244  if (hit_curve == NULL) {
2245  /* init space conversion stuff */
2247 
2248  /* First Pass: Find stroke point which gets hit */
2249  GP_EVALUATED_STROKES_BEGIN (gpstroke_iter, C, gpl, gps) {
2250  bGPDstroke *gps_active = (gps->runtime.gps_orig) ? gps->runtime.gps_orig : gps;
2251  bGPDspoint *pt;
2252  int i;
2253 
2254  /* firstly, check for hit-point */
2255  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2256  int xy[2];
2257 
2258  bGPDspoint pt2;
2259  gpencil_point_to_parent_space(pt, gpstroke_iter.diff_mat, &pt2);
2260  gpencil_point_to_xy(&gsc, gps_active, &pt2, &xy[0], &xy[1]);
2261 
2262  /* do boundbox check first */
2263  if (!ELEM(V2D_IS_CLIPPED, xy[0], xy[1])) {
2264  const int pt_distance = len_manhattan_v2v2_int(mval, xy);
2265 
2266  /* check if point is inside */
2267  if (pt_distance <= radius_squared) {
2268  /* only use this point if it is a better match than the current hit - T44685 */
2269  if (pt_distance < hit_distance) {
2270  hit_layer = gpl;
2271  hit_stroke = gps_active;
2272  hit_point = (pt->runtime.pt_orig) ? pt->runtime.pt_orig : pt;
2273  hit_distance = pt_distance;
2274  }
2275  }
2276  }
2277  }
2278  }
2279  GP_EVALUATED_STROKES_END(gpstroke_iter);
2280  }
2281 
2282  /* Abort if nothing hit... */
2283  if (!hit_curve && !hit_curve_point && !hit_point && !hit_stroke) {
2284 
2285  if (deselect_all) {
2286  /* since left mouse select change, deselect all if click outside any hit */
2288 
2289  /* copy on write tag is needed, or else no refresh happens */
2294 
2295  return OPERATOR_FINISHED;
2296  }
2297 
2298  return OPERATOR_CANCELLED;
2299  }
2300 
2301  /* select all handles if the click was on the curve but not on a handle */
2302  if (is_curve_edit && hit_point != NULL) {
2303  whole = true;
2304  hit_curve = hit_stroke->editcurve;
2305  }
2306 
2307  /* adjust selection behavior - for toggle option */
2308  if (toggle) {
2309  if (hit_curve_point != NULL) {
2310  BezTriple *bezt = &hit_curve_point->bezt;
2311  if ((bezt->f1 & SELECT) && (hit_curve_handle == 0)) {
2312  deselect = true;
2313  }
2314  if ((bezt->f2 & SELECT) && (hit_curve_handle == 1)) {
2315  deselect = true;
2316  }
2317  if ((bezt->f3 & SELECT) && (hit_curve_handle == 2)) {
2318  deselect = true;
2319  }
2320  }
2321  else {
2322  deselect = (hit_point->flag & GP_SPOINT_SELECT) != 0;
2323  }
2324  }
2325 
2326  /* If not extending selection, deselect everything else */
2327  if (extend == false) {
2329  }
2330 
2331  /* Perform selection operations... */
2332  if (whole) {
2333  /* Generate editcurve if it does not exist */
2334  if (is_curve_edit && hit_curve == NULL) {
2335  BKE_gpencil_stroke_editcurve_update(gpd, hit_layer, hit_stroke);
2336  hit_stroke->flag |= GP_STROKE_NEEDS_CURVE_UPDATE;
2337  BKE_gpencil_stroke_geometry_update(gpd, hit_stroke);
2338  hit_curve = hit_stroke->editcurve;
2339  }
2340  /* select all curve points */
2341  if (hit_curve != NULL) {
2342  select_all_curve_points(gpd, hit_stroke, hit_curve, deselect);
2343  }
2344  else {
2345  bGPDspoint *pt;
2346  int i;
2347 
2348  /* entire stroke's points */
2349  for (i = 0, pt = hit_stroke->points; i < hit_stroke->totpoints; i++, pt++) {
2350  if (deselect == false) {
2351  pt->flag |= GP_SPOINT_SELECT;
2352  }
2353  else {
2354  pt->flag &= ~GP_SPOINT_SELECT;
2355  }
2356  }
2357 
2358  /* stroke too... */
2359  if (deselect == false) {
2360  hit_stroke->flag |= GP_STROKE_SELECT;
2361  BKE_gpencil_stroke_select_index_set(gpd, hit_stroke);
2362  }
2363  else {
2364  hit_stroke->flag &= ~GP_STROKE_SELECT;
2366  }
2367  }
2368  }
2369  else {
2370  /* just the point (and the stroke) */
2371  if (deselect == false) {
2372  if (hit_curve_point != NULL) {
2373  hit_curve_point->flag |= GP_CURVE_POINT_SELECT;
2374  BEZT_SEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
2375  hit_curve->flag |= GP_CURVE_SELECT;
2376  hit_stroke->flag |= GP_STROKE_SELECT;
2377  BKE_gpencil_stroke_select_index_set(gpd, hit_stroke);
2378  }
2379  else {
2380  /* we're adding selection, so selection must be true */
2381  hit_point->flag |= GP_SPOINT_SELECT;
2382  hit_stroke->flag |= GP_STROKE_SELECT;
2383  BKE_gpencil_stroke_select_index_set(gpd, hit_stroke);
2384 
2385  /* expand selection to segment */
2386  int selectmode;
2387  if (ob && ob->mode == OB_MODE_SCULPT_GPENCIL) {
2389  }
2390  else if (ob && ob->mode == OB_MODE_VERTEX_GPENCIL) {
2392  }
2393  else {
2394  selectmode = ts->gpencil_selectmode_edit;
2395  }
2396 
2397  if (selectmode == GP_SELECTMODE_SEGMENT) {
2398  float r_hita[3], r_hitb[3];
2399  bool hit_select = (bool)(hit_point->flag & GP_SPOINT_SELECT);
2401  gpd, hit_layer, hit_stroke, hit_point, hit_select, false, scale, r_hita, r_hitb);
2402  }
2403  }
2404  }
2405  else {
2406  if (hit_curve_point != NULL) {
2407  BEZT_DESEL_IDX(&hit_curve_point->bezt, hit_curve_handle);
2408  if (!BEZT_ISSEL_ANY(&hit_curve_point->bezt)) {
2409  hit_curve_point->flag &= ~GP_CURVE_POINT_SELECT;
2410  }
2411  BKE_gpencil_curve_sync_selection(gpd, hit_stroke);
2412  }
2413  else {
2414  /* deselect point */
2415  hit_point->flag &= ~GP_SPOINT_SELECT;
2416 
2417  /* ensure that stroke is selected correctly */
2418  BKE_gpencil_stroke_sync_selection(gpd, hit_stroke);
2419  }
2420  }
2421  }
2422 
2423  /* updates */
2424  if (hit_curve_point != NULL || hit_point != NULL) {
2426 
2427  /* copy on write tag is needed, or else no refresh happens */
2429 
2432  }
2433 
2434  return OPERATOR_FINISHED;
2435 }
2436 
2437 static int gpencil_select_invoke(bContext *C, wmOperator *op, const wmEvent *event)
2438 {
2439  RNA_int_set_array(op->ptr, "location", event->mval);
2440 
2441  if (!RNA_struct_property_is_set(op->ptr, "use_shift_extend")) {
2442  RNA_boolean_set(op->ptr, "use_shift_extend", event->shift);
2443  }
2444 
2445  return gpencil_select_exec(C, op);
2446 }
2447 
2449 {
2450  PropertyRNA *prop;
2451 
2452  /* identifiers */
2453  ot->name = "Select";
2454  ot->description = "Select Grease Pencil strokes and/or stroke points";
2455  ot->idname = "GPENCIL_OT_select";
2456 
2457  /* callbacks */
2461 
2462  /* flag */
2463  ot->flag = OPTYPE_UNDO;
2464 
2465  /* properties */
2467 
2468  prop = RNA_def_boolean(ot->srna,
2469  "entire_strokes",
2470  false,
2471  "Entire Strokes",
2472  "Select entire strokes instead of just the nearest stroke vertex");
2474 
2475  prop = RNA_def_int_vector(ot->srna,
2476  "location",
2477  2,
2478  NULL,
2479  INT_MIN,
2480  INT_MAX,
2481  "Location",
2482  "Mouse location",
2483  INT_MIN,
2484  INT_MAX);
2486 
2487  prop = RNA_def_boolean(ot->srna, "use_shift_extend", false, "Extend", "");
2489 }
2490 
2491 /* Select by Vertex Color. */
2492 /* Helper to create a hash of colors. */
2494  Object *ob,
2495  const int threshold,
2496  GHash *hue_table)
2497 {
2498  const float range = pow(10, 5 - threshold);
2499  float hsv[3];
2500 
2501  /* Extract all colors. */
2502  CTX_DATA_BEGIN (C, bGPDlayer *, gpl, editable_gpencil_layers) {
2503  LISTBASE_FOREACH (bGPDframe *, gpf, &gpl->frames) {
2504  LISTBASE_FOREACH (bGPDstroke *, gps, &gpf->strokes) {
2505  if (ED_gpencil_stroke_can_use(C, gps) == false) {
2506  continue;
2507  }
2508  if (ED_gpencil_stroke_material_editable(ob, gpl, gps) == false) {
2509  continue;
2510  }
2511  if ((gps->flag & GP_STROKE_SELECT) == 0) {
2512  continue;
2513  }
2514 
2515  /* Read all points to get all colors selected. */
2516  bGPDspoint *pt;
2517  int i;
2518  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2519  if (((pt->flag & GP_SPOINT_SELECT) == 0) || (pt->vert_color[3] == 0.0f)) {
2520  continue;
2521  }
2522  /* Round Hue value. */
2523  rgb_to_hsv_compat_v(pt->vert_color, hsv);
2524  uint key = truncf(hsv[0] * range);
2525  if (!BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
2526  BLI_ghash_insert(hue_table, POINTER_FROM_INT(key), POINTER_FROM_INT(key));
2527  }
2528  }
2529  }
2530  }
2531  }
2532  CTX_DATA_END;
2533 }
2534 
2536 {
2539  if ((ob == NULL) || (ob->type != OB_GPENCIL)) {
2540  return false;
2541  }
2542  bGPdata *gpd = (bGPdata *)ob->data;
2543 
2544  if (GPENCIL_VERTEX_MODE(gpd)) {
2546  return false;
2547  }
2548 
2549  /* Any data to use. */
2550  if (gpd->layers.first) {
2551  return true;
2552  }
2553  }
2554 
2555  return false;
2556 }
2557 
2559 {
2563 
2564  const float threshold = RNA_int_get(op->ptr, "threshold");
2565  const int selectmode = gpencil_select_mode_from_vertex(ts->gpencil_selectmode_vertex);
2566  const float range = pow(10, 5 - threshold);
2567 
2568  bool changed = false;
2569 
2570  /* Create a hash table with all selected colors. */
2571  GHash *hue_table = BLI_ghash_int_new(__func__);
2572  gpencil_selected_hue_table(C, ob, threshold, hue_table);
2573  if (BLI_ghash_len(hue_table) == 0) {
2574  BKE_report(op->reports, RPT_ERROR, "Select before some Vertex to use as a filter color");
2575  BLI_ghash_free(hue_table, NULL, NULL);
2576 
2577  return OPERATOR_CANCELLED;
2578  }
2579 
2580  /* Select any visible stroke that uses any of these colors. */
2581  CTX_DATA_BEGIN (C, bGPDstroke *, gps, editable_gpencil_strokes) {
2582  bGPDspoint *pt;
2583  int i;
2584  bool gps_selected = false;
2585  /* Check all stroke points. */
2586  for (i = 0, pt = gps->points; i < gps->totpoints; i++, pt++) {
2587  if (pt->vert_color[3] == 0.0f) {
2588  continue;
2589  }
2590 
2591  /* Only check Hue to get value and saturation full ranges. */
2592  float hsv[3];
2593  /* Round Hue value. */
2594  rgb_to_hsv_compat_v(pt->vert_color, hsv);
2595  uint key = truncf(hsv[0] * range);
2596 
2597  if (BLI_ghash_haskey(hue_table, POINTER_FROM_INT(key))) {
2598  pt->flag |= GP_SPOINT_SELECT;
2599  gps_selected = true;
2600  }
2601  }
2602 
2603  if (gps_selected) {
2604  gps->flag |= GP_STROKE_SELECT;
2606 
2607  /* Extend stroke selection. */
2608  if (selectmode == GP_SELECTMODE_STROKE) {
2609  bGPDspoint *pt1 = NULL;
2610 
2611  for (i = 0, pt1 = gps->points; i < gps->totpoints; i++, pt1++) {
2612  pt1->flag |= GP_SPOINT_SELECT;
2613  }
2614  }
2615  }
2616  }
2617  CTX_DATA_END;
2618 
2619  if (changed) {
2620  /* updates */
2622 
2623  /* copy on write tag is needed, or else no refresh happens */
2625 
2628  }
2629 
2630  /* Free memory. */
2631  if (hue_table != NULL) {
2632  BLI_ghash_free(hue_table, NULL, NULL);
2633  }
2634 
2635  return OPERATOR_FINISHED;
2636 }
2637 
2639 {
2640  PropertyRNA *prop;
2641 
2642  /* identifiers */
2643  ot->name = "Select Vertex Color";
2644  ot->idname = "GPENCIL_OT_select_vertex_color";
2645  ot->description = "Select all points with similar vertex color of current selected";
2646 
2647  /* callbacks */
2650 
2651  /* flags */
2653 
2654  /* properties */
2655  prop = RNA_def_int(
2656  ot->srna,
2657  "threshold",
2658  0,
2659  0,
2660  5,
2661  "Threshold",
2662  "Tolerance of the selection. Higher values select a wider range of similar colors",
2663  0,
2664  5);
2665  /* avoid re-using last var */
2667 }
2668 
struct ScrArea * CTX_wm_area(const bContext *C)
Definition: context.c:714
struct Scene * CTX_data_scene(const bContext *C)
Definition: context.c:1034
#define CTX_DATA_BEGIN(C, Type, instance, member)
Definition: BKE_context.h:252
struct Object * CTX_data_active_object(const bContext *C)
Definition: context.c:1279
struct View3D * CTX_wm_view3d(const bContext *C)
Definition: context.c:760
struct ARegion * CTX_wm_region(const bContext *C)
Definition: context.c:725
struct ToolSettings * CTX_data_tool_settings(const bContext *C)
Definition: context.c:1208
#define CTX_DATA_END
Definition: BKE_context.h:260
void BKE_gpencil_curve_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1167
void BKE_gpencil_stroke_select_index_set(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1203
void BKE_gpencil_stroke_select_index_reset(struct bGPDstroke *gps)
Definition: gpencil.c:1210
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:1307
@ GP_GETFRAME_USE_PREV
Definition: BKE_gpencil.h:187
void BKE_gpencil_stroke_sync_selection(struct bGPdata *gpd, struct bGPDstroke *gps)
Definition: gpencil.c:1139
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:713
void BKE_report(ReportList *reports, ReportType type, const char *message)
Definition: report.c:104
#define BLI_assert(a)
Definition: BLI_assert.h:58
GSet * BLI_gset_str_new(const char *info)
struct GSet GSet
Definition: BLI_ghash.h:189
unsigned int BLI_ghash_len(GHash *gh) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:744
bool BLI_ghash_haskey(GHash *gh, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:941
GHash * BLI_ghash_int_new(const char *info) ATTR_MALLOC ATTR_WARN_UNUSED_RESULT
bool BLI_gset_haskey(GSet *gs, const void *key) ATTR_WARN_UNUSED_RESULT
Definition: BLI_ghash.c:1216
void BLI_ghash_insert(GHash *gh, void *key, void *val)
Definition: BLI_ghash.c:756
void BLI_ghash_free(GHash *gh, GHashKeyFreeFP keyfreefp, GHashValFreeFP valfreefp)
Definition: BLI_ghash.c:1008
void BLI_gset_free(GSet *gs, GSetKeyFreeFP keyfreefp)
Definition: BLI_ghash.c:1253
bool BLI_gset_add(GSet *gs, void *key)
Definition: BLI_ghash.c:1160
void BLI_lasso_boundbox(struct rcti *rect, const int mcoords[][2], const unsigned int mcoords_len)
Definition: lasso_2d.c:31
bool BLI_lasso_is_point_inside(const int mcoords[][2], const unsigned int mcoords_len, const int sx, const int sy, const int error_value)
Definition: lasso_2d.c:54
#define LISTBASE_FOREACH(type, var, list)
Definition: BLI_listbase.h:172
void rgb_to_hsv_compat_v(const float rgb[3], float r_hsv[3])
Definition: math_color.c:342
void mul_v3_m4v3(float r[3], const float M[4][4], const float v[3])
Definition: math_matrix.c:742
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
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, const int x, const 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:226
unsigned int uint
Definition: BLI_sys_types.h:83
#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:599
@ ID_RECALC_COPY_ON_WRITE
Definition: DNA_ID.h:654
@ ID_RECALC_GEOMETRY
Definition: DNA_ID.h:611
#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
#define CFRA
@ 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
#define SEL_OP_USE_PRE_DESELECT(sel_op)
@ SEL_DESELECT
eSelectOp ED_select_op_modal(const eSelectOp sel_op, const bool is_first)
Definition: select_utils.c:77
int ED_select_op_action_deselected(const eSelectOp sel_op, const bool is_select, const bool is_inside)
Definition: select_utils.c:53
eSelectOp
@ SEL_OP_SUB
eV3DProjStatus ED_view3d_project_int_global(const struct ARegion *region, const float co[3], int r_co[2], const eV3DProjTest flag)
@ V3D_PROJ_RET_CLIP_WIN
Definition: ED_view3d.h:186
@ V3D_PROJ_RET_CLIP_BB
Definition: ED_view3d.h:184
@ V3D_PROJ_RET_OK
Definition: ED_view3d.h:176
Read Guarded memory(de)allocation.
@ PROP_SKIP_SAVE
Definition: RNA_types.h:204
@ PROP_HIDDEN
Definition: RNA_types.h:202
#define C
Definition: RandGen.cpp:39
#define V2D_IS_CLIPPED
Definition: UI_view2d.h:40
#define NC_GEOM
Definition: WM_types.h:294
@ OPTYPE_UNDO
Definition: WM_types.h:155
@ OPTYPE_REGISTER
Definition: WM_types.h:153
#define ND_SELECT
Definition: WM_types.h:407
#define NC_GPENCIL
Definition: WM_types.h:300
#define NA_SELECTED
Definition: WM_types.h:467
unsigned int U
Definition: btGjkEpa3.h:78
#define SELECT
StackEntry * from
Scene scene
void * user_data
static bool is_inside(int x, int y, int cols, int rows)
Definition: filesel.c:663
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_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 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_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)
bool ED_gpencil_stroke_point_is_inside(bGPDstroke *gps, GP_SpaceConversion *gsc, int mouse[2], const float diff_mat[4][4])
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_can_use(const bContext *C, const bGPDstroke *gps)
bGPdata * ED_gpencil_data_get_active(const bContext *C)
void(* MEM_freeN)(void *vmemh)
Definition: mallocn.c:41
void *(* MEM_callocN)(size_t len, const char *str)
Definition: mallocn.c:45
INLINE Rall1d< T, V, S > pow(const Rall1d< T, V, S > &arg, double m)
Definition: rall1d.h:359
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:6343
void RNA_int_get_array(PointerRNA *ptr, const char *name, int *values)
Definition: rna_access.c:6331
void RNA_boolean_set(PointerRNA *ptr, const char *name, bool value)
Definition: rna_access.c:6272
int RNA_int_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6308
bool RNA_struct_property_is_set(PointerRNA *ptr, const char *identifier)
Definition: rna_access.c:6685
bool RNA_boolean_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6261
int RNA_enum_get(PointerRNA *ptr, const char *name)
Definition: rna_access.c:6402
PropertyRNA * RNA_def_boolean(StructOrFunctionRNA *cont_, const char *identifier, bool default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3481
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:3611
void RNA_def_property_flag(PropertyRNA *prop, PropertyFlag flag)
Definition: rna_define.c:1512
PropertyRNA * RNA_def_int(StructOrFunctionRNA *cont_, const char *identifier, int default_value, int hardmin, int hardmax, const char *ui_name, const char *ui_description, int softmin, int softmax)
Definition: rna_define.c:3585
PropertyRNA * RNA_def_enum(StructOrFunctionRNA *cont_, const char *identifier, const EnumPropertyItem *items, int default_value, const char *ui_name, const char *ui_description)
Definition: rna_define.c:3771
#define min(a, b)
Definition: sort.c:51
float vec[3][3]
const int(* lasso_coords)[2]
struct ARegion * region
void * first
Definition: DNA_listBase.h:47
void * data
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:80
int ymax
Definition: DNA_vec_types.h:80
int xmin
Definition: DNA_vec_types.h:79
int xmax
Definition: DNA_vec_types.h:79
short shift
Definition: WM_types.h:618
int mval[2]
Definition: WM_types.h:583
int(* invoke)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:752
const char * name
Definition: WM_types.h:721
int(* modal)(struct bContext *, struct wmOperator *, const struct wmEvent *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:768
const char * idname
Definition: WM_types.h:723
bool(* poll)(struct bContext *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:776
void(* cancel)(struct bContext *, struct wmOperator *)
Definition: WM_types.h:760
struct StructRNA * srna
Definition: WM_types.h:802
const char * description
Definition: WM_types.h:726
int(* exec)(struct bContext *, struct wmOperator *) ATTR_WARN_UNUSED_RESULT
Definition: WM_types.h:736
PropertyRNA * prop
Definition: WM_types.h:814
struct ReportList * reports
struct PointerRNA * ptr
float max
__forceinline const avxb select(const avxb &m, const avxb &t, const avxb &f)
Definition: util_avxb.h:167
void WM_event_add_notifier(const bContext *C, uint type, void *reference)
wmOperatorType * ot
Definition: wm_files.c:3156
bool WM_gesture_is_modal_first(const wmGesture *gesture)
Definition: wm_gesture.c:124
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)
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_gesture_circle(wmOperatorType *ot)
void WM_operator_properties_select_all(wmOperatorType *ot)
void WM_operator_properties_mouse_select(wmOperatorType *ot)
int WM_menu_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
Definition: wm_operators.c:982