2 * ***** BEGIN GPL LICENSE BLOCK *****
4 * This program is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU General Public License
6 * as published by the Free Software Foundation; either version 2
7 * of the License, or (at your option) any later version.
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 * GNU General Public License for more details.
14 * You should have received a copy of the GNU General Public License
15 * along with this program; if not, write to the Free Software Foundation,
16 * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
18 * The Original Code is Copyright (C) 2008 Blender Foundation.
19 * All rights reserved.
22 * Contributor(s): Blender Foundation
24 * ***** END GPL LICENSE BLOCK *****
27 /** \file blender/editors/space_view3d/view3d_select.c
38 #include "DNA_action_types.h"
39 #include "DNA_armature_types.h"
40 #include "DNA_curve_types.h"
41 #include "DNA_meta_types.h"
42 #include "DNA_mesh_types.h"
43 #include "DNA_meshdata_types.h"
44 #include "DNA_object_types.h"
45 #include "DNA_scene_types.h"
46 #include "DNA_tracking_types.h"
48 #include "MEM_guardedalloc.h"
51 #include "BLI_lasso.h"
54 #include "BLI_linklist.h"
55 #include "BLI_listbase.h"
56 #include "BLI_string.h"
57 #include "BLI_utildefines.h"
59 /* vertex box select */
60 #include "IMB_imbuf_types.h"
61 #include "IMB_imbuf.h"
62 #include "BKE_global.h"
64 #include "BKE_context.h"
65 #include "BKE_paint.h"
66 #include "BKE_armature.h"
67 #include "BKE_tessmesh.h"
68 #include "BKE_movieclip.h"
69 #include "BKE_object.h"
70 #include "BKE_tracking.h"
74 #include "BIF_glutil.h"
79 #include "RNA_access.h"
80 #include "RNA_define.h"
81 #include "RNA_enum_types.h"
83 #include "ED_armature.h"
85 #include "ED_particle.h"
87 #include "ED_object.h"
88 #include "ED_screen.h"
91 #include "UI_interface.h"
92 #include "UI_resources.h"
94 #include "view3d_intern.h" // own include
96 // TODO: should return whether there is valid context to continue
97 void view3d_set_viewcontext(bContext *C, ViewContext *vc)
99 memset(vc, 0, sizeof(ViewContext));
100 vc->ar = CTX_wm_region(C);
101 vc->scene = CTX_data_scene(C);
102 vc->v3d = CTX_wm_view3d(C);
103 vc->rv3d = CTX_wm_region_view3d(C);
104 vc->obact = CTX_data_active_object(C);
105 vc->obedit = CTX_data_edit_object(C);
108 int view3d_get_view_aligned_coordinate(ViewContext *vc, float fp[3], const int mval[2], const short do_fallback)
113 mval_cpy[0] = mval[0];
114 mval_cpy[1] = mval[1];
116 project_int_noclip(vc->ar, fp, mval_cpy);
118 initgrabz(vc->rv3d, fp[0], fp[1], fp[2]);
120 if (mval_cpy[0] != IS_CLIPPED) {
122 VECSUB2D(mval_f, mval_cpy, mval);
123 ED_view3d_win_to_delta(vc->ar, mval_f, dvec);
129 /* fallback to the view center */
131 negate_v3_v3(fp, vc->rv3d->ofs);
132 return view3d_get_view_aligned_coordinate(vc, fp, mval, FALSE);
141 * ob == NULL if you want global matrices
143 void view3d_get_transformation(const ARegion *ar, RegionView3D *rv3d, Object *ob, bglMats *mats)
149 mult_m4_m4m4(cpy, rv3d->viewmat, ob->obmat);
152 copy_m4_m4(cpy, rv3d->viewmat);
155 for (i = 0; i < 4; ++i) {
156 for (j = 0; j < 4; ++j) {
157 mats->projection[i * 4 + j] = rv3d->winmat[i][j];
158 mats->modelview[i * 4 + j] = cpy[i][j];
162 mats->viewport[0] = ar->winrct.xmin;
163 mats->viewport[1] = ar->winrct.ymin;
164 mats->viewport[2] = ar->winx;
165 mats->viewport[3] = ar->winy;
168 /* ********************** view3d_select: selection manipulations ********************* */
170 /* local prototypes */
172 static void edbm_backbuf_check_and_select_verts(BMEditMesh *em, int select)
176 int index = bm_wireoffs;
178 eve = BM_iter_new(&iter, em->bm, BM_VERTS_OF_MESH, NULL);
179 for (; eve; eve = BM_iter_step(&iter), index++) {
180 if (!BM_elem_flag_test(eve, BM_ELEM_HIDDEN)) {
181 if (EDBM_backbuf_check(index)) {
182 BM_vert_select_set(em->bm, eve, select);
188 static void edbm_backbuf_check_and_select_edges(BMEditMesh *em, int select)
192 int index = bm_solidoffs;
194 eed = BM_iter_new(&iter, em->bm, BM_EDGES_OF_MESH, NULL);
195 for (; eed; eed = BM_iter_step(&iter), index++) {
196 if (!BM_elem_flag_test(eed, BM_ELEM_HIDDEN)) {
197 if (EDBM_backbuf_check(index)) {
198 BM_edge_select_set(em->bm, eed, select);
204 static void edbm_backbuf_check_and_select_faces(BMEditMesh *em, int select)
210 efa = BM_iter_new(&iter, em->bm, BM_FACES_OF_MESH, NULL);
211 for (; efa; efa = BM_iter_step(&iter), index++) {
212 if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) {
213 if (EDBM_backbuf_check(index)) {
214 BM_face_select_set(em->bm, efa, select);
221 /* object mode, EM_ prefix is confusing here, rename? */
222 static void edbm_backbuf_check_and_select_verts_obmode(Mesh *me, int select)
224 MVert *mv = me->mvert;
228 for (a = 1; a <= me->totvert; a++, mv++) {
229 if (EDBM_backbuf_check(a)) {
230 if (!(mv->flag & ME_HIDE)) {
231 mv->flag = select ? (mv->flag | SELECT) : (mv->flag & ~SELECT);
237 /* object mode, EM_ prefix is confusing here, rename? */
239 static void edbm_backbuf_check_and_select_tfaces(Mesh *me, int select)
241 MPoly *mpoly = me->mpoly;
245 for (a = 1; a <= me->totpoly; a++, mpoly++) {
246 if (EDBM_backbuf_check(a)) {
247 mpoly->flag = select ? (mpoly->flag | ME_FACE_SEL) : (mpoly->flag & ~ME_FACE_SEL);
253 /* *********************** GESTURE AND LASSO ******************* */
255 typedef struct LassoSelectUserData {
258 int (*mcords)[2], moves, select, pass, done;
259 } LassoSelectUserData;
261 static int view3d_selectable_data(bContext *C)
263 Object *ob = CTX_data_active_object(C);
265 if (!ED_operator_region_view3d_active(C))
269 if (ob->mode & OB_MODE_EDIT) {
270 if (ob->type == OB_FONT) {
275 if (ob->mode & OB_MODE_SCULPT) {
278 if ((ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT)) &&
279 !paint_facesel_test(ob) && !paint_vertsel_test(ob))
290 /* helper also for borderselect */
291 static int edge_fully_inside_rect(rcti *rect, short x1, short y1, short x2, short y2)
293 return BLI_in_rcti(rect, x1, y1) && BLI_in_rcti(rect, x2, y2);
296 static int edge_inside_rect(rcti *rect, short x1, short y1, short x2, short y2)
300 /* check points in rect */
301 if (edge_fully_inside_rect(rect, x1, y1, x2, y2)) return 1;
303 /* check points completely out rect */
304 if (x1 < rect->xmin && x2 < rect->xmin) return 0;
305 if (x1 > rect->xmax && x2 > rect->xmax) return 0;
306 if (y1 < rect->ymin && y2 < rect->ymin) return 0;
307 if (y1 > rect->ymax && y2 > rect->ymax) return 0;
309 /* simple check lines intersecting. */
310 d1 = (y1 - y2) * (x1 - rect->xmin) + (x2 - x1) * (y1 - rect->ymin);
311 d2 = (y1 - y2) * (x1 - rect->xmin) + (x2 - x1) * (y1 - rect->ymax);
312 d3 = (y1 - y2) * (x1 - rect->xmax) + (x2 - x1) * (y1 - rect->ymax);
313 d4 = (y1 - y2) * (x1 - rect->xmax) + (x2 - x1) * (y1 - rect->ymin);
315 if (d1 < 0 && d2 < 0 && d3 < 0 && d4 < 0) return 0;
316 if (d1 > 0 && d2 > 0 && d3 > 0 && d4 > 0) return 0;
322 #define MOVES_GESTURE 50
323 #define MOVES_LASSO 500
326 /* warning; lasso select with backbuffer-check draws in backbuf with persp(PERSP_WIN)
327 * and returns with persp(PERSP_VIEW). After lasso select backbuf is not OK
329 static void do_lasso_select_pose(ViewContext *vc, Object *ob, int mcords[][2], short moves, short select)
333 int sco1[2], sco2[2];
334 bArmature *arm = ob->data;
336 if (ob->type != OB_ARMATURE || ob->pose == NULL) return;
338 for (pchan = ob->pose->chanbase.first; pchan; pchan = pchan->next) {
339 if (PBONE_VISIBLE(arm, pchan->bone) && (pchan->bone->flag & BONE_UNSELECTABLE) == 0) {
340 mul_v3_m4v3(vec, ob->obmat, pchan->pose_head);
341 project_int(vc->ar, vec, sco1);
342 mul_v3_m4v3(vec, ob->obmat, pchan->pose_tail);
343 project_int(vc->ar, vec, sco2);
345 if (BLI_lasso_is_edge_inside(mcords, moves, sco1[0], sco1[1], sco2[0], sco2[1], IS_CLIPPED)) {
346 if (select) pchan->bone->flag |= BONE_SELECTED;
347 else pchan->bone->flag &= ~BONE_SELECTED;
353 static void object_deselect_all_visible(Scene *scene, View3D *v3d)
357 for (base = scene->base.first; base; base = base->next) {
358 if (BASE_SELECTABLE(v3d, base)) {
359 ED_base_object_select(base, BA_DESELECT);
364 static void do_lasso_select_objects(ViewContext *vc, int mcords[][2], short moves, short extend, short select)
368 if (extend == 0 && select)
369 object_deselect_all_visible(vc->scene, vc->v3d);
371 for (base = vc->scene->base.first; base; base = base->next) {
372 if (BASE_SELECTABLE(vc->v3d, base)) { /* use this to avoid un-needed lasso lookups */
373 project_short(vc->ar, base->object->obmat[3], &base->sx);
374 if (BLI_lasso_is_point_inside(mcords, moves, base->sx, base->sy, IS_CLIPPED)) {
376 if (select) ED_base_object_select(base, BA_SELECT);
377 else ED_base_object_select(base, BA_DESELECT);
378 base->object->flag = base->flag;
380 if (base->object->mode & OB_MODE_POSE) {
381 do_lasso_select_pose(vc, base->object, mcords, moves, select);
387 static void do_lasso_select_mesh__doSelectVert(void *userData, BMVert *eve, int x, int y, int UNUSED(index))
389 LassoSelectUserData *data = userData;
391 if (BLI_in_rcti(data->rect, x, y) &&
392 BLI_lasso_is_point_inside(data->mcords, data->moves, x, y, IS_CLIPPED))
394 BM_vert_select_set(data->vc->em->bm, eve, data->select);
397 static void do_lasso_select_mesh__doSelectEdge(void *userData, BMEdge *eed, int x0, int y0, int x1, int y1, int index)
399 LassoSelectUserData *data = userData;
401 if (EDBM_backbuf_check(bm_solidoffs + index)) {
402 if (data->pass == 0) {
403 if (edge_fully_inside_rect(data->rect, x0, y0, x1, y1) &&
404 BLI_lasso_is_point_inside(data->mcords, data->moves, x0, y0, IS_CLIPPED) &&
405 BLI_lasso_is_point_inside(data->mcords, data->moves, x1, y1, IS_CLIPPED))
407 BM_edge_select_set(data->vc->em->bm, eed, data->select);
412 if (BLI_lasso_is_edge_inside(data->mcords, data->moves, x0, y0, x1, y1, IS_CLIPPED)) {
413 BM_edge_select_set(data->vc->em->bm, eed, data->select);
418 static void do_lasso_select_mesh__doSelectFace(void *userData, BMFace *efa, int x, int y, int UNUSED(index))
420 LassoSelectUserData *data = userData;
422 if (BLI_in_rcti(data->rect, x, y) &&
423 BLI_lasso_is_point_inside(data->mcords, data->moves, x, y, IS_CLIPPED))
425 BM_face_select_set(data->vc->em->bm, efa, data->select);
429 static void do_lasso_select_mesh(ViewContext *vc, int mcords[][2], short moves, short extend, short select)
431 LassoSelectUserData data;
432 ToolSettings *ts = vc->scene->toolsettings;
436 BLI_lasso_boundbox(&rect, mcords, moves);
439 vc->em = BMEdit_FromObject(vc->obedit);
443 data.mcords = mcords;
445 data.select = select;
449 if (extend == 0 && select)
450 EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT);
452 /* for non zbuf projections, don't change the GL state */
453 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
455 glLoadMatrixf(vc->rv3d->viewmat);
456 bbsel = EDBM_backbuf_border_mask_init(vc, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
458 if (ts->selectmode & SCE_SELECT_VERTEX) {
460 edbm_backbuf_check_and_select_verts(vc->em, select);
463 mesh_foreachScreenVert(vc, do_lasso_select_mesh__doSelectVert, &data, V3D_CLIP_TEST_RV3D_CLIPPING);
466 if (ts->selectmode & SCE_SELECT_EDGE) {
467 /* Does both bbsel and non-bbsel versions (need screen cos for both) */
469 mesh_foreachScreenEdge(vc, do_lasso_select_mesh__doSelectEdge, &data, V3D_CLIP_TEST_OFF);
471 if (data.done == 0) {
473 mesh_foreachScreenEdge(vc, do_lasso_select_mesh__doSelectEdge, &data, V3D_CLIP_TEST_OFF);
477 if (ts->selectmode & SCE_SELECT_FACE) {
479 edbm_backbuf_check_and_select_faces(vc->em, select);
482 mesh_foreachScreenFace(vc, do_lasso_select_mesh__doSelectFace, &data);
487 EDBM_selectmode_flush(vc->em);
490 static void do_lasso_select_curve__doSelect(void *userData, Nurb *UNUSED(nu), BPoint *bp, BezTriple *bezt, int beztindex, int x, int y)
492 LassoSelectUserData *data = userData;
493 Object *obedit = data->vc->obedit;
494 Curve *cu = (Curve *)obedit->data;
496 if (BLI_lasso_is_point_inside(data->mcords, data->moves, x, y, IS_CLIPPED)) {
498 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
499 if (bp == cu->lastsel && !(bp->f1 & 1)) cu->lastsel = NULL;
502 if (cu->drawflag & CU_HIDE_HANDLES) {
503 /* can only be beztindex==0 here since handles are hidden */
504 bezt->f1 = bezt->f2 = bezt->f3 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT);
507 if (beztindex == 0) {
508 bezt->f1 = data->select ? (bezt->f1 | SELECT) : (bezt->f1 & ~SELECT);
510 else if (beztindex == 1) {
511 bezt->f2 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT);
514 bezt->f3 = data->select ? (bezt->f3 | SELECT) : (bezt->f3 & ~SELECT);
518 if (bezt == cu->lastsel && !(bezt->f2 & 1)) cu->lastsel = NULL;
523 static void do_lasso_select_curve(ViewContext *vc, int mcords[][2], short moves, short extend, short select)
525 LassoSelectUserData data;
527 /* set vc->editnurb */
529 data.mcords = mcords;
531 data.select = select;
533 if (extend == 0 && select)
534 CU_deselect_all(vc->obedit);
536 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
537 nurbs_foreachScreenVert(vc, do_lasso_select_curve__doSelect, &data);
540 static void do_lasso_select_lattice__doSelect(void *userData, BPoint *bp, int x, int y)
542 LassoSelectUserData *data = userData;
544 if (BLI_lasso_is_point_inside(data->mcords, data->moves, x, y, IS_CLIPPED)) {
545 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
548 static void do_lasso_select_lattice(ViewContext *vc, int mcords[][2], short moves, short extend, short select)
550 LassoSelectUserData data;
552 /* set editdata in vc */
553 data.mcords = mcords;
555 data.select = select;
557 if (extend == 0 && select)
558 ED_setflagsLatt(vc->obedit, 0);
560 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
561 lattice_foreachScreenVert(vc, do_lasso_select_lattice__doSelect, &data);
564 static void do_lasso_select_armature(ViewContext *vc, int mcords[][2], short moves, short extend, short select)
566 bArmature *arm = vc->obedit->data;
569 short sco1[2], sco2[2], didpoint;
572 if (extend == 0 && select)
573 ED_armature_deselect_all_visible(vc->obedit);
575 /* set editdata in vc */
577 for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
578 if (EBONE_VISIBLE(arm, ebone) && (ebone->flag & BONE_UNSELECTABLE) == 0) {
579 mul_v3_m4v3(vec, vc->obedit->obmat, ebone->head);
580 project_short(vc->ar, vec, sco1);
581 mul_v3_m4v3(vec, vc->obedit->obmat, ebone->tail);
582 project_short(vc->ar, vec, sco2);
585 if (BLI_lasso_is_point_inside(mcords, moves, sco1[0], sco1[1], IS_CLIPPED)) {
586 if (select) ebone->flag |= BONE_ROOTSEL;
587 else ebone->flag &= ~BONE_ROOTSEL;
591 if (BLI_lasso_is_point_inside(mcords, moves, sco2[0], sco2[1], IS_CLIPPED)) {
592 if (select) ebone->flag |= BONE_TIPSEL;
593 else ebone->flag &= ~BONE_TIPSEL;
597 /* if one of points selected, we skip the bone itself */
599 BLI_lasso_is_edge_inside(mcords, moves, sco1[0], sco1[1], sco2[0], sco2[1], IS_CLIPPED))
601 if (select) ebone->flag |= BONE_TIPSEL | BONE_ROOTSEL | BONE_SELECTED;
602 else ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
609 ED_armature_sync_selection(arm->edbo);
610 ED_armature_validate_active(arm);
611 WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, vc->obedit);
618 static void do_lasso_select_meta(ViewContext *vc, int mcords[][2], short moves, short extend, short select)
620 MetaBall *mb = (MetaBall *)vc->obedit->data;
625 if (extend == 0 && select) {
626 for (ml = mb->editelems->first; ml; ml = ml->next) {
631 for (ml = mb->editelems->first; ml; ml = ml->next) {
633 mul_v3_m4v3(vec, vc->obedit->obmat, &ml->x);
634 project_short(vc->ar, vec, sco);
636 if (BLI_lasso_is_point_inside(mcords, moves, sco[0], sco[1], IS_CLIPPED)) {
637 if (select) ml->flag |= SELECT;
638 else ml->flag &= ~SELECT;
643 int do_paintvert_box_select(ViewContext *vc, rcti *rect, int select, int extend)
651 int sx = rect->xmax - rect->xmin + 1;
652 int sy = rect->ymax - rect->ymin + 1;
654 me = vc->obact->data;
656 if (me == NULL || me->totvert == 0 || sx * sy <= 0)
657 return OPERATOR_CANCELLED;
659 selar = MEM_callocN(me->totvert + 1, "selar");
661 if (extend == 0 && select)
662 paintvert_deselect_all_visible(vc->obact, SEL_DESELECT, FALSE);
664 view3d_validate_backbuf(vc);
666 ibuf = IMB_allocImBuf(sx, sy, 32, IB_rect);
668 glReadPixels(rect->xmin + vc->ar->winrct.xmin, rect->ymin + vc->ar->winrct.ymin, sx, sy, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect);
669 if (ENDIAN_ORDER == B_ENDIAN) IMB_convert_rgba_to_abgr(ibuf);
674 index = WM_framebuffer_to_index(*rt);
675 if (index <= me->totvert) selar[index] = 1;
681 for (a = 1; a <= me->totvert; a++, mvert++) {
683 if ((mvert->flag & ME_HIDE) == 0) {
684 if (select) mvert->flag |= SELECT;
685 else mvert->flag &= ~SELECT;
694 glReadBuffer(GL_BACK);
697 paintvert_flush_flags(vc->obact);
699 return OPERATOR_FINISHED;
702 static void do_lasso_select_paintvert(ViewContext *vc, int mcords[][2], short moves, short extend, short select)
704 Object *ob = vc->obact;
705 Mesh *me = ob ? ob->data : NULL;
708 if (me == NULL || me->totvert == 0)
711 if (extend == 0 && select)
712 paintvert_deselect_all_visible(ob, SEL_DESELECT, FALSE); /* flush selection at the end */
713 bm_vertoffs = me->totvert + 1; /* max index array */
715 BLI_lasso_boundbox(&rect, mcords, moves);
716 EDBM_backbuf_border_mask_init(vc, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
718 edbm_backbuf_check_and_select_verts_obmode(me, select);
722 paintvert_flush_flags(ob);
724 static void do_lasso_select_paintface(ViewContext *vc, int mcords[][2], short moves, short extend, short select)
726 Object *ob = vc->obact;
727 Mesh *me = ob ? ob->data : NULL;
730 if (me == NULL || me->totpoly == 0)
733 if (extend == 0 && select)
734 paintface_deselect_all_visible(ob, SEL_DESELECT, FALSE); /* flush selection at the end */
736 bm_vertoffs = me->totpoly + 1; /* max index array */
738 BLI_lasso_boundbox(&rect, mcords, moves);
739 EDBM_backbuf_border_mask_init(vc, mcords, moves, rect.xmin, rect.ymin, rect.xmax, rect.ymax);
741 edbm_backbuf_check_and_select_tfaces(me, select);
745 paintface_flush_flags(ob);
749 static void do_lasso_select_node(int mcords[][2], short moves, short select)
751 SpaceNode *snode = sa->spacedata.first;
758 BLI_lasso_boundbox(&rect, mcords, moves);
760 /* store selection in temp test flag */
761 for (node = snode->edittree->nodes.first; node; node = node->next) {
763 node_centf[0] = (node->totr.xmin + node->totr.xmax) / 2;
764 node_centf[1] = (node->totr.ymin + node->totr.ymax) / 2;
766 ipoco_to_areaco_noclip(G.v2d, node_centf, node_cent);
767 if (BLI_in_rcti(&rect, node_cent[0], node_cent[1]) && BLI_lasso_is_point_inside(mcords, moves, node_cent[0], node_cent[1])) {
769 node->flag |= SELECT;
772 node->flag &= ~SELECT;
776 BIF_undo_push("Lasso select nodes");
780 static void view3d_lasso_select(bContext *C, ViewContext *vc, int mcords[][2], short moves, short extend, short select)
782 Object *ob = CTX_data_active_object(C);
784 if (vc->obedit == NULL) { /* Object Mode */
785 if (paint_facesel_test(ob))
786 do_lasso_select_paintface(vc, mcords, moves, extend, select);
787 else if (paint_vertsel_test(ob))
788 do_lasso_select_paintvert(vc, mcords, moves, extend, select);
789 else if (ob && ob->mode & (OB_MODE_VERTEX_PAINT | OB_MODE_WEIGHT_PAINT | OB_MODE_TEXTURE_PAINT))
791 else if (ob && ob->mode & OB_MODE_PARTICLE_EDIT)
792 PE_lasso_select(C, mcords, moves, extend, select);
794 do_lasso_select_objects(vc, mcords, moves, extend, select);
795 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
798 else { /* Edit Mode */
799 switch (vc->obedit->type) {
801 do_lasso_select_mesh(vc, mcords, moves, extend, select);
805 do_lasso_select_curve(vc, mcords, moves, extend, select);
808 do_lasso_select_lattice(vc, mcords, moves, extend, select);
811 do_lasso_select_armature(vc, mcords, moves, extend, select);
814 do_lasso_select_meta(vc, mcords, moves, extend, select);
817 assert(!"lasso select on incorrect object type");
820 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc->obedit->data);
825 /* lasso operator gives properties, but since old code works
826 * with short array we convert */
827 static int view3d_lasso_select_exec(bContext *C, wmOperator *op)
833 RNA_BEGIN (op->ptr, itemptr, "path") {
836 RNA_float_get_array(&itemptr, "loc", loc);
837 mcords[i][0] = (int)loc[0];
838 mcords[i][1] = (int)loc[1];
840 if (i >= 1024) break;
845 short extend, select;
846 view3d_operator_needs_opengl(C);
848 /* setup view context for argument to callbacks */
849 view3d_set_viewcontext(C, &vc);
851 extend = RNA_boolean_get(op->ptr, "extend");
852 select = !RNA_boolean_get(op->ptr, "deselect");
853 view3d_lasso_select(C, &vc, mcords, i, extend, select);
855 return OPERATOR_FINISHED;
857 return OPERATOR_PASS_THROUGH;
860 void VIEW3D_OT_select_lasso(wmOperatorType *ot)
862 ot->name = "Lasso Select";
863 ot->description = "Select items using lasso selection";
864 ot->idname = "VIEW3D_OT_select_lasso";
866 ot->invoke = WM_gesture_lasso_invoke;
867 ot->modal = WM_gesture_lasso_modal;
868 ot->exec = view3d_lasso_select_exec;
869 ot->poll = view3d_selectable_data;
870 ot->cancel = WM_gesture_lasso_cancel;
873 ot->flag = OPTYPE_UNDO;
875 RNA_def_collection_runtime(ot->srna, "path", &RNA_OperatorMousePath, "Path", "");
876 RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect rather than select items");
877 RNA_def_boolean(ot->srna, "extend", 1, "Extend", "Extend selection instead of deselecting everything first");
881 /* ************************************************* */
884 /* smart function to sample a rect spiralling outside, nice for backbuf selection */
885 static unsigned int samplerect(unsigned int *buf, int size, unsigned int dontdo)
888 unsigned int *bufmin, *bufmax;
889 int a, b, rc, tel, len, dirvec[4][2], maxob;
890 unsigned int retval = 0;
893 if (base == 0) return 0;
894 maxob = base->selcol;
896 len = (size - 1) / 2;
902 dirvec[1][1] = -size;
909 bufmax = buf + size * size;
910 buf += len * size + len;
912 for (tel = 1; tel <= size; tel++) {
914 for (a = 0; a < 2; a++) {
915 for (b = 0; b < tel; b++) {
917 if (*buf && *buf <= maxob && *buf != dontdo) return *buf;
918 if (*buf == dontdo) retval = dontdo; /* if only color dontdo is available, still return dontdo */
920 buf += (dirvec[rc][0] + dirvec[rc][1]);
922 if (buf < bufmin || buf >= bufmax) return retval;
932 /* ************************** mouse select ************************* */
935 /* The max number of menu items in an object select menu */
936 typedef struct SelMenuItemF {
937 char idname[MAX_ID_NAME - 2];
941 #define SEL_MENU_SIZE 22
942 static SelMenuItemF object_mouse_select_menu_data[SEL_MENU_SIZE];
944 /* special (crappy) operator only for menu select */
945 static EnumPropertyItem *object_select_menu_enum_itemf(bContext *C, PointerRNA *UNUSED(ptr), PropertyRNA *UNUSED(prop), int *free)
947 EnumPropertyItem *item = NULL, item_tmp = {0};
951 /* don't need context but avoid docgen using this */
952 if (C == NULL || object_mouse_select_menu_data[i].idname[0] == '\0') {
953 return DummyRNA_NULL_items;
956 for (; i < SEL_MENU_SIZE && object_mouse_select_menu_data[i].idname[0] != '\0'; i++) {
957 item_tmp.name = object_mouse_select_menu_data[i].idname;
958 item_tmp.identifier = object_mouse_select_menu_data[i].idname;
960 item_tmp.icon = object_mouse_select_menu_data[i].icon;
961 RNA_enum_item_add(&item, &totitem, &item_tmp);
964 RNA_enum_item_end(&item, &totitem);
970 static int object_select_menu_exec(bContext *C, wmOperator *op)
972 int name_index = RNA_enum_get(op->ptr, "name");
973 short extend = RNA_boolean_get(op->ptr, "extend");
975 const char *name = object_mouse_select_menu_data[name_index].idname;
978 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
979 if (base->flag & SELECT) {
980 ED_base_object_select(base, BA_DESELECT);
987 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
988 /* this is a bit dodjy, there should only be ONE object with this name, but library objects can mess this up */
989 if (strcmp(name, base->object->id.name + 2) == 0) {
990 ED_base_object_activate(C, base);
991 ED_base_object_select(base, BA_SELECT);
997 /* weak but ensures we activate menu again before using the enum */
998 memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
1002 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, CTX_data_scene(C));
1003 return OPERATOR_FINISHED;
1006 return OPERATOR_CANCELLED;
1010 void VIEW3D_OT_select_menu(wmOperatorType *ot)
1015 ot->name = "Select Menu";
1016 ot->description = "Menu object selection";
1017 ot->idname = "VIEW3D_OT_select_menu";
1020 ot->invoke = WM_menu_invoke;
1021 ot->exec = object_select_menu_exec;
1024 ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
1026 /* keyingset to use (dynamic enum) */
1027 prop = RNA_def_enum(ot->srna, "name", DummyRNA_NULL_items, 0, "Object Name", "");
1028 RNA_def_enum_funcs(prop, object_select_menu_enum_itemf);
1029 RNA_def_property_flag(prop, PROP_HIDDEN);
1032 RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend selection instead of deselecting everything first");
1035 static void deselectall_except(Scene *scene, Base *b) /* deselect all except b */
1039 for (base = FIRSTBASE; base; base = base->next) {
1040 if (base->flag & SELECT) {
1042 ED_base_object_select(base, BA_DESELECT);
1048 static Base *object_mouse_select_menu(bContext *C, ViewContext *vc, unsigned int *buffer, int hits, const int mval[2], short extend)
1050 short baseCount = 0;
1052 LinkNode *linklist = NULL;
1054 CTX_DATA_BEGIN (C, Base *, base, selectable_bases) {
1057 /* two selection methods, the CTRL select uses max dist of 15 */
1060 for (a = 0; a < hits; a++) {
1061 /* index was converted */
1062 if (base->selcol == buffer[(4 * a) + 3])
1067 int temp, dist = 15;
1069 project_short(vc->ar, base->object->obmat[3], &base->sx);
1071 temp = abs(base->sx - mval[0]) + abs(base->sy - mval[1]);
1078 BLI_linklist_prepend(&linklist, base);
1080 if (baseCount == SEL_MENU_SIZE)
1086 if (baseCount == 0) {
1089 if (baseCount == 1) {
1090 Base *base = (Base *)linklist->link;
1091 BLI_linklist_free(linklist, NULL);
1095 /* UI, full in static array values that we later use in an enum function */
1099 memset(object_mouse_select_menu_data, 0, sizeof(object_mouse_select_menu_data));
1101 for (node = linklist, i = 0; node; node = node->next, i++) {
1102 Base *base = node->link;
1103 Object *ob = base->object;
1104 char *name = ob->id.name + 2;
1106 BLI_strncpy(object_mouse_select_menu_data[i].idname, name, MAX_ID_NAME - 2);
1107 object_mouse_select_menu_data[i].icon = uiIconFromID(&ob->id);
1113 WM_operator_properties_create(&ptr, "VIEW3D_OT_select_menu");
1114 RNA_boolean_set(&ptr, "extend", extend);
1115 WM_operator_name_call(C, "VIEW3D_OT_select_menu", WM_OP_INVOKE_DEFAULT, &ptr);
1116 WM_operator_properties_free(&ptr);
1119 BLI_linklist_free(linklist, NULL);
1124 /* we want a select buffer with bones, if there are... */
1125 /* so check three selection levels and compare */
1126 static short mixed_bones_object_selectbuffer(ViewContext *vc, unsigned int *buffer, const int mval[2])
1130 short a, hits15, hits9 = 0, hits5 = 0;
1131 short has_bones15 = 0, has_bones9 = 0, has_bones5 = 0;
1133 BLI_init_rcti(&rect, mval[0] - 14, mval[0] + 14, mval[1] - 14, mval[1] + 14);
1134 hits15 = view3d_opengl_select(vc, buffer, MAXPICKBUF, &rect);
1136 for (a = 0; a < hits15; a++) if (buffer[4 * a + 3] & 0xFFFF0000) has_bones15 = 1;
1139 BLI_init_rcti(&rect, mval[0] - 9, mval[0] + 9, mval[1] - 9, mval[1] + 9);
1140 hits9 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect);
1142 for (a = 0; a < hits9; a++) if (buffer[offs + 4 * a + 3] & 0xFFFF0000) has_bones9 = 1;
1145 BLI_init_rcti(&rect, mval[0] - 5, mval[0] + 5, mval[1] - 5, mval[1] + 5);
1146 hits5 = view3d_opengl_select(vc, buffer + offs, MAXPICKBUF - offs, &rect);
1148 for (a = 0; a < hits5; a++) if (buffer[offs + 4 * a + 3] & 0xFFFF0000) has_bones5 = 1;
1153 offs = 4 * hits15 + 4 * hits9;
1154 memcpy(buffer, buffer + offs, 4 * offs);
1159 memcpy(buffer, buffer + offs, 4 * offs);
1167 offs = 4 * hits15 + 4 * hits9;
1168 memcpy(buffer, buffer + offs, 4 * offs);
1173 memcpy(buffer, buffer + offs, 4 * offs);
1182 /* returns basact */
1183 static Base *mouse_select_eval_buffer(ViewContext *vc, unsigned int *buffer, int hits, const int mval[2], Base *startbase, int has_bones)
1185 Scene *scene = vc->scene;
1186 View3D *v3d = vc->v3d;
1187 Base *base, *basact = NULL;
1188 static int lastmval[2] = {-100, -100};
1189 int a, donearest = 0;
1191 /* define if we use solid nearest select or not */
1192 if (v3d->drawtype > OB_WIRE) {
1194 if (ABS(mval[0] - lastmval[0]) < 3 && ABS(mval[1] - lastmval[1]) < 3) {
1195 if (!has_bones) /* hrms, if theres bones we always do nearest */
1199 lastmval[0] = mval[0]; lastmval[1] = mval[1];
1202 unsigned int min = 0xFFFFFFFF;
1203 int selcol = 0, notcol = 0;
1207 /* we skip non-bone hits */
1208 for (a = 0; a < hits; a++) {
1209 if (min > buffer[4 * a + 1] && (buffer[4 * a + 3] & 0xFFFF0000) ) {
1210 min = buffer[4 * a + 1];
1211 selcol = buffer[4 * a + 3] & 0xFFFF;
1216 /* only exclude active object when it is selected... */
1217 if (BASACT && (BASACT->flag & SELECT) && hits > 1) notcol = BASACT->selcol;
1219 for (a = 0; a < hits; a++) {
1220 if (min > buffer[4 * a + 1] && notcol != (buffer[4 * a + 3] & 0xFFFF)) {
1221 min = buffer[4 * a + 1];
1222 selcol = buffer[4 * a + 3] & 0xFFFF;
1229 if (BASE_SELECTABLE(v3d, base)) {
1230 if (base->selcol == selcol) break;
1234 if (base) basact = base;
1240 /* skip objects with select restriction, to prevent prematurely ending this loop
1241 * with an un-selectable choice */
1242 if (base->object->restrictflag & OB_RESTRICT_SELECT) {
1244 if (base == NULL) base = FIRSTBASE;
1245 if (base == startbase) break;
1248 if (BASE_SELECTABLE(v3d, base)) {
1249 for (a = 0; a < hits; a++) {
1251 /* skip non-bone objects */
1252 if ((buffer[4 * a + 3] & 0xFFFF0000)) {
1253 if (base->selcol == (buffer[(4 * a) + 3] & 0xFFFF))
1258 if (base->selcol == (buffer[(4 * a) + 3] & 0xFFFF))
1267 if (base == NULL) base = FIRSTBASE;
1268 if (base == startbase) break;
1275 /* mval comes from event->mval, only use within region handlers */
1276 Base *ED_view3d_give_base_under_cursor(bContext *C, const int mval[2])
1279 Base *basact = NULL;
1280 unsigned int buffer[4 * MAXPICKBUF];
1283 /* setup view context for argument to callbacks */
1284 view3d_operator_needs_opengl(C);
1285 view3d_set_viewcontext(C, &vc);
1287 hits = mixed_bones_object_selectbuffer(&vc, buffer, mval);
1290 int a, has_bones = 0;
1292 for (a = 0; a < hits; a++) if (buffer[4 * a + 3] & 0xFFFF0000) has_bones = 1;
1294 basact = mouse_select_eval_buffer(&vc, buffer, hits, mval, vc.scene->base.first, has_bones);
1300 static void deselect_all_tracks(MovieTracking *tracking)
1302 MovieTrackingObject *object;
1304 object = tracking->objects.first;
1306 ListBase *tracksbase = BKE_tracking_object_tracks(tracking, object);
1307 MovieTrackingTrack *track = tracksbase->first;
1310 BKE_tracking_deselect_track(track, TRACK_AREA_ALL);
1312 track = track->next;
1315 object = object->next;
1319 /* mval is region coords */
1320 static int mouse_select(bContext *C, const int mval[2], short extend, short obcenter, short enumerate)
1323 ARegion *ar = CTX_wm_region(C);
1324 View3D *v3d = CTX_wm_view3d(C);
1325 Scene *scene = CTX_data_scene(C);
1326 Base *base, *startbase = NULL, *basact = NULL, *oldbasact = NULL;
1327 int temp, a, dist = 100;
1331 /* setup view context for argument to callbacks */
1332 view3d_set_viewcontext(C, &vc);
1334 /* always start list from basact in wire mode */
1335 startbase = FIRSTBASE;
1336 if (BASACT && BASACT->next) startbase = BASACT->next;
1338 /* This block uses the control key to make the object selected by its center point rather than its contents */
1339 /* in editmode do not activate */
1342 /* note; shift+alt goes to group-flush-selecting */
1344 basact = object_mouse_select_menu(C, &vc, NULL, 0, mval, extend);
1349 if (BASE_SELECTABLE(v3d, base)) {
1350 project_short(ar, base->object->obmat[3], &base->sx);
1352 temp = abs(base->sx - mval[0]) + abs(base->sy - mval[1]);
1353 if (base == BASACT) temp += 10;
1362 if (base == NULL) base = FIRSTBASE;
1363 if (base == startbase) break;
1368 unsigned int buffer[4 * MAXPICKBUF];
1370 /* if objects have posemode set, the bones are in the same selection buffer */
1372 hits = mixed_bones_object_selectbuffer(&vc, buffer, mval);
1377 /* note: bundles are handling in the same way as bones */
1378 for (a = 0; a < hits; a++) if (buffer[4 * a + 3] & 0xFFFF0000) has_bones = 1;
1380 /* note; shift+alt goes to group-flush-selecting */
1381 if (has_bones == 0 && enumerate) {
1382 basact = object_mouse_select_menu(C, &vc, buffer, hits, mval, extend);
1385 basact = mouse_select_eval_buffer(&vc, buffer, hits, mval, startbase, has_bones);
1388 if (has_bones && basact) {
1389 if (basact->object->type == OB_CAMERA) {
1390 if (BASACT == basact) {
1394 for (i = 0; i < hits; i++) {
1395 hitresult = buffer[3 + (i * 4)];
1397 /* if there's bundles in buffer select bundles first,
1398 * so non-camera elements should be ignored in buffer */
1399 if (basact->selcol != (hitresult & 0xFFFF)) {
1403 /* index of bundle is 1<<16-based. if there's no "bone" index
1404 * in hight word, this buffer value belongs to camera,. not to bundle */
1405 if (buffer[4 * i + 3] & 0xFFFF0000) {
1406 MovieClip *clip = object_get_movieclip(scene, basact->object, 0);
1407 MovieTracking *tracking = &clip->tracking;
1408 ListBase *tracksbase;
1409 MovieTrackingTrack *track;
1411 track = BKE_tracking_indexed_track(&clip->tracking, hitresult >> 16, &tracksbase);
1413 if (TRACK_SELECTED(track) && extend) {
1415 BKE_tracking_deselect_track(track, TRACK_AREA_ALL);
1418 int oldsel = TRACK_SELECTED(track) ? 1 : 0;
1420 deselect_all_tracks(tracking);
1422 BKE_tracking_select_track(tracksbase, track, TRACK_AREA_ALL, extend);
1424 if (oldsel != (TRACK_SELECTED(track) ? 1 : 0))
1428 basact->flag |= SELECT;
1429 basact->object->flag = basact->flag;
1433 WM_event_add_notifier(C, NC_MOVIECLIP | ND_SELECT, track);
1434 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1441 /* fallback to regular object selection if no new bundles were selected,
1442 * allows to select object parented to reconstruction object */
1443 basact = mouse_select_eval_buffer(&vc, buffer, hits, mval, startbase, 0);
1447 else if (ED_do_pose_selectbuffer(scene, basact, buffer, hits, extend) ) { /* then bone is found */
1449 /* we make the armature selected:
1450 * not-selected active object in posemode won't work well for tools */
1451 basact->flag |= SELECT;
1452 basact->object->flag = basact->flag;
1455 WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, basact->object);
1456 WM_event_add_notifier(C, NC_OBJECT | ND_BONE_ACTIVE, basact->object);
1458 /* in weightpaint, we use selected bone to select vertexgroup, so no switch to new active object */
1459 if (BASACT && (BASACT->object->mode & OB_MODE_WEIGHT_PAINT)) {
1460 /* prevent activating */
1465 /* prevent bone selecting to pass on to object selecting */
1466 if (basact == BASACT)
1472 /* so, do we have something selected? */
1477 /* only do select */
1478 deselectall_except(scene, basact);
1479 ED_base_object_select(basact, BA_SELECT);
1481 /* also prevent making it active on mouse selection */
1482 else if (BASE_SELECTABLE(v3d, basact)) {
1487 deselectall_except(scene, basact);
1488 ED_base_object_select(basact, BA_SELECT);
1491 // XXX select_all_from_groups(basact);
1494 if (basact->flag & SELECT) {
1495 if (basact == oldbasact)
1496 ED_base_object_select(basact, BA_DESELECT);
1498 else ED_base_object_select(basact, BA_SELECT);
1501 if (oldbasact != basact) {
1502 ED_base_object_activate(C, basact); /* adds notifier */
1506 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, scene);
1512 /* ******************** border and circle ************************************** */
1514 typedef struct BoxSelectUserData {
1517 int select, pass, done;
1518 } BoxSelectUserData;
1520 int edge_inside_circle(short centx, short centy, short rad, short x1, short y1, short x2, short y2)
1522 int radsq = rad * rad;
1523 float v1[2], v2[2], v3[2];
1525 /* check points in circle itself */
1526 if ( (x1 - centx) * (x1 - centx) + (y1 - centy) * (y1 - centy) <= radsq) return 1;
1527 if ( (x2 - centx) * (x2 - centx) + (y2 - centy) * (y2 - centy) <= radsq) return 1;
1537 if (dist_to_line_segment_v2(v3, v1, v2) < (float)rad) return 1;
1542 static void do_nurbs_box_select__doSelect(void *userData, Nurb *UNUSED(nu), BPoint *bp, BezTriple *bezt, int beztindex, int x, int y)
1544 BoxSelectUserData *data = userData;
1545 Object *obedit = data->vc->obedit;
1546 Curve *cu = (Curve *)obedit->data;
1548 if (BLI_in_rcti(data->rect, x, y)) {
1550 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
1551 if (bp == cu->lastsel && !(bp->f1 & 1)) cu->lastsel = NULL;
1554 if (cu->drawflag & CU_HIDE_HANDLES) {
1555 /* can only be beztindex==0 here since handles are hidden */
1556 bezt->f1 = bezt->f2 = bezt->f3 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT);
1559 if (beztindex == 0) {
1560 bezt->f1 = data->select ? (bezt->f1 | SELECT) : (bezt->f1 & ~SELECT);
1562 else if (beztindex == 1) {
1563 bezt->f2 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT);
1566 bezt->f3 = data->select ? (bezt->f3 | SELECT) : (bezt->f3 & ~SELECT);
1570 if (bezt == cu->lastsel && !(bezt->f2 & 1)) cu->lastsel = NULL;
1574 static int do_nurbs_box_select(ViewContext *vc, rcti *rect, int select, int extend)
1576 BoxSelectUserData data;
1580 data.select = select;
1582 if (extend == 0 && select)
1583 CU_deselect_all(vc->obedit);
1585 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
1586 nurbs_foreachScreenVert(vc, do_nurbs_box_select__doSelect, &data);
1588 return OPERATOR_FINISHED;
1591 static void do_lattice_box_select__doSelect(void *userData, BPoint *bp, int x, int y)
1593 BoxSelectUserData *data = userData;
1595 if (BLI_in_rcti(data->rect, x, y)) {
1596 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
1599 static int do_lattice_box_select(ViewContext *vc, rcti *rect, int select, int extend)
1601 BoxSelectUserData data;
1605 data.select = select;
1607 if (extend == 0 && select)
1608 ED_setflagsLatt(vc->obedit, 0);
1610 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
1611 lattice_foreachScreenVert(vc, do_lattice_box_select__doSelect, &data);
1613 return OPERATOR_FINISHED;
1616 static void do_mesh_box_select__doSelectVert(void *userData, BMVert *eve, int x, int y, int UNUSED(index))
1618 BoxSelectUserData *data = userData;
1620 if (BLI_in_rcti(data->rect, x, y)) {
1621 BM_vert_select_set(data->vc->em->bm, eve, data->select);
1624 static void do_mesh_box_select__doSelectEdge(void *userData, BMEdge *eed, int x0, int y0, int x1, int y1, int index)
1626 BoxSelectUserData *data = userData;
1628 if (EDBM_backbuf_check(bm_solidoffs + index)) {
1629 if (data->pass == 0) {
1630 if (edge_fully_inside_rect(data->rect, x0, y0, x1, y1)) {
1631 BM_edge_select_set(data->vc->em->bm, eed, data->select);
1636 if (edge_inside_rect(data->rect, x0, y0, x1, y1)) {
1637 BM_edge_select_set(data->vc->em->bm, eed, data->select);
1642 static void do_mesh_box_select__doSelectFace(void *userData, BMFace *efa, int x, int y, int UNUSED(index))
1644 BoxSelectUserData *data = userData;
1646 if (BLI_in_rcti(data->rect, x, y)) {
1647 BM_face_select_set(data->vc->em->bm, efa, data->select);
1650 static int do_mesh_box_select(ViewContext *vc, rcti *rect, int select, int extend)
1652 BoxSelectUserData data;
1653 ToolSettings *ts = vc->scene->toolsettings;
1658 data.select = select;
1662 if (extend == 0 && select)
1663 EDBM_flag_disable_all(vc->em, BM_ELEM_SELECT);
1665 /* for non zbuf projections, don't change the GL state */
1666 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d);
1668 glLoadMatrixf(vc->rv3d->viewmat);
1669 bbsel = EDBM_backbuf_border_init(vc, rect->xmin, rect->ymin, rect->xmax, rect->ymax);
1671 if (ts->selectmode & SCE_SELECT_VERTEX) {
1673 edbm_backbuf_check_and_select_verts(vc->em, select);
1676 mesh_foreachScreenVert(vc, do_mesh_box_select__doSelectVert, &data, V3D_CLIP_TEST_RV3D_CLIPPING);
1679 if (ts->selectmode & SCE_SELECT_EDGE) {
1680 /* Does both bbsel and non-bbsel versions (need screen cos for both) */
1683 mesh_foreachScreenEdge(vc, do_mesh_box_select__doSelectEdge, &data, V3D_CLIP_TEST_OFF);
1685 if (data.done == 0) {
1687 mesh_foreachScreenEdge(vc, do_mesh_box_select__doSelectEdge, &data, V3D_CLIP_TEST_OFF);
1691 if (ts->selectmode & SCE_SELECT_FACE) {
1693 edbm_backbuf_check_and_select_faces(vc->em, select);
1696 mesh_foreachScreenFace(vc, do_mesh_box_select__doSelectFace, &data);
1700 EDBM_backbuf_free();
1702 EDBM_selectmode_flush(vc->em);
1704 return OPERATOR_FINISHED;
1707 static int do_meta_box_select(ViewContext *vc, rcti *rect, int select, int extend)
1709 MetaBall *mb = (MetaBall *)vc->obedit->data;
1713 unsigned int buffer[4 * MAXPICKBUF];
1716 hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, rect);
1718 if (extend == 0 && select) {
1719 for (ml = mb->editelems->first; ml; ml = ml->next) {
1720 ml->flag &= ~SELECT;
1724 for (ml = mb->editelems->first; ml; ml = ml->next) {
1725 for (a = 0; a < hits; a++) {
1726 if (ml->selcol1 == buffer[(4 * a) + 3]) {
1727 ml->flag |= MB_SCALE_RAD;
1728 if (select) ml->flag |= SELECT;
1729 else ml->flag &= ~SELECT;
1732 if (ml->selcol2 == buffer[(4 * a) + 3]) {
1733 ml->flag &= ~MB_SCALE_RAD;
1734 if (select) ml->flag |= SELECT;
1735 else ml->flag &= ~SELECT;
1741 return OPERATOR_FINISHED;
1744 static int do_armature_box_select(ViewContext *vc, rcti *rect, short select, short extend)
1746 bArmature *arm = vc->obedit->data;
1750 unsigned int buffer[4 * MAXPICKBUF];
1753 hits = view3d_opengl_select(vc, buffer, MAXPICKBUF, rect);
1755 /* clear flag we use to detect point was affected */
1756 for (ebone = arm->edbo->first; ebone; ebone = ebone->next)
1757 ebone->flag &= ~BONE_DONE;
1759 if (extend == 0 && select)
1760 ED_armature_deselect_all_visible(vc->obedit);
1762 /* first we only check points inside the border */
1763 for (a = 0; a < hits; a++) {
1764 int index = buffer[(4 * a) + 3];
1766 ebone = BLI_findlink(arm->edbo, index & ~(BONESEL_ANY));
1767 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
1768 if (index & BONESEL_TIP) {
1769 ebone->flag |= BONE_DONE;
1770 if (select) ebone->flag |= BONE_TIPSEL;
1771 else ebone->flag &= ~BONE_TIPSEL;
1774 if (index & BONESEL_ROOT) {
1775 ebone->flag |= BONE_DONE;
1776 if (select) ebone->flag |= BONE_ROOTSEL;
1777 else ebone->flag &= ~BONE_ROOTSEL;
1783 /* now we have to flush tag from parents... */
1784 for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
1785 if (ebone->parent && (ebone->flag & BONE_CONNECTED)) {
1786 if (ebone->parent->flag & BONE_DONE)
1787 ebone->flag |= BONE_DONE;
1791 /* only select/deselect entire bones when no points where in the rect */
1792 for (a = 0; a < hits; a++) {
1793 int index = buffer[(4 * a) + 3];
1795 ebone = BLI_findlink(arm->edbo, index & ~(BONESEL_ANY));
1796 if (index & BONESEL_BONE) {
1797 if ((ebone->flag & BONE_UNSELECTABLE) == 0) {
1798 if (!(ebone->flag & BONE_DONE)) {
1800 ebone->flag |= (BONE_ROOTSEL | BONE_TIPSEL | BONE_SELECTED);
1802 ebone->flag &= ~(BONE_ROOTSEL | BONE_TIPSEL | BONE_SELECTED);
1809 ED_armature_sync_selection(arm->edbo);
1811 return OPERATOR_CANCELLED;
1814 static int do_object_pose_box_select(bContext *C, ViewContext *vc, rcti *rect, int select, int extend)
1817 Object *ob = vc->obact;
1818 unsigned int *vbuffer = NULL; /* selection buffer */
1819 unsigned int *col; /* color in buffer */
1821 int bone_selected = 0;
1822 int totobj = MAXPICKBUF; // XXX solve later
1825 if ((ob) && (ob->mode & OB_MODE_POSE))
1830 if (extend == 0 && select) {
1832 CTX_DATA_BEGIN (C, bPoseChannel *, pchan, visible_pose_bones) {
1833 if ((pchan->bone->flag & BONE_UNSELECTABLE) == 0) {
1834 pchan->bone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
1840 object_deselect_all_visible(vc->scene, vc->v3d);
1844 /* selection buffer now has bones potentially too, so we add MAXPICKBUF */
1845 vbuffer = MEM_mallocN(4 * (totobj + MAXPICKBUF) * sizeof(unsigned int), "selection buffer");
1846 hits = view3d_opengl_select(vc, vbuffer, 4 * (totobj + MAXPICKBUF), rect);
1848 * LOGIC NOTES (theeth):
1849 * The buffer and ListBase have the same relative order, which makes the selection
1850 * very simple. Loop through both data sets at the same time, if the color
1851 * is the same as the object, we have a hit and can move to the next color
1852 * and object pair, if not, just move to the next object,
1853 * keeping the same color until we have a hit.
1855 * The buffer order is defined by OGL standard, hopefully no stupid GFX card
1856 * does it incorrectly.
1859 if (hits > 0) { /* no need to loop if there's no hit */
1863 for (base = vc->scene->base.first; base && hits; base = base->next) {
1864 if (BASE_SELECTABLE(vc->v3d, base)) {
1865 while (base->selcol == (*col & 0xFFFF)) { /* we got an object */
1867 if (*col & 0xFFFF0000) { /* we got a bone */
1868 bone = get_indexed_bone(base->object, *col & ~(BONESEL_ANY));
1871 if ((bone->flag & BONE_UNSELECTABLE) == 0) {
1872 bone->flag |= BONE_SELECTED;
1874 // XXX select_actionchannel_by_name(base->object->action, bone->name, 1);
1878 bArmature *arm = base->object->data;
1879 bone->flag &= ~BONE_SELECTED;
1880 // XXX select_actionchannel_by_name(base->object->action, bone->name, 0);
1881 if (arm->act_bone == bone)
1882 arm->act_bone = NULL;
1887 else if (!bone_only) {
1889 ED_base_object_select(base, BA_SELECT);
1891 ED_base_object_select(base, BA_DESELECT);
1894 col += 4; /* next color */
1896 if (hits == 0) break;
1900 if (bone_selected) {
1901 WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, base->object);
1905 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, vc->scene);
1910 return hits > 0 ? OPERATOR_FINISHED : OPERATOR_CANCELLED;
1913 static int view3d_borderselect_exec(bContext *C, wmOperator *op)
1920 int ret = OPERATOR_CANCELLED;
1922 view3d_operator_needs_opengl(C);
1924 /* setup view context for argument to callbacks */
1925 view3d_set_viewcontext(C, &vc);
1927 select = (RNA_int_get(op->ptr, "gesture_mode") == GESTURE_MODAL_SELECT);
1928 rect.xmin = RNA_int_get(op->ptr, "xmin");
1929 rect.ymin = RNA_int_get(op->ptr, "ymin");
1930 rect.xmax = RNA_int_get(op->ptr, "xmax");
1931 rect.ymax = RNA_int_get(op->ptr, "ymax");
1932 extend = RNA_boolean_get(op->ptr, "extend");
1935 switch (vc.obedit->type) {
1937 vc.em = BMEdit_FromObject(vc.obedit);
1938 ret = do_mesh_box_select(&vc, &rect, select, extend);
1939 // if (EM_texFaceCheck())
1940 if (ret & OPERATOR_FINISHED) {
1941 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
1946 ret = do_nurbs_box_select(&vc, &rect, select, extend);
1947 if (ret & OPERATOR_FINISHED) {
1948 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
1952 ret = do_meta_box_select(&vc, &rect, select, extend);
1953 if (ret & OPERATOR_FINISHED) {
1954 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
1958 ret = do_armature_box_select(&vc, &rect, select, extend);
1959 if (ret & OPERATOR_FINISHED) {
1960 WM_event_add_notifier(C, NC_OBJECT | ND_BONE_SELECT, vc.obedit);
1964 ret = do_lattice_box_select(&vc, &rect, select, extend);
1965 if (ret & OPERATOR_FINISHED) {
1966 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, vc.obedit->data);
1970 assert(!"border select on incorrect object type");
1973 else { /* no editmode, unified for bones and objects */
1974 if (vc.obact && vc.obact->mode & OB_MODE_SCULPT) {
1977 else if (vc.obact && paint_facesel_test(vc.obact)) {
1978 ret = do_paintface_box_select(&vc, &rect, select, extend);
1980 else if (vc.obact && paint_vertsel_test(vc.obact)) {
1981 ret = do_paintvert_box_select(&vc, &rect, select, extend);
1983 else if (vc.obact && vc.obact->mode & OB_MODE_PARTICLE_EDIT) {
1984 ret = PE_border_select(C, &rect, select, extend);
1986 else { /* object mode with none active */
1987 ret = do_object_pose_box_select(C, &vc, &rect, select, extend);
1995 /* *****************Selection Operators******************* */
1997 /* ****** Border Select ****** */
1998 void VIEW3D_OT_select_border(wmOperatorType *ot)
2001 ot->name = "Border Select";
2002 ot->description = "Select items using border selection";
2003 ot->idname = "VIEW3D_OT_select_border";
2006 ot->invoke = WM_border_select_invoke;
2007 ot->exec = view3d_borderselect_exec;
2008 ot->modal = WM_border_select_modal;
2009 ot->poll = view3d_selectable_data;
2010 ot->cancel = WM_border_select_cancel;
2013 ot->flag = OPTYPE_UNDO;
2016 WM_operator_properties_gesture_border(ot, TRUE);
2019 /* much like facesel_face_pick()*/
2020 /* returns 0 if not found, otherwise 1 */
2021 static int vertsel_vert_pick(struct bContext *C, Mesh *me, const int mval[2], unsigned int *index, int size)
2024 view3d_set_viewcontext(C, &vc);
2026 if (!me || me->totvert == 0)
2030 /* sample rect to increase changes of selecting, so that when clicking
2031 * on an face in the backbuf, we can still select a vert */
2034 *index = view3d_sample_backbuf_rect(&vc, mval, size, 1, me->totvert + 1, &dist, 0, NULL, NULL);
2037 /* sample only on the exact position */
2038 *index = view3d_sample_backbuf(&vc, mval[0], mval[1]);
2041 if ((*index) <= 0 || (*index) > (unsigned int)me->totvert)
2049 /* mouse selection in weight paint */
2050 /* gets called via generic mouse select operator */
2051 static int mouse_weight_paint_vertex_select(bContext *C, const int mval[2], short extend, Object *obact)
2053 Mesh*me = obact->data; /* already checked for NULL */
2054 unsigned int index = 0;
2057 if (vertsel_vert_pick(C, me, mval, &index, 50)) {
2058 mv = me->mvert + index;
2063 paintvert_deselect_all_visible(obact, SEL_DESELECT, FALSE);
2066 paintvert_flush_flags(obact);
2067 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
2073 /* ****** Mouse Select ****** */
2076 static int view3d_select_invoke(bContext *C, wmOperator *op, wmEvent *event)
2078 Object *obedit = CTX_data_edit_object(C);
2079 Object *obact = CTX_data_active_object(C);
2080 short extend = RNA_boolean_get(op->ptr, "extend");
2081 short center = RNA_boolean_get(op->ptr, "center");
2082 short enumerate = RNA_boolean_get(op->ptr, "enumerate");
2083 short object = RNA_boolean_get(op->ptr, "object");
2086 view3d_operator_needs_opengl(C);
2092 /* ack, this is incorrect but to do this correctly we would need an
2093 * alternative editmode/objectmode keymap, this copies the functionality
2094 * from 2.4x where Ctrl+Select in editmode does object select only */
2098 if (obedit && object == FALSE) {
2099 if (obedit->type == OB_MESH)
2100 retval = mouse_mesh(C, event->mval, extend);
2101 else if (obedit->type == OB_ARMATURE)
2102 retval = mouse_armature(C, event->mval, extend);
2103 else if (obedit->type == OB_LATTICE)
2104 retval = mouse_lattice(C, event->mval, extend);
2105 else if (ELEM(obedit->type, OB_CURVE, OB_SURF))
2106 retval = mouse_nurb(C, event->mval, extend);
2107 else if (obedit->type == OB_MBALL)
2108 retval = mouse_mball(C, event->mval, extend);
2111 else if (obact && obact->mode & OB_MODE_SCULPT)
2112 return OPERATOR_CANCELLED;
2113 else if (obact && obact->mode & OB_MODE_PARTICLE_EDIT)
2114 return PE_mouse_particles(C, event->mval, extend);
2115 else if (obact && paint_facesel_test(obact))
2116 retval = paintface_mouse_select(C, obact, event->mval, extend);
2117 else if (paint_vertsel_test(obact))
2118 retval = mouse_weight_paint_vertex_select(C, event->mval, extend, obact);
2120 retval = mouse_select(C, event->mval, extend, center, enumerate);
2122 /* passthrough allows tweaks
2123 * FINISHED to signal one operator worked
2126 return OPERATOR_PASS_THROUGH | OPERATOR_FINISHED;
2128 return OPERATOR_PASS_THROUGH; /* nothing selected, just passthrough */
2131 void VIEW3D_OT_select(wmOperatorType *ot)
2134 ot->name = "Activate/Select";
2135 ot->description = "Activate/select item(s)";
2136 ot->idname = "VIEW3D_OT_select";
2139 ot->invoke = view3d_select_invoke;
2140 ot->poll = ED_operator_view3d_active;
2143 ot->flag = OPTYPE_UNDO;
2146 RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend selection instead of deselecting everything first");
2147 RNA_def_boolean(ot->srna, "center", 0, "Center", "Use the object center when selecting, in editmode used to extend object selection");
2148 RNA_def_boolean(ot->srna, "enumerate", 0, "Enumerate", "List objects under the mouse (object mode only)");
2149 RNA_def_boolean(ot->srna, "object", 0, "Object", "Use object selection (editmode only)");
2153 /* -------------------- circle select --------------------------------------------- */
2155 typedef struct CircleSelectUserData {
2160 } CircleSelectUserData;
2162 static void mesh_circle_doSelectVert(void *userData, BMVert *eve, int x, int y, int UNUSED(index))
2164 CircleSelectUserData *data = userData;
2165 int mx = x - data->mval[0], my = y - data->mval[1];
2166 float r = sqrt(mx * mx + my * my);
2168 if (r <= data->radius) {
2169 BM_vert_select_set(data->vc->em->bm, eve, data->select);
2172 static void mesh_circle_doSelectEdge(void *userData, BMEdge *eed, int x0, int y0, int x1, int y1, int UNUSED(index))
2174 CircleSelectUserData *data = userData;
2176 if (edge_inside_circle(data->mval[0], data->mval[1], (short) data->radius, x0, y0, x1, y1)) {
2177 BM_edge_select_set(data->vc->em->bm, eed, data->select);
2180 static void mesh_circle_doSelectFace(void *userData, BMFace *efa, int x, int y, int UNUSED(index))
2182 CircleSelectUserData *data = userData;
2183 int mx = x - data->mval[0], my = y - data->mval[1];
2184 float r = sqrt(mx * mx + my * my);
2186 if (r <= data->radius) {
2187 BM_face_select_set(data->vc->em->bm, efa, data->select);
2191 static void mesh_circle_select(ViewContext *vc, int select, const int mval[2], float rad)
2193 ToolSettings *ts = vc->scene->toolsettings;
2195 CircleSelectUserData data;
2197 bbsel = EDBM_backbuf_circle_init(vc, mval[0], mval[1], (short)(rad + 1.0));
2198 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
2200 vc->em = BMEdit_FromObject(vc->obedit);
2203 data.select = select;
2204 data.mval[0] = mval[0];
2205 data.mval[1] = mval[1];
2208 if (ts->selectmode & SCE_SELECT_VERTEX) {
2210 edbm_backbuf_check_and_select_verts(vc->em, select == LEFTMOUSE);
2213 mesh_foreachScreenVert(vc, mesh_circle_doSelectVert, &data, V3D_CLIP_TEST_RV3D_CLIPPING);
2217 if (ts->selectmode & SCE_SELECT_EDGE) {
2219 edbm_backbuf_check_and_select_edges(vc->em, select == LEFTMOUSE);
2222 mesh_foreachScreenEdge(vc, mesh_circle_doSelectEdge, &data, V3D_CLIP_TEST_OFF);
2226 if (ts->selectmode & SCE_SELECT_FACE) {
2228 edbm_backbuf_check_and_select_faces(vc->em, select == LEFTMOUSE);
2231 mesh_foreachScreenFace(vc, mesh_circle_doSelectFace, &data);
2235 EDBM_backbuf_free();
2236 EDBM_selectmode_flush(vc->em);
2239 static void paint_facesel_circle_select(ViewContext *vc, int select, const int mval[2], float rad)
2241 Object *ob = vc->obact;
2242 Mesh *me = ob ? ob->data : NULL;
2243 /* int bbsel; */ /* UNUSED */
2246 bm_vertoffs = me->totpoly + 1; /* max index array */
2248 /* bbsel= */ /* UNUSED */ EDBM_backbuf_circle_init(vc, mval[0], mval[1], (short)(rad + 1.0));
2249 edbm_backbuf_check_and_select_tfaces(me, select == LEFTMOUSE);
2250 EDBM_backbuf_free();
2255 static void paint_vertsel_circle_select(ViewContext *vc, int select, const int mval[2], float rad)
2257 Object *ob = vc->obact;
2258 Mesh *me = ob ? ob->data : NULL;
2259 /* int bbsel; */ /* UNUSED */
2260 /* CircleSelectUserData data = {NULL}; */ /* UNUSED */
2262 bm_vertoffs = me->totvert + 1; /* max index array */
2264 /* bbsel= */ /* UNUSED */ EDBM_backbuf_circle_init(vc, mval[0], mval[1], (short)(rad + 1.0f));
2265 edbm_backbuf_check_and_select_verts_obmode(me, select == LEFTMOUSE);
2266 EDBM_backbuf_free();
2268 paintvert_flush_flags(ob);
2273 static void nurbscurve_circle_doSelect(void *userData, Nurb *UNUSED(nu), BPoint *bp, BezTriple *bezt, int beztindex, int x, int y)
2275 CircleSelectUserData *data = userData;
2276 int mx = x - data->mval[0], my = y - data->mval[1];
2277 float r = sqrt(mx * mx + my * my);
2278 Object *obedit = data->vc->obedit;
2279 Curve *cu = (Curve *)obedit->data;
2281 if (r <= data->radius) {
2283 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
2285 if (bp == cu->lastsel && !(bp->f1 & 1)) cu->lastsel = NULL;
2288 if (cu->drawflag & CU_HIDE_HANDLES) {
2289 /* can only be beztindex==0 here since handles are hidden */
2290 bezt->f1 = bezt->f2 = bezt->f3 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT);
2293 if (beztindex == 0) {
2294 bezt->f1 = data->select ? (bezt->f1 | SELECT) : (bezt->f1 & ~SELECT);
2296 else if (beztindex == 1) {
2297 bezt->f2 = data->select ? (bezt->f2 | SELECT) : (bezt->f2 & ~SELECT);
2300 bezt->f3 = data->select ? (bezt->f3 | SELECT) : (bezt->f3 & ~SELECT);
2304 if (bezt == cu->lastsel && !(bezt->f2 & 1)) cu->lastsel = NULL;
2308 static void nurbscurve_circle_select(ViewContext *vc, int select, const int mval[2], float rad)
2310 CircleSelectUserData data;
2312 /* set vc-> edit data */
2314 data.select = select;
2315 data.mval[0] = mval[0];
2316 data.mval[1] = mval[1];
2320 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
2321 nurbs_foreachScreenVert(vc, nurbscurve_circle_doSelect, &data);
2325 static void latticecurve_circle_doSelect(void *userData, BPoint *bp, int x, int y)
2327 CircleSelectUserData *data = userData;
2328 int mx = x - data->mval[0], my = y - data->mval[1];
2329 float r = sqrt(mx * mx + my * my);
2331 if (r <= data->radius) {
2332 bp->f1 = data->select ? (bp->f1 | SELECT) : (bp->f1 & ~SELECT);
2335 static void lattice_circle_select(ViewContext *vc, int select, const int mval[2], float rad)
2337 CircleSelectUserData data;
2339 /* set vc-> edit data */
2341 data.select = select;
2342 data.mval[0] = mval[0];
2343 data.mval[1] = mval[1];
2346 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
2347 lattice_foreachScreenVert(vc, latticecurve_circle_doSelect, &data);
2351 // NOTE: pose-bone case is copied from editbone case...
2352 static short pchan_circle_doSelectJoint(void *userData, bPoseChannel *pchan, int x, int y)
2354 CircleSelectUserData *data = userData;
2355 int mx = x - data->mval[0], my = y - data->mval[1];
2356 float r = sqrt(mx * mx + my * my);
2358 if (r <= data->radius) {
2360 pchan->bone->flag |= BONE_SELECTED;
2362 pchan->bone->flag &= ~BONE_SELECTED;
2367 static void pose_circle_select(ViewContext *vc, int select, const int mval[2], float rad)
2369 CircleSelectUserData data;
2370 bArmature *arm = vc->obact->data;
2371 bPose *pose = vc->obact->pose;
2372 bPoseChannel *pchan;
2375 /* set vc->edit data */
2376 data.select = select;
2377 data.mval[0] = mval[0];
2378 data.mval[1] = mval[1];
2381 ED_view3d_init_mats_rv3d(vc->obact, vc->rv3d); /* for foreach's screen/vert projection */
2383 /* check each PoseChannel... */
2384 // TODO: could be optimized at some point
2385 for (pchan = pose->chanbase.first; pchan; pchan = pchan->next) {
2386 short sco1[2], sco2[2], didpoint = 0;
2389 /* skip invisible bones */
2390 if (PBONE_VISIBLE(arm, pchan->bone) == 0)
2393 /* project head location to screenspace */
2394 mul_v3_m4v3(vec, vc->obact->obmat, pchan->pose_head);
2395 project_short(vc->ar, vec, sco1);
2397 /* project tail location to screenspace */
2398 mul_v3_m4v3(vec, vc->obact->obmat, pchan->pose_tail);
2399 project_short(vc->ar, vec, sco2);
2401 /* check if the head and/or tail is in the circle
2402 * - the call to check also does the selection already
2404 if (pchan_circle_doSelectJoint(&data, pchan, sco1[0], sco1[1]))
2406 if (pchan_circle_doSelectJoint(&data, pchan, sco2[0], sco2[1]))
2413 WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, vc->obact);
2417 static short armature_circle_doSelectJoint(void *userData, EditBone *ebone, int x, int y, short head)
2419 CircleSelectUserData *data = userData;
2420 int mx = x - data->mval[0], my = y - data->mval[1];
2421 float r = sqrt(mx * mx + my * my);
2423 if (r <= data->radius) {
2426 ebone->flag |= BONE_ROOTSEL;
2428 ebone->flag &= ~BONE_ROOTSEL;
2432 ebone->flag |= BONE_TIPSEL;
2434 ebone->flag &= ~BONE_TIPSEL;
2440 static void armature_circle_select(ViewContext *vc, int select, const int mval[2], float rad)
2442 CircleSelectUserData data;
2443 bArmature *arm = vc->obedit->data;
2447 /* set vc->edit data */
2448 data.select = select;
2449 data.mval[0] = mval[0];
2450 data.mval[1] = mval[1];
2453 ED_view3d_init_mats_rv3d(vc->obedit, vc->rv3d); /* for foreach's screen/vert projection */
2455 /* check each EditBone... */
2456 // TODO: could be optimized at some point
2457 for (ebone = arm->edbo->first; ebone; ebone = ebone->next) {
2458 short sco1[2], sco2[2], didpoint = 0;
2461 /* project head location to screenspace */
2462 mul_v3_m4v3(vec, vc->obedit->obmat, ebone->head);
2463 project_short(vc->ar, vec, sco1);
2465 /* project tail location to screenspace */
2466 mul_v3_m4v3(vec, vc->obedit->obmat, ebone->tail);
2467 project_short(vc->ar, vec, sco2);
2469 /* check if the head and/or tail is in the circle
2470 * - the call to check also does the selection already
2472 if (armature_circle_doSelectJoint(&data, ebone, sco1[0], sco1[1], 1))
2474 if (armature_circle_doSelectJoint(&data, ebone, sco2[0], sco2[1], 0))
2477 /* only if the endpoints didn't get selected, deal with the middle of the bone too */
2478 // XXX should we just do this always?
2479 if ( (didpoint == 0) && edge_inside_circle(mval[0], mval[1], rad, sco1[0], sco1[1], sco2[0], sco2[1]) ) {
2481 ebone->flag |= BONE_TIPSEL | BONE_ROOTSEL | BONE_SELECTED;
2483 ebone->flag &= ~(BONE_SELECTED | BONE_TIPSEL | BONE_ROOTSEL);
2491 ED_armature_sync_selection(arm->edbo);
2492 ED_armature_validate_active(arm);
2493 WM_main_add_notifier(NC_OBJECT | ND_BONE_SELECT, vc->obedit);
2497 /** Callbacks for circle selection in Editmode */
2499 static void obedit_circle_select(ViewContext *vc, short select, const int mval[2], float rad)
2501 switch (vc->obedit->type) {
2503 mesh_circle_select(vc, select, mval, rad);
2507 nurbscurve_circle_select(vc, select, mval, rad);
2510 lattice_circle_select(vc, select, mval, rad);
2513 armature_circle_select(vc, select, mval, rad);
2520 /* not a real operator, only for circle test */
2521 static int view3d_circle_select_exec(bContext *C, wmOperator *op)
2523 ScrArea *sa = CTX_wm_area(C);
2524 ARegion *ar = CTX_wm_region(C);
2525 Scene *scene = CTX_data_scene(C);
2526 Object *obact = CTX_data_active_object(C);
2527 View3D *v3d = sa->spacedata.first;
2528 int x = RNA_int_get(op->ptr, "x");
2529 int y = RNA_int_get(op->ptr, "y");
2530 int radius = RNA_int_get(op->ptr, "radius");
2531 int gesture_mode = RNA_int_get(op->ptr, "gesture_mode");
2534 select = (gesture_mode == GESTURE_MODAL_SELECT);
2536 if (CTX_data_edit_object(C) || paint_facesel_test(obact) || paint_vertsel_test(obact) ||
2537 (obact && (obact->mode & (OB_MODE_PARTICLE_EDIT | OB_MODE_POSE))) )
2542 view3d_operator_needs_opengl(C);
2544 view3d_set_viewcontext(C, &vc);
2548 if (CTX_data_edit_object(C)) {
2549 obedit_circle_select(&vc, select, mval, (float)radius);
2550 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
2552 else if (paint_facesel_test(obact)) {
2553 paint_facesel_circle_select(&vc, select, mval, (float)radius);
2554 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
2556 else if (paint_vertsel_test(obact)) {
2557 paint_vertsel_circle_select(&vc, select, mval, (float)radius);
2558 WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obact->data);
2560 else if (obact->mode & OB_MODE_POSE)
2561 pose_circle_select(&vc, select, mval, (float)radius);
2563 return PE_circle_select(C, select, mval, (float)radius);
2565 else if (obact && obact->mode & OB_MODE_SCULPT) {
2566 return OPERATOR_CANCELLED;
2570 select = select ? BA_SELECT : BA_DESELECT;
2571 for (base = FIRSTBASE; base; base = base->next) {
2572 if (BASE_SELECTABLE(v3d, base)) {
2573 project_short(ar, base->object->obmat[3], &base->sx);
2574 if (base->sx != IS_CLIPPED) {
2575 int dx = base->sx - x;
2576 int dy = base->sy - y;
2577 if (dx * dx + dy * dy < radius * radius)
2578 ED_base_object_select(base, select);
2583 WM_event_add_notifier(C, NC_SCENE | ND_OB_SELECT, CTX_data_scene(C));
2586 return OPERATOR_FINISHED;
2589 void VIEW3D_OT_select_circle(wmOperatorType *ot)
2591 ot->name = "Circle Select";
2592 ot->description = "Select items using circle selection";
2593 ot->idname = "VIEW3D_OT_select_circle";
2595 ot->invoke = WM_gesture_circle_invoke;
2596 ot->modal = WM_gesture_circle_modal;
2597 ot->exec = view3d_circle_select_exec;
2598 ot->poll = view3d_selectable_data;
2599 ot->cancel = WM_gesture_circle_cancel;
2602 ot->flag = OPTYPE_UNDO;
2604 RNA_def_int(ot->srna, "x", 0, INT_MIN, INT_MAX, "X", "", INT_MIN, INT_MAX);
2605 RNA_def_int(ot->srna, "y", 0, INT_MIN, INT_MAX, "Y", "", INT_MIN, INT_MAX);
2606 RNA_def_int(ot->srna, "radius", 0, INT_MIN, INT_MAX, "Radius", "", INT_MIN, INT_MAX);
2607 RNA_def_int(ot->srna, "gesture_mode", 0, INT_MIN, INT_MAX, "Event Type", "", INT_MIN, INT_MAX);