[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