Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
Diffstat (limited to 'source/blender/editors/curve/editcurve_select.c')
-rw-r--r--source/blender/editors/curve/editcurve_select.c1728
1 files changed, 1728 insertions, 0 deletions
diff --git a/source/blender/editors/curve/editcurve_select.c b/source/blender/editors/curve/editcurve_select.c
new file mode 100644
index 00000000000..0f3942b0c90
--- /dev/null
+++ b/source/blender/editors/curve/editcurve_select.c
@@ -0,0 +1,1728 @@
+/*
+ * ***** BEGIN GPL LICENSE BLOCK *****
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
+ *
+ * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
+ * All rights reserved.
+ *
+ * The Original Code is: all of this file.
+ *
+ * Contributor(s): John Roper
+ *
+ * ***** END GPL LICENSE BLOCK *****
+ */
+
+/** \file blender/editors/curve/editcurve_select.c
+ * \ingroup edcurve
+ */
+
+#include "DNA_object_types.h"
+#include "DNA_scene_types.h"
+
+#include "MEM_guardedalloc.h"
+
+#include "BLI_bitmap.h"
+#include "BLI_math.h"
+#include "BLI_rand.h"
+#include "BLI_heap.h"
+
+#include "BKE_context.h"
+#include "BKE_curve.h"
+#include "BKE_fcurve.h"
+#include "BKE_report.h"
+
+#include "WM_api.h"
+#include "WM_types.h"
+
+#include "ED_screen.h"
+#include "ED_types.h"
+#include "ED_view3d.h"
+#include "ED_curve.h"
+
+#include "curve_intern.h"
+
+
+#include "RNA_access.h"
+#include "RNA_define.h"
+
+
+/* returns 1 in case (de)selection was successful */
+bool select_beztriple(BezTriple *bezt, bool selstatus, short flag, eVisible_Types hidden)
+{
+ if ((bezt->hide == 0) || (hidden == HIDDEN)) {
+ if (selstatus == SELECT) { /* selects */
+ bezt->f1 |= flag;
+ bezt->f2 |= flag;
+ bezt->f3 |= flag;
+ return true;
+ }
+ else { /* deselects */
+ bezt->f1 &= ~flag;
+ bezt->f2 &= ~flag;
+ bezt->f3 &= ~flag;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+/* returns 1 in case (de)selection was successful */
+bool select_bpoint(BPoint *bp, bool selstatus, short flag, bool hidden)
+{
+ if ((bp->hide == 0) || (hidden == 1)) {
+ if (selstatus == SELECT) {
+ bp->f1 |= flag;
+ return true;
+ }
+ else {
+ bp->f1 &= ~flag;
+ return true;
+ }
+ }
+
+ return false;
+}
+
+static bool swap_selection_beztriple(BezTriple *bezt)
+{
+ if (bezt->f2 & SELECT)
+ return select_beztriple(bezt, DESELECT, SELECT, VISIBLE);
+ else
+ return select_beztriple(bezt, SELECT, SELECT, VISIBLE);
+}
+
+static bool swap_selection_bpoint(BPoint *bp)
+{
+ if (bp->f1 & SELECT)
+ return select_bpoint(bp, DESELECT, SELECT, VISIBLE);
+ else
+ return select_bpoint(bp, SELECT, SELECT, VISIBLE);
+}
+
+bool ED_curve_nurb_select_check(Curve *cu, Nurb *nu)
+{
+ if (nu->type == CU_BEZIER) {
+ BezTriple *bezt;
+ int i;
+
+ for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
+ if (BEZT_ISSEL_ANY_HIDDENHANDLES(cu, bezt)) {
+ return true;
+ }
+ }
+ }
+ else {
+ BPoint *bp;
+ int i;
+
+ for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
+ if (bp->f1 & SELECT) {
+ return true;
+ }
+ }
+ }
+ return false;
+}
+
+int ED_curve_nurb_select_count(Curve *cu, Nurb *nu)
+{
+ int sel = 0;
+
+ if (nu->type == CU_BEZIER) {
+ BezTriple *bezt;
+ int i;
+
+ for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
+ if (BEZT_ISSEL_ANY_HIDDENHANDLES(cu, bezt)) {
+ sel++;
+ }
+ }
+ }
+ else {
+ BPoint *bp;
+ int i;
+
+ for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
+ if (bp->f1 & SELECT) {
+ sel++;
+ }
+ }
+ }
+
+ return sel;
+}
+
+void ED_curve_nurb_select_all(Nurb *nu)
+{
+ int i;
+
+ if (nu->bezt) {
+ BezTriple *bezt;
+ for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
+ if (bezt->hide == 0) {
+ BEZT_SEL_ALL(bezt);
+ }
+ }
+ }
+ else if (nu->bp) {
+ BPoint *bp;
+ for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
+ if (bp->hide == 0) {
+ bp->f1 |= SELECT;
+ }
+ }
+ }
+}
+
+void ED_curve_select_all(EditNurb *editnurb)
+{
+ Nurb *nu;
+ for (nu = editnurb->nurbs.first; nu; nu = nu->next) {
+ ED_curve_nurb_select_all(nu);
+ }
+}
+
+void ED_curve_nurb_deselect_all(Nurb *nu)
+{
+ int i;
+
+ if (nu->bezt) {
+ BezTriple *bezt;
+ for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
+ BEZT_DESEL_ALL(bezt);
+ }
+ }
+ else if (nu->bp) {
+ BPoint *bp;
+ for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
+ bp->f1 &= ~SELECT;
+ }
+ }
+}
+
+bool ED_curve_select_check(Curve *cu, struct EditNurb *editnurb)
+{
+ Nurb *nu;
+
+ for (nu = editnurb->nurbs.first; nu; nu = nu->next) {
+ if (ED_curve_nurb_select_check(cu, nu)) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+void ED_curve_deselect_all(EditNurb *editnurb)
+{
+ Nurb *nu;
+
+ for (nu = editnurb->nurbs.first; nu; nu = nu->next) {
+ ED_curve_nurb_deselect_all(nu);
+ }
+}
+
+void ED_curve_select_swap(EditNurb *editnurb, bool hide_handles)
+{
+ Nurb *nu;
+ BPoint *bp;
+ BezTriple *bezt;
+ int a;
+
+ for (nu = editnurb->nurbs.first; nu; nu = nu->next) {
+ if (nu->type == CU_BEZIER) {
+ bezt = nu->bezt;
+ a = nu->pntsu;
+ while (a--) {
+ if (bezt->hide == 0) {
+ bezt->f2 ^= SELECT; /* always do the center point */
+ if (!hide_handles) {
+ bezt->f1 ^= SELECT;
+ bezt->f3 ^= SELECT;
+ }
+ }
+ bezt++;
+ }
+ }
+ else {
+ bp = nu->bp;
+ a = nu->pntsu * nu->pntsv;
+ while (a--) {
+ swap_selection_bpoint(bp);
+ bp++;
+ }
+ }
+ }
+}
+
+/**
+ * \param next: -1/1 for prev/next
+ * \param cont: when true select continuously
+ * \param selstatus: inverts behavior
+ */
+static void select_adjacent_cp(
+ ListBase *editnurb, short next,
+ const bool cont, const bool selstatus)
+{
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+ bool lastsel = false;
+
+ if (next == 0) return;
+
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ lastsel = false;
+ if (nu->type == CU_BEZIER) {
+ a = nu->pntsu;
+ bezt = nu->bezt;
+ if (next < 0) bezt = &nu->bezt[a - 1];
+ while (a--) {
+ if (a - abs(next) < 0) break;
+ if ((lastsel == 0) && (bezt->hide == 0) && ((bezt->f2 & SELECT) || (selstatus == DESELECT))) {
+ bezt += next;
+ if (!(bezt->f2 & SELECT) || (selstatus == DESELECT)) {
+ short sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE);
+ if ((sel == 1) && (cont == 0)) lastsel = true;
+ }
+ }
+ else {
+ bezt += next;
+ lastsel = false;
+ }
+ /* move around in zigzag way so that we go through each */
+ bezt -= (next - next / abs(next));
+ }
+ }
+ else {
+ a = nu->pntsu * nu->pntsv;
+ bp = nu->bp;
+ if (next < 0) bp = &nu->bp[a - 1];
+ while (a--) {
+ if (a - abs(next) < 0) break;
+ if ((lastsel == 0) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == DESELECT))) {
+ bp += next;
+ if (!(bp->f1 & SELECT) || (selstatus == DESELECT)) {
+ short sel = select_bpoint(bp, selstatus, SELECT, VISIBLE);
+ if ((sel == 1) && (cont == 0)) lastsel = true;
+ }
+ }
+ else {
+ bp += next;
+ lastsel = false;
+ }
+ /* move around in zigzag way so that we go through each */
+ bp -= (next - next / abs(next));
+ }
+ }
+ }
+}
+
+/**************** select start/end operators **************/
+
+/* (de)selects first or last of visible part of each Nurb depending on selFirst
+ * selFirst: defines the end of which to select
+ * doswap: defines if selection state of each first/last control point is swapped
+ * selstatus: selection status in case doswap is false
+ */
+static void selectend_nurb(Object *obedit, eEndPoint_Types selfirst, bool doswap, bool selstatus)
+{
+ ListBase *editnurb = object_editcurve_get(obedit);
+ Nurb *nu;
+ BPoint *bp;
+ BezTriple *bezt;
+ Curve *cu;
+ int a;
+
+ if (obedit == NULL) return;
+
+ cu = (Curve *)obedit->data;
+ cu->actvert = CU_ACT_NONE;
+
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ if (nu->type == CU_BEZIER) {
+ a = nu->pntsu;
+
+ /* which point? */
+ if (selfirst == LAST) { /* select last */
+ bezt = &nu->bezt[a - 1];
+ }
+ else { /* select first */
+ bezt = nu->bezt;
+ }
+
+ while (a--) {
+ bool sel;
+ if (doswap) sel = swap_selection_beztriple(bezt);
+ else sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE);
+
+ if (sel == true) break;
+ }
+ }
+ else {
+ a = nu->pntsu * nu->pntsv;
+
+ /* which point? */
+ if (selfirst == LAST) { /* select last */
+ bp = &nu->bp[a - 1];
+ }
+ else { /* select first */
+ bp = nu->bp;
+ }
+
+ while (a--) {
+ if (bp->hide == 0) {
+ bool sel;
+ if (doswap) sel = swap_selection_bpoint(bp);
+ else sel = select_bpoint(bp, selstatus, SELECT, VISIBLE);
+
+ if (sel == true) break;
+ }
+ }
+ }
+ }
+}
+
+static int de_select_first_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+
+ selectend_nurb(obedit, FIRST, true, DESELECT);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+ BKE_curve_nurb_vert_active_validate(obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_de_select_first(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "(De)select First";
+ ot->idname = "CURVE_OT_de_select_first";
+ ot->description = "(De)select first of visible part of each NURBS";
+
+ /* api cfirstbacks */
+ ot->exec = de_select_first_exec;
+ ot->poll = ED_operator_editcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int de_select_last_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+
+ selectend_nurb(obedit, LAST, true, DESELECT);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+ BKE_curve_nurb_vert_active_validate(obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_de_select_last(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "(De)select Last";
+ ot->idname = "CURVE_OT_de_select_last";
+ ot->description = "(De)select last of visible part of each NURBS";
+
+ /* api clastbacks */
+ ot->exec = de_select_last_exec;
+ ot->poll = ED_operator_editcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+static int de_select_all_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ Curve *cu = obedit->data;
+ int action = RNA_enum_get(op->ptr, "action");
+
+ if (action == SEL_TOGGLE) {
+ action = SEL_SELECT;
+ if (ED_curve_select_check(cu, cu->editnurb)) {
+ action = SEL_DESELECT;
+ }
+ }
+
+ switch (action) {
+ case SEL_SELECT:
+ ED_curve_select_all(cu->editnurb);
+ break;
+ case SEL_DESELECT:
+ ED_curve_deselect_all(cu->editnurb);
+ break;
+ case SEL_INVERT:
+ ED_curve_select_swap(cu->editnurb, (cu->drawflag & CU_HIDE_HANDLES) != 0);
+ break;
+ }
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+ BKE_curve_nurb_vert_active_validate(cu);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_all(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "(De)select All";
+ ot->idname = "CURVE_OT_select_all";
+ ot->description = "(De)select all control points";
+
+ /* api callbacks */
+ ot->exec = de_select_all_exec;
+ ot->poll = ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ WM_operator_properties_select_all(ot);
+}
+
+
+
+/***************** select linked operator ******************/
+
+static int select_linked_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+ Curve *cu = (Curve *)obedit->data;
+ EditNurb *editnurb = cu->editnurb;
+ ListBase *nurbs = &editnurb->nurbs;
+ Nurb *nu;
+
+ for (nu = nurbs->first; nu; nu = nu->next) {
+ if (ED_curve_nurb_select_check(cu, nu)) {
+ ED_curve_nurb_select_all(nu);
+ }
+ }
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+static int select_linked_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event))
+{
+ return select_linked_exec(C, op);
+}
+
+void CURVE_OT_select_linked(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Linked All";
+ ot->idname = "CURVE_OT_select_linked";
+ ot->description = "Select all control points linked to active one";
+
+ /* api callbacks */
+ ot->exec = select_linked_exec;
+ ot->invoke = select_linked_invoke;
+ ot->poll = ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+}
+
+
+/***************** select linked pick operator ******************/
+
+static int select_linked_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ ViewContext vc;
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+ const bool select = !RNA_boolean_get(op->ptr, "deselect");
+
+ view3d_operator_needs_opengl(C);
+ view3d_set_viewcontext(C, &vc);
+
+ if (!ED_curve_pick_vert(&vc, 1, event->mval, &nu, &bezt, &bp, NULL)) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if (bezt) {
+ a = nu->pntsu;
+ bezt = nu->bezt;
+ while (a--) {
+ select_beztriple(bezt, select, SELECT, VISIBLE);
+ bezt++;
+ }
+ }
+ else if (bp) {
+ a = nu->pntsu * nu->pntsv;
+ bp = nu->bp;
+ while (a--) {
+ select_bpoint(bp, select, SELECT, VISIBLE);
+ bp++;
+ }
+ }
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+ if (!select) {
+ BKE_curve_nurb_vert_active_validate(obedit->data);
+ }
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_linked_pick(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Linked";
+ ot->idname = "CURVE_OT_select_linked_pick";
+ ot->description = "Select all control points linked to already selected ones";
+
+ /* api callbacks */
+ ot->invoke = select_linked_pick_invoke;
+ ot->poll = ED_operator_editsurfcurve_region_view3d;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_boolean(ot->srna, "deselect", 0, "Deselect", "Deselect linked control points rather than selecting them");
+}
+
+/***************** select row operator **********************/
+
+static int select_row_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+ Curve *cu = obedit->data;
+ ListBase *editnurb = object_editcurve_get(obedit);
+ static BPoint *last = NULL;
+ static int direction = 0;
+ Nurb *nu = NULL;
+ BPoint *bp = NULL;
+ int u = 0, v = 0, a, b;
+
+ if (!BKE_curve_nurb_vert_active_get(cu, &nu, (void *)&bp))
+ return OPERATOR_CANCELLED;
+
+ if (last == bp) {
+ direction = 1 - direction;
+ BKE_nurbList_flag_set(editnurb, 0);
+ }
+ last = bp;
+
+ u = cu->actvert % nu->pntsu;
+ v = cu->actvert / nu->pntsu;
+ bp = nu->bp;
+ for (a = 0; a < nu->pntsv; a++) {
+ for (b = 0; b < nu->pntsu; b++, bp++) {
+ if (direction) {
+ if (a == v) select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ }
+ else {
+ if (b == u) select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_row(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Control Point Row";
+ ot->idname = "CURVE_OT_select_row";
+ ot->description = "Select a row of control points including active one";
+
+ /* api callbacks */
+ ot->exec = select_row_exec;
+ ot->poll = ED_operator_editsurf;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/***************** select next operator **********************/
+
+static int select_next_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+ ListBase *editnurb = object_editcurve_get(obedit);
+
+ select_adjacent_cp(editnurb, 1, 0, SELECT);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_next(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Next";
+ ot->idname = "CURVE_OT_select_next";
+ ot->description = "Select control points following already selected ones along the curves";
+
+ /* api callbacks */
+ ot->exec = select_next_exec;
+ ot->poll = ED_operator_editcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/***************** select previous operator **********************/
+
+static int select_previous_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+ ListBase *editnurb = object_editcurve_get(obedit);
+
+ select_adjacent_cp(editnurb, -1, 0, SELECT);
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_previous(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Previous";
+ ot->idname = "CURVE_OT_select_previous";
+ ot->description = "Select control points preceding already selected ones along the curves";
+
+ /* api callbacks */
+ ot->exec = select_previous_exec;
+ ot->poll = ED_operator_editcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/***************** select more operator **********************/
+
+static int select_more_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+ ListBase *editnurb = object_editcurve_get(obedit);
+ Nurb *nu;
+ BPoint *bp, *tempbp;
+ int a;
+ short sel = 0;
+
+ /* note that NURBS surface is a special case because we mimic */
+ /* the behavior of "select more" of mesh tools. */
+ /* The algorithm is designed to work in planar cases so it */
+ /* may not be optimal always (example: end of NURBS sphere) */
+ if (obedit->type == OB_SURF) {
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ BLI_bitmap *selbpoints;
+ a = nu->pntsu * nu->pntsv;
+ bp = nu->bp;
+ selbpoints = BLI_BITMAP_NEW(a, "selectlist");
+ while (a > 0) {
+ if ((!BLI_BITMAP_TEST(selbpoints, a)) && (bp->hide == 0) && (bp->f1 & SELECT)) {
+ /* upper control point */
+ if (a % nu->pntsu != 0) {
+ tempbp = bp - 1;
+ if (!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
+ }
+
+ /* left control point. select only if it is not selected already */
+ if (a - nu->pntsu > 0) {
+ sel = 0;
+ tempbp = bp + nu->pntsu;
+ if (!(tempbp->f1 & SELECT)) sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
+ /* make sure selected bpoint is discarded */
+ if (sel == 1) BLI_BITMAP_ENABLE(selbpoints, a - nu->pntsu);
+ }
+
+ /* right control point */
+ if (a + nu->pntsu < nu->pntsu * nu->pntsv) {
+ tempbp = bp - nu->pntsu;
+ if (!(tempbp->f1 & SELECT)) select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
+ }
+
+ /* lower control point. skip next bp in case selection was made */
+ if (a % nu->pntsu != 1) {
+ sel = 0;
+ tempbp = bp + 1;
+ if (!(tempbp->f1 & SELECT)) sel = select_bpoint(tempbp, SELECT, SELECT, VISIBLE);
+ if (sel) {
+ bp++;
+ a--;
+ }
+ }
+ }
+
+ bp++;
+ a--;
+ }
+
+ MEM_freeN(selbpoints);
+ }
+ }
+ else {
+ select_adjacent_cp(editnurb, 1, 0, SELECT);
+ select_adjacent_cp(editnurb, -1, 0, SELECT);
+ }
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_more(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select More";
+ ot->idname = "CURVE_OT_select_more";
+ ot->description = "Select control points directly linked to already selected ones";
+
+ /* api callbacks */
+ ot->exec = select_more_exec;
+ ot->poll = ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/******************** select less operator *****************/
+
+/* basic method: deselect if control point doesn't have all neighbors selected */
+static int select_less_exec(bContext *C, wmOperator *UNUSED(op))
+{
+ Object *obedit = CTX_data_edit_object(C);
+ ListBase *editnurb = object_editcurve_get(obedit);
+ Nurb *nu;
+ BPoint *bp;
+ BezTriple *bezt;
+ int a;
+ int sel = 0;
+ short lastsel = false;
+
+ if (obedit->type == OB_SURF) {
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ BLI_bitmap *selbpoints;
+ a = nu->pntsu * nu->pntsv;
+ bp = nu->bp;
+ selbpoints = BLI_BITMAP_NEW(a, "selectlist");
+ while (a--) {
+ if ((bp->hide == 0) && (bp->f1 & SELECT)) {
+ sel = 0;
+
+ /* check if neighbors have been selected */
+ /* edges of surface are an exception */
+ if ((a + 1) % nu->pntsu == 0) {
+ sel++;
+ }
+ else {
+ bp--;
+ if (BLI_BITMAP_TEST(selbpoints, a + 1) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++;
+ bp++;
+ }
+
+ if ((a + 1) % nu->pntsu == 1) {
+ sel++;
+ }
+ else {
+ bp++;
+ if ((bp->hide == 0) && (bp->f1 & SELECT)) sel++;
+ bp--;
+ }
+
+ if (a + 1 > nu->pntsu * nu->pntsv - nu->pntsu) {
+ sel++;
+ }
+ else {
+ bp -= nu->pntsu;
+ if (BLI_BITMAP_TEST(selbpoints, a + nu->pntsu) || ((bp->hide == 0) && (bp->f1 & SELECT))) sel++;
+ bp += nu->pntsu;
+ }
+
+ if (a < nu->pntsu) {
+ sel++;
+ }
+ else {
+ bp += nu->pntsu;
+ if ((bp->hide == 0) && (bp->f1 & SELECT)) sel++;
+ bp -= nu->pntsu;
+ }
+
+ if (sel != 4) {
+ select_bpoint(bp, DESELECT, SELECT, VISIBLE);
+ BLI_BITMAP_ENABLE(selbpoints, a);
+ }
+ }
+ else {
+ lastsel = false;
+ }
+
+ bp++;
+ }
+
+ MEM_freeN(selbpoints);
+ }
+ }
+ else {
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ lastsel = false;
+ /* check what type of curve/nurb it is */
+ if (nu->type == CU_BEZIER) {
+ a = nu->pntsu;
+ bezt = nu->bezt;
+ while (a--) {
+ if ((bezt->hide == 0) && (bezt->f2 & SELECT)) {
+ sel = (lastsel == 1);
+
+ /* check if neighbors have been selected */
+ /* first and last are exceptions */
+ if (a == nu->pntsu - 1) {
+ sel++;
+ }
+ else {
+ bezt--;
+ if ((bezt->hide == 0) && (bezt->f2 & SELECT)) sel++;
+ bezt++;
+ }
+
+ if (a == 0) {
+ sel++;
+ }
+ else {
+ bezt++;
+ if ((bezt->hide == 0) && (bezt->f2 & SELECT)) sel++;
+ bezt--;
+ }
+
+ if (sel != 2) {
+ select_beztriple(bezt, DESELECT, SELECT, VISIBLE);
+ lastsel = true;
+ }
+ else {
+ lastsel = false;
+ }
+ }
+ else {
+ lastsel = false;
+ }
+
+ bezt++;
+ }
+ }
+ else {
+ a = nu->pntsu * nu->pntsv;
+ bp = nu->bp;
+ while (a--) {
+ if ((lastsel == 0) && (bp->hide == 0) && (bp->f1 & SELECT)) {
+ if (lastsel != 0) sel = 1;
+ else sel = 0;
+
+ /* first and last are exceptions */
+ if (a == nu->pntsu * nu->pntsv - 1) {
+ sel++;
+ }
+ else {
+ bp--;
+ if ((bp->hide == 0) && (bp->f1 & SELECT)) sel++;
+ bp++;
+ }
+
+ if (a == 0) {
+ sel++;
+ }
+ else {
+ bp++;
+ if ((bp->hide == 0) && (bp->f1 & SELECT)) sel++;
+ bp--;
+ }
+
+ if (sel != 2) {
+ select_bpoint(bp, DESELECT, SELECT, VISIBLE);
+ lastsel = true;
+ }
+ else {
+ lastsel = false;
+ }
+ }
+ else {
+ lastsel = false;
+ }
+
+ bp++;
+ }
+ }
+ }
+ }
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+ BKE_curve_nurb_vert_active_validate(obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_less(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Less";
+ ot->idname = "CURVE_OT_select_less";
+ ot->description = "Reduce current selection by deselecting boundary elements";
+
+ /* api callbacks */
+ ot->exec = select_less_exec;
+ ot->poll = ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/********************** select random *********************/
+
+static void curve_select_random(ListBase *editnurb, float randfac, bool select)
+{
+ Nurb *nu;
+ BezTriple *bezt;
+ BPoint *bp;
+ int a;
+
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ if (nu->type == CU_BEZIER) {
+ bezt = nu->bezt;
+ a = nu->pntsu;
+ while (a--) {
+ if (!bezt->hide) {
+ if (BLI_frand() < randfac) {
+ select_beztriple(bezt, select, SELECT, VISIBLE);
+ }
+ }
+ bezt++;
+ }
+ }
+ else {
+ bp = nu->bp;
+ a = nu->pntsu * nu->pntsv;
+
+ while (a--) {
+ if (!bp->hide) {
+ if (BLI_frand() < randfac) {
+ select_bpoint(bp, select, SELECT, VISIBLE);
+ }
+ }
+ bp++;
+ }
+ }
+ }
+}
+
+static int curve_select_random_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ ListBase *editnurb = object_editcurve_get(obedit);
+ const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
+ const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
+
+ curve_select_random(editnurb, randfac, select);
+ BKE_curve_nurb_vert_active_validate(obedit->data);
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_random(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Random";
+ ot->idname = "CURVE_OT_select_random";
+ ot->description = "Randomly select some control points";
+
+ /* api callbacks */
+ ot->exec = curve_select_random_exec;
+ ot->poll = ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ RNA_def_float_percentage(ot->srna, "percent", 50.f, 0.0f, 100.0f,
+ "Percent", "Percentage of elements to select randomly", 0.0f, 100.0f);
+ WM_operator_properties_select_action_simple(ot, SEL_SELECT);
+}
+
+/********************* every nth number of point *******************/
+
+static void select_nth_bezt(Nurb *nu, BezTriple *bezt, int nth, int skip, int offset)
+{
+ int a, start;
+
+ start = bezt - nu->bezt;
+ a = nu->pntsu;
+ bezt = &nu->bezt[a - 1];
+
+ while (a--) {
+ const int depth = abs(start - a);
+ if ((offset + depth) % (skip + nth) >= skip) {
+ select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
+ }
+
+ bezt--;
+ }
+}
+
+static void select_nth_bp(Nurb *nu, BPoint *bp, int nth, int skip, int offset)
+{
+ int a, startrow, startpnt;
+ int row, pnt;
+
+ startrow = (bp - nu->bp) / nu->pntsu;
+ startpnt = (bp - nu->bp) % nu->pntsu;
+
+ a = nu->pntsu * nu->pntsv;
+ bp = &nu->bp[a - 1];
+ row = nu->pntsv - 1;
+ pnt = nu->pntsu - 1;
+
+ while (a--) {
+ const int depth = abs(pnt - startpnt) + abs(row - startrow);
+ if ((offset + depth) % (skip + nth) >= skip) {
+ select_bpoint(bp, DESELECT, SELECT, HIDDEN);
+ }
+
+ pnt--;
+ if (pnt < 0) {
+ pnt = nu->pntsu - 1;
+ row--;
+ }
+
+ bp--;
+ }
+}
+
+bool ED_curve_select_nth(Curve *cu, int nth, int skip, int offset)
+{
+ Nurb *nu = NULL;
+ void *vert = NULL;
+
+ if (!BKE_curve_nurb_vert_active_get(cu, &nu, &vert))
+ return false;
+
+ if (nu->bezt) {
+ select_nth_bezt(nu, vert, nth, skip, offset);
+ }
+ else {
+ select_nth_bp(nu, vert, nth, skip, offset);
+ }
+
+ return true;
+}
+
+static int select_nth_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ const int nth = RNA_int_get(op->ptr, "nth") - 1;
+ const int skip = RNA_int_get(op->ptr, "skip");
+ int offset = RNA_int_get(op->ptr, "offset");
+
+ /* so input of offset zero ends up being (nth - 1) */
+ offset = mod_i(offset, nth + skip);
+
+ if (!ED_curve_select_nth(obedit->data, nth, skip, offset)) {
+ if (obedit->type == OB_SURF) {
+ BKE_report(op->reports, RPT_ERROR, "Surface has not got active point");
+ }
+ else {
+ BKE_report(op->reports, RPT_ERROR, "Curve has not got active point");
+ }
+
+ return OPERATOR_CANCELLED;
+ }
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_select_nth(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Checker Deselect";
+ ot->description = "Deselect every other vertex";
+ ot->idname = "CURVE_OT_select_nth";
+
+ /* api callbacks */
+ ot->exec = select_nth_exec;
+ ot->poll = ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ RNA_def_int(ot->srna, "nth", 2, 2, INT_MAX, "Nth Selection", "", 2, 100);
+ RNA_def_int(ot->srna, "skip", 1, 1, INT_MAX, "Skip", "", 1, 100);
+ RNA_def_int(ot->srna, "offset", 0, INT_MIN, INT_MAX, "Offset", "", -100, 100);
+}
+
+
+/* -------------------------------------------------------------------- */
+/* Select Similar */
+
+/** \name Select Similar
+ * \{ */
+
+enum {
+ SIM_CMP_EQ = 0,
+ SIM_CMP_GT,
+ SIM_CMP_LT,
+};
+
+static EnumPropertyItem curve_prop_similar_compare_types[] = {
+ {SIM_CMP_EQ, "EQUAL", 0, "Equal", ""},
+ {SIM_CMP_GT, "GREATER", 0, "Greater", ""},
+ {SIM_CMP_LT, "LESS", 0, "Less", ""},
+
+ {0, NULL, 0, NULL, NULL}
+};
+
+enum {
+ SIMCURHAND_TYPE = 0,
+ SIMCURHAND_RADIUS,
+ SIMCURHAND_WEIGHT,
+ SIMCURHAND_DIRECTION,
+};
+
+static EnumPropertyItem curve_prop_similar_types[] = {
+ {SIMCURHAND_TYPE, "TYPE", 0, "Type", ""},
+ {SIMCURHAND_RADIUS, "RADIUS", 0, "Radius", ""},
+ {SIMCURHAND_WEIGHT, "WEIGHT", 0, "Weight", ""},
+ {SIMCURHAND_DIRECTION, "DIRECTION", 0, "Direction", ""},
+ {0, NULL, 0, NULL, NULL}
+};
+
+static int curve_select_similar_cmp_fl(const float delta, const float thresh, const int compare)
+{
+ switch (compare) {
+ case SIM_CMP_EQ:
+ return (fabsf(delta) <= thresh);
+ case SIM_CMP_GT:
+ return ((delta + thresh) >= 0.0f);
+ case SIM_CMP_LT:
+ return ((delta - thresh) <= 0.0f);
+ default:
+ BLI_assert(0);
+ return 0;
+ }
+}
+
+
+static void curve_select_similar_direction__bezt(Nurb *nu, const float dir_ref[3], float angle_cos)
+{
+ BezTriple *bezt;
+ int i;
+
+ for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
+ if (!bezt->hide) {
+ float dir[3];
+ BKE_nurb_bezt_calc_normal(nu, bezt, dir);
+ if (fabsf(dot_v3v3(dir_ref, dir)) >= angle_cos) {
+ select_beztriple(bezt, SELECT, SELECT, VISIBLE);
+ }
+ }
+ }
+}
+
+static void curve_select_similar_direction__bp(Nurb *nu, const float dir_ref[3], float angle_cos)
+{
+ BPoint *bp;
+ int i;
+
+ for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
+ if (!bp->hide) {
+ float dir[3];
+ BKE_nurb_bpoint_calc_normal(nu, bp, dir);
+ if (fabsf(dot_v3v3(dir_ref, dir)) >= angle_cos) {
+ select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ }
+ }
+ }
+}
+
+static bool curve_select_similar_direction(ListBase *editnurb, Curve *cu, float thresh)
+{
+ Nurb *nu, *act_nu;
+ void *act_vert;
+ float dir[3];
+ float angle_cos;
+
+ if (!BKE_curve_nurb_vert_active_get(cu, &act_nu, &act_vert)) {
+ return false;
+ }
+
+ if (act_nu->type == CU_BEZIER) {
+ BKE_nurb_bezt_calc_normal(act_nu, act_vert, dir);
+ }
+ else {
+ BKE_nurb_bpoint_calc_normal(act_nu, act_vert, dir);
+ }
+
+ /* map 0-1 to radians, 'cos' for comparison */
+ angle_cos = cosf(thresh * (float)M_PI_2);
+
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ if (nu->type == CU_BEZIER) {
+ curve_select_similar_direction__bezt(nu, dir, angle_cos);
+ }
+ else {
+ curve_select_similar_direction__bp(nu, dir, angle_cos);
+ }
+ }
+
+ return true;
+}
+
+static void curve_select_similar_radius__bezt(Nurb *nu, float radius_ref, int compare, float thresh)
+{
+ BezTriple *bezt;
+ int i;
+
+ for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
+ if (!bezt->hide) {
+ if (curve_select_similar_cmp_fl(bezt->radius - radius_ref, thresh, compare)) {
+ select_beztriple(bezt, SELECT, SELECT, VISIBLE);
+ }
+ }
+ }
+}
+
+static void curve_select_similar_radius__bp(Nurb *nu, float radius_ref, int compare, float thresh)
+{
+ BPoint *bp;
+ int i;
+
+ for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
+ if (!bp->hide) {
+ if (curve_select_similar_cmp_fl(bp->radius - radius_ref, thresh, compare)) {
+ select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ }
+ }
+ }
+}
+
+static bool curve_select_similar_radius(ListBase *editnurb, Curve *cu, float compare, float thresh)
+{
+ Nurb *nu, *act_nu;
+ void *act_vert;
+ float radius_ref;
+
+ if (!BKE_curve_nurb_vert_active_get(cu, &act_nu, &act_vert)) {
+ return false;
+ }
+
+ if (act_nu->type == CU_BEZIER) {
+ radius_ref = ((BezTriple *)act_vert)->radius;
+ }
+ else {
+ radius_ref = ((BPoint *)act_vert)->radius;
+ }
+
+ /* make relative */
+ thresh *= radius_ref;
+
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ if (nu->type == CU_BEZIER) {
+ curve_select_similar_radius__bezt(nu, radius_ref, compare, thresh);
+ }
+ else {
+ curve_select_similar_radius__bp(nu, radius_ref, compare, thresh);
+ }
+ }
+
+ return true;
+}
+
+static void curve_select_similar_weight__bezt(Nurb *nu, float weight_ref, int compare, float thresh)
+{
+ BezTriple *bezt;
+ int i;
+
+ for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
+ if (!bezt->hide) {
+ if (curve_select_similar_cmp_fl(bezt->weight - weight_ref, thresh, compare)) {
+ select_beztriple(bezt, SELECT, SELECT, VISIBLE);
+ }
+ }
+ }
+}
+
+static void curve_select_similar_weight__bp(Nurb *nu, float weight_ref, int compare, float thresh)
+{
+ BPoint *bp;
+ int i;
+
+ for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
+ if (!bp->hide) {
+ if (curve_select_similar_cmp_fl(bp->weight - weight_ref, thresh, compare)) {
+ select_bpoint(bp, SELECT, SELECT, VISIBLE);
+ }
+ }
+ }
+}
+
+static bool curve_select_similar_weight(ListBase *editnurb, Curve *cu, float compare, float thresh)
+{
+ Nurb *nu, *act_nu;
+ void *act_vert;
+ float weight_ref;
+
+ if (!BKE_curve_nurb_vert_active_get(cu, &act_nu, &act_vert))
+ return false;
+
+ if (act_nu->type == CU_BEZIER) {
+ weight_ref = ((BezTriple *)act_vert)->weight;
+ }
+ else {
+ weight_ref = ((BPoint *)act_vert)->weight;
+ }
+
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ if (nu->type == CU_BEZIER) {
+ curve_select_similar_weight__bezt(nu, weight_ref, compare, thresh);
+ }
+ else {
+ curve_select_similar_weight__bp(nu, weight_ref, compare, thresh);
+ }
+ }
+
+ return true;
+}
+
+static bool curve_select_similar_type(ListBase *editnurb, Curve *cu)
+{
+ Nurb *nu, *act_nu;
+ int type_ref;
+
+ /* Get active nurb type */
+ act_nu = BKE_curve_nurb_active_get(cu);
+
+ if (!act_nu)
+ return false;
+
+ /* Get the active nurb type */
+ type_ref = act_nu->type;
+
+ for (nu = editnurb->first; nu; nu = nu->next) {
+ if (nu->type == type_ref) {
+ ED_curve_nurb_select_all(nu);
+ }
+ }
+
+ return true;
+}
+
+static int curve_select_similar_exec(bContext *C, wmOperator *op)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ Curve *cu = obedit->data;
+ ListBase *editnurb = object_editcurve_get(obedit);
+ bool changed = false;
+
+ /* Get props */
+ const int type = RNA_enum_get(op->ptr, "type");
+ const float thresh = RNA_float_get(op->ptr, "threshold");
+ const int compare = RNA_enum_get(op->ptr, "compare");
+
+ switch (type) {
+ case SIMCURHAND_TYPE:
+ changed = curve_select_similar_type(editnurb, cu);
+ break;
+ case SIMCURHAND_RADIUS:
+ changed = curve_select_similar_radius(editnurb, cu, compare, thresh);
+ break;
+ case SIMCURHAND_WEIGHT:
+ changed = curve_select_similar_weight(editnurb, cu, compare, thresh);
+ break;
+ case SIMCURHAND_DIRECTION:
+ changed = curve_select_similar_direction(editnurb, cu, thresh);
+ break;
+ }
+
+ if (changed) {
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+ return OPERATOR_FINISHED;
+ }
+ else {
+ return OPERATOR_CANCELLED;
+ }
+}
+
+void CURVE_OT_select_similar(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Select Similar";
+ ot->idname = "CURVE_OT_select_similar";
+ ot->description = "Select similar curve points by property type";
+
+ /* api callbacks */
+ ot->invoke = WM_menu_invoke;
+ ot->exec = curve_select_similar_exec;
+ ot->poll = ED_operator_editsurfcurve;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+
+ /* properties */
+ ot->prop = RNA_def_enum(ot->srna, "type", curve_prop_similar_types, SIMCURHAND_WEIGHT, "Type", "");
+ RNA_def_enum(ot->srna, "compare", curve_prop_similar_compare_types, SIM_CMP_EQ, "Compare", "");
+ RNA_def_float(ot->srna, "threshold", 0.1, 0.0, FLT_MAX, "Threshold", "", 0.0, 100.0);
+}
+
+/** \} */
+
+
+/* -------------------------------------------------------------------- */
+/* Select Shortest Path */
+
+/** \name Select Path
+ * \{ */
+
+static float curve_calc_dist_pair(const Nurb *nu, int a, int b)
+{
+ const float *a_fl, *b_fl;
+
+ if (nu->type == CU_BEZIER) {
+ a_fl = nu->bezt[a].vec[1];
+ b_fl = nu->bezt[b].vec[1];
+ }
+ else {
+ a_fl = nu->bp[a].vec;
+ b_fl = nu->bp[b].vec;
+ }
+
+ return len_v3v3(a_fl, b_fl);
+}
+
+static float curve_calc_dist_span(Nurb *nu, int vert_src, int vert_dst)
+{
+ const int u = nu->pntsu;
+ int i_prev, i;
+ float dist = 0.0f;
+
+ BLI_assert(nu->pntsv == 1);
+
+ i_prev = vert_src;
+ i = (i_prev + 1) % u;
+
+ while (true) {
+ dist += curve_calc_dist_pair(nu, i_prev, i);
+
+ if (i == vert_dst) {
+ break;
+ }
+ i = (i + 1) % u;
+ }
+ return dist;
+}
+
+static void curve_select_shortest_path_curve(Nurb *nu, int vert_src, int vert_dst)
+{
+ const int u = nu->pntsu;
+ int i;
+
+ if (vert_src > vert_dst) {
+ SWAP(int, vert_src, vert_dst);
+ }
+
+ if (nu->flagu & CU_NURB_CYCLIC) {
+ if (curve_calc_dist_span(nu, vert_src, vert_dst) >
+ curve_calc_dist_span(nu, vert_dst, vert_src))
+ {
+ SWAP(int, vert_src, vert_dst);
+ }
+ }
+
+ i = vert_src;
+ while (true) {
+ if (nu->type & CU_BEZIER) {
+ select_beztriple(&nu->bezt[i], SELECT, SELECT, HIDDEN);
+ }
+ else {
+ select_bpoint(&nu->bp[i], SELECT, SELECT, HIDDEN);
+ }
+
+ if (i == vert_dst) {
+ break;
+ }
+ i = (i + 1) % u;
+ }
+}
+
+static void curve_select_shortest_path_surf(Nurb *nu, int vert_src, int vert_dst)
+{
+ Heap *heap;
+
+ int i, vert_curr;
+
+ int totu = nu->pntsu;
+ int totv = nu->pntsv;
+ int vert_num = totu * totv;
+
+ /* custom data */
+ struct PointAdj {
+ int vert, vert_prev;
+ float cost;
+ } *data;
+
+ /* init connectivity data */
+ data = MEM_mallocN(sizeof(*data) * vert_num, __func__);
+ for (i = 0; i < vert_num; i++) {
+ data[i].vert = i;
+ data[i].vert_prev = -1;
+ data[i].cost = FLT_MAX;
+ }
+
+ /* init heap */
+ heap = BLI_heap_new();
+
+ BLI_heap_insert(heap, 0.0f, &data[vert_src].vert);
+ data[vert_src].cost = 0.0f;
+ data[vert_src].vert_prev = vert_src; /* nop */
+
+ while (!BLI_heap_is_empty(heap)) {
+ int axis, sign;
+ int u, v;
+
+ vert_curr = *((int *)BLI_heap_popmin(heap));
+ if (vert_curr == vert_dst) {
+ break;
+ }
+
+ BKE_nurb_index_to_uv(nu, vert_curr, &u, &v);
+
+ /* loop over 4 adjacent verts */
+ for (sign = -1; sign != 3; sign += 2) {
+ for (axis = 0; axis != 2; axis += 1) {
+ int uv_other[2] = {u, v};
+ int vert_other;
+
+ uv_other[axis] += sign;
+
+ vert_other = BKE_nurb_index_from_uv(nu, uv_other[0], uv_other[1]);
+ if (vert_other != -1) {
+ const float dist = data[vert_curr].cost + curve_calc_dist_pair(nu, vert_curr, vert_other);
+
+ if (data[vert_other].cost > dist) {
+ data[vert_other].cost = dist;
+ if (data[vert_other].vert_prev == -1) {
+ BLI_heap_insert(heap, data[vert_other].cost, &data[vert_other].vert);
+ }
+ data[vert_other].vert_prev = vert_curr;
+ }
+ }
+
+ }
+ }
+
+ }
+
+ BLI_heap_free(heap, NULL);
+
+ if (vert_curr == vert_dst) {
+ i = 0;
+ while (vert_curr != vert_src && i++ < vert_num) {
+ if (nu->type == CU_BEZIER) {
+ select_beztriple(&nu->bezt[vert_curr], SELECT, SELECT, HIDDEN);
+ }
+ else {
+ select_bpoint(&nu->bp[vert_curr], SELECT, SELECT, HIDDEN);
+ }
+ vert_curr = data[vert_curr].vert_prev;
+ }
+ }
+
+ MEM_freeN(data);
+}
+
+static int edcu_shortest_path_pick_invoke(bContext *C, wmOperator *op, const wmEvent *event)
+{
+ Object *obedit = CTX_data_edit_object(C);
+ Curve *cu = obedit->data;
+ Nurb *nu_src = BKE_curve_nurb_active_get(cu);
+ int vert_src = cu->actvert;
+
+ ViewContext vc;
+ Nurb *nu_dst;
+ BezTriple *bezt_dst;
+ BPoint *bp_dst;
+ int vert_dst;
+ void *vert_dst_p;
+
+ if (vert_src == CU_ACT_NONE) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ view3d_operator_needs_opengl(C);
+ view3d_set_viewcontext(C, &vc);
+
+ if (!ED_curve_pick_vert(&vc, 1, event->mval, &nu_dst, &bezt_dst, &bp_dst, NULL)) {
+ return OPERATOR_PASS_THROUGH;
+ }
+
+ if (nu_src != nu_dst) {
+ BKE_report(op->reports, RPT_ERROR, "Control point belongs to another spline");
+ return OPERATOR_CANCELLED;
+ }
+
+ vert_dst_p = bezt_dst ? (void *)bezt_dst : (void *)bp_dst;
+ vert_dst = BKE_curve_nurb_vert_index_get(nu_dst, vert_dst_p);
+ if (vert_src == vert_dst) {
+ return OPERATOR_CANCELLED;
+ }
+
+ if ((obedit->type == OB_SURF) && (nu_src->pntsv > 1)) {
+ curve_select_shortest_path_surf(nu_src, vert_src, vert_dst);
+ }
+ else {
+ curve_select_shortest_path_curve(nu_src, vert_src, vert_dst);
+ }
+
+ BKE_curve_nurb_vert_active_set(cu, nu_dst, vert_dst_p);
+
+ WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
+ return OPERATOR_FINISHED;
+}
+
+void CURVE_OT_shortest_path_pick(wmOperatorType *ot)
+{
+ /* identifiers */
+ ot->name = "Pick Shortest Path";
+ ot->idname = "CURVE_OT_shortest_path_pick";
+ ot->description = "Select shortest path between two selections";
+
+ /* api callbacks */
+ ot->invoke = edcu_shortest_path_pick_invoke;
+ ot->poll = ED_operator_editsurfcurve_region_view3d;
+
+ /* flags */
+ ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
+}
+
+/** \} */