[Bf-blender-cvs] [e4d1fe7] GPencil_EditStrokes: GP Circle Select

Joshua Leung noreply at git.blender.org
Sun Sep 28 17:26:54 CEST 2014


Commit: e4d1fe7c65a9f1f57c255f02c75505c61eb09aa1
Author: Joshua Leung
Date:   Sun Sep 28 03:45:07 2014 +1300
Branches: GPencil_EditStrokes
https://developer.blender.org/rBe4d1fe7c65a9f1f57c255f02c75505c61eb09aa1

GP Circle Select

It's now possible to use Circle/Brush Select (D+C) to select individual Grease Pencil
strokes. This will eventually be useful for editing operations using these.

Implementation Notes:
* This is currently a quick and dirty implementation, which just copies and pastes
  some of the logic from the eraser, and reuses some of the same utility functions
  too.
* The shared methods are now exposed internally for use in the gpencil module.
  Perhaps they should eventually get their own dedicated section/file?

===================================================================

M	source/blender/editors/gpencil/gpencil_intern.h
M	source/blender/editors/gpencil/gpencil_ops.c
M	source/blender/editors/gpencil/gpencil_paint.c
M	source/blender/editors/gpencil/gpencil_select.c
M	source/blender/windowmanager/intern/wm_operators.c

===================================================================

diff --git a/source/blender/editors/gpencil/gpencil_intern.h b/source/blender/editors/gpencil/gpencil_intern.h
index 3a9c812..a4e2f5a 100644
--- a/source/blender/editors/gpencil/gpencil_intern.h
+++ b/source/blender/editors/gpencil/gpencil_intern.h
@@ -33,12 +33,46 @@
 
 /* internal exports only */
 
+struct bGPdata;
+struct bGPDstroke;
+struct bGPDspoint;
+
+struct ARegion;
+struct View2D;
+struct rctf;
+struct wmOperatorType;
+
+
 
 /* ***************************************************** */
-/* Operator Defines */
+/* Internal API */
 
-struct bGPdata;
-struct wmOperatorType;
+/** 
+ * Check whether a given stroke segment is inside a circular brush 
+ *
+ * \param mval     The current screen-space coordinates (midpoint) of the brush
+ * \param mvalo    The previous screen-space coordinates (midpoint) of the brush (NOT CURRENTLY USED)
+ * \param rad      The radius of the brush
+ *
+ * \param x0, y0   The screen-space x and y coordinates of the start of the stroke segment
+ * \param x1, y1   The screen-space x and y coordinates of the end of the stroke segment
+ */
+bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]),
+                             int rad, int x0, int y0, int x1, int y1);
+
+/**
+ * Convert a Grease Pencil coordinate (i.e. can be 2D or 3D) to screenspace (2D)
+ *
+ * \param subrect   For the camera view in the 3D Viewport
+ * \param[out] r_x  The screen-space x-coordinate of the point
+ * \param[out] r_y  The screen-space y-coordinate of the point
+ */
+/* gpencil_paint.c */
+void gp_point_to_xy(struct ARegion *ar, struct View2D *v2d, struct rctf *subrect, struct bGPDstroke *gps, struct bGPDspoint *pt,
+                    int *r_x, int *r_y);
+
+/* ***************************************************** */
+/* Operator Defines */
 
 /* drawing ---------- */
 
@@ -55,6 +89,7 @@ typedef enum eGPencil_PaintModes {
 /* stroke editing ----- */
 
 void GPENCIL_OT_select_all(struct wmOperatorType *ot);
+void GPENCIL_OT_select_circle(struct wmOperatorType *ot);
 
 /* buttons editing --- */
 
diff --git a/source/blender/editors/gpencil/gpencil_ops.c b/source/blender/editors/gpencil/gpencil_ops.c
index d6e009e..ea9e2ba 100644
--- a/source/blender/editors/gpencil/gpencil_ops.c
+++ b/source/blender/editors/gpencil/gpencil_ops.c
@@ -75,9 +75,12 @@ void ED_keymap_gpencil(wmKeyConfig *keyconf)
 	kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_all", AKEY, KM_PRESS, 0, DKEY);
 	RNA_enum_set(kmi->ptr, "action", SEL_TOGGLE);
 	
-	// XXX?
+	// XXX? This is quite awkward to use...
 	kmi = WM_keymap_add_item(keymap, "GPENCIL_OT_select_all", IKEY, KM_PRESS, KM_CTRL, DKEY);
 	RNA_enum_set(kmi->ptr, "action", SEL_INVERT);
+	
+	/* circle select */
+	WM_keymap_add_item(keymap, "GPENCIL_OT_select_circle", CKEY, KM_PRESS, 0, DKEY);
 }
 
 /* ****************************************** */
@@ -91,6 +94,7 @@ void ED_operatortypes_gpencil(void)
 	/* Editing (Strokes) ------------ */
 	
 	WM_operatortype_append(GPENCIL_OT_select_all);
+	WM_operatortype_append(GPENCIL_OT_select_circle);
 	
 	/* Editing (Buttons) ------------ */
 	
diff --git a/source/blender/editors/gpencil/gpencil_paint.c b/source/blender/editors/gpencil/gpencil_paint.c
index afecdd9..b8674b6 100644
--- a/source/blender/editors/gpencil/gpencil_paint.c
+++ b/source/blender/editors/gpencil/gpencil_paint.c
@@ -875,8 +875,8 @@ static bool gp_stroke_eraser_is_occluded(tGPsdata *p,
 }
 
 /* eraser tool - check if part of stroke occurs within last segment drawn by eraser */
-static short gp_stroke_eraser_strokeinside(const int mval[2], const int UNUSED(mvalo[2]),
-                                           int rad, int x0, int y0, int x1, int y1)
+bool gp_stroke_inside_circle(const int mval[2], const int UNUSED(mvalo[2]),
+                             int rad, int x0, int y0, int x1, int y1)
 {
 	/* simple within-radius check for now */
 	const float mval_fl[2]     = {mval[0], mval[1]};
@@ -891,8 +891,8 @@ static short gp_stroke_eraser_strokeinside(const int mval[2], const int UNUSED(m
 	return false;
 } 
 
-static void gp_point_to_xy(ARegion *ar, View2D *v2d, rctf *subrect, bGPDstroke *gps, bGPDspoint *pt,
-                           int *r_x, int *r_y)
+void gp_point_to_xy(ARegion *ar, View2D *v2d, rctf *subrect, bGPDstroke *gps, bGPDspoint *pt,
+                    int *r_x, int *r_y)
 {
 	int xyval[2];
 
@@ -971,7 +971,7 @@ static void gp_stroke_eraser_dostroke(tGPsdata *p,
 				 * eraser region  (either within stroke painted, or on its lines)
 				 *  - this assumes that linewidth is irrelevant
 				 */
-				if (gp_stroke_eraser_strokeinside(mval, mvalo, rad, x0, y0, x1, y1)) {
+				if (gp_stroke_inside_circle(mval, mvalo, rad, x0, y0, x1, y1)) {
 					if ((gp_stroke_eraser_is_occluded(p, pt1, x0, y0) == false) ||
 					    (gp_stroke_eraser_is_occluded(p, pt2, x1, y1) == false))
 					{
diff --git a/source/blender/editors/gpencil/gpencil_select.c b/source/blender/editors/gpencil/gpencil_select.c
index 039d9ca..2b20d12 100644
--- a/source/blender/editors/gpencil/gpencil_select.c
+++ b/source/blender/editors/gpencil/gpencil_select.c
@@ -120,6 +120,7 @@ static int gpencil_select_all_exec(bContext *C, wmOperator *op)
 	}
 	
 	/* select or deselect all strokes */
+	// xxx: refactor this into a proper looper
 	for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
 		if (!(gpl->flag & GP_LAYER_HIDE) && (gpl->actframe)) {
 			bGPDframe *gpf = gpl->actframe;
@@ -165,4 +166,173 @@ void GPENCIL_OT_select_all(wmOperatorType *ot)
 
 /* ********************************************** */
 
+/* Helper to check if a given stroke is within the area */
+/* NOTE: Code here is adapted (i.e. copied directly) from gpencil_paint.c::gp_stroke_eraser_dostroke()
+ *       It would be great to de-duplicate the logic here sometime, but that can wait...
+ */
+static bool gp_stroke_do_circle_sel(bGPDstroke *gps, ARegion *ar, View2D *v2d, rctf *subrect,
+                                    const int mx, const int my, const int radius, 
+                                    const bool select, rcti *rect)
+{
+	bGPDspoint *pt1, *pt2;
+	int x0 = 0, y0 = 0, x1 = 0, y1 = 0;
+	int i;
+	
+	if (gps->totpoints == 1) {
+		gp_point_to_xy(ar, v2d, subrect, gps, gps->points, &x0, &y0);
+		
+		/* do boundbox check first */
+		if ((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) {
+			/* only check if point is inside */
+			if (((x0 - mx) * (x0 - mx) + (y0 - my) * (y0 - my)) <= radius * radius) {
+				/* change selection */
+				if (select)
+					gps->flag |= GP_STROKE_SELECT;
+				else
+					gps->flag &= ~GP_STROKE_SELECT;
+				
+				return true;
+			}
+		}
+	}
+	else {
+		/* loop over the points in the stroke, checking for intersections 
+		 *  - an intersection means that we touched the stroke
+		 */
+		for (i = 0; (i + 1) < gps->totpoints; i++) {
+			/* get points to work with */
+			pt1 = gps->points + i;
+			pt2 = gps->points + i + 1;
+			
+			gp_point_to_xy(ar, v2d, subrect, gps, pt1, &x0, &y0);
+			gp_point_to_xy(ar, v2d, subrect, gps, pt2, &x1, &y1);
+			
+			/* check that point segment of the boundbox of the eraser stroke */
+			if (((!ELEM(V2D_IS_CLIPPED, x0, y0)) && BLI_rcti_isect_pt(rect, x0, y0)) ||
+			    ((!ELEM(V2D_IS_CLIPPED, x1, y1)) && BLI_rcti_isect_pt(rect, x1, y1)))
+			{
+				int mval[2]  = {mx, my};
+				int mvalo[2] = {mx, my}; /* dummy - this isn't used... */
+				
+				/* check if point segment of stroke had anything to do with
+				 * eraser region  (either within stroke painted, or on its lines)
+				 *  - this assumes that linewidth is irrelevant
+				 */
+				if (gp_stroke_inside_circle(mval, mvalo, radius, x0, y0, x1, y1)) {
+					/* change selection */
+					if (select)
+						gps->flag |= GP_STROKE_SELECT;
+					else
+						gps->flag &= ~GP_STROKE_SELECT;
+						
+					/* we only need to change the selection once... when we detect a hit! */
+					return true;
+				}
+			}
+		}
+	}
+	
+	return false;
+}
+
+
+static int gpencil_circle_select_exec(bContext *C, wmOperator *op)
+{
+	ScrArea *sa = CTX_wm_area(C);
+	ARegion *ar = CTX_wm_region(C);
+	
+	Scene *scene = CTX_data_scene(C);
+	bGPdata *gpd = ED_gpencil_data_get_active(C);
+	bGPDlayer *gpl;
+	
+	const int mx = RNA_int_get(op->ptr, "x");
+	const int my = RNA_int_get(op->ptr, "y");
+	const int radius = RNA_int_get(op->ptr, "radius");
+	
+	const int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
+	const bool select = (gesture_mode == GESTURE_MODAL_SELECT);
+	
+	rctf *subrect = NULL;       /* for using the camera rect within the 3d view */
+	rctf subrect_data = {0.0f};
+	rcti rect = {0};            /* for bounding rect around circle (for quicky intersection testing) */
+	
+	bool changed = false;
+	
+	/* for 3D View, init depth buffer stuff used for 3D projections... */
+	if (sa == NULL) {
+		BKE_report(op->reports, RPT_ERROR, "No active area");
+		return OPERATOR_CANCELLED;
+	}
+	
+	if (sa->spacetype == SPACE_VIEW3D) {
+		wmWindow *win = CTX_wm_window(C);
+		View3D *v3d = (View3D *)CTX_wm_space_data(C);
+		RegionView3D *rv3d = ar->regiondata;
+		
+		/* init 3d depth buffers */
+		view3d_operator_needs_opengl(C);
+		view3d_region_operator_needs_opengl(win, ar);
+		ED_view3d_autodist_init(scene, ar, v3d, 0);
+		
+		/* for camera view set the subrect */
+		if (rv3d->persp == RV3D_CAMOB) {
+			ED_view3d_calc_camera_border(scene, ar, v3d, rv3d, &subrect_data, true); /* no shift */
+			subrect = &subrect_data;
+		}
+	}
+	
+	/* rect is rectangle of selection circle */
+	rect.xmin = mx - radius;
+	rect.ymin = my - radius;
+	rect.xmax = mx + radius;
+	rect.ymax = my + radius;
+	
+	
+	
+	/* find visible strokes, and select if hit */
+	// XXX: replace this looper with a proper method
+	for (gpl = gpd->layers.first; gpl; gpl = gpl->next) {
+		if (!(gpl->flag & GP_LAYER_HIDE) && (gpl->actframe)) {
+			bGPDframe *gpf = gpl->actframe;
+			bGPDstroke *gps;
+			
+			for (gps = gpf->strokes.first; gps; gps = gps->next) {
+				changed |= gp_stroke_do_circle_sel(gps, ar, &ar->v2d, subrect, 
+				                                   mx, my, radius, select, &rect);
+			}
+		}
+	}
+	
+	/* updates */
+	//if (changed)
+	WM_event_add_notifier(C, NC_GPENCIL | NA_SELECTED, NULL);
+	return OPERATOR_FINISHED;
+}
+
+void GPENCIL_OT_select_cir

@@ Diff output truncated at 10240 characters. @@




More information about the Bf-blender-cvs mailing list