2029 lines
47 KiB
C
2029 lines
47 KiB
C
/*
|
|
* 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.
|
|
*/
|
|
|
|
/** \file
|
|
* \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_simple.h"
|
|
#include "BLI_kdtree.h"
|
|
|
|
#include "BKE_context.h"
|
|
#include "BKE_curve.h"
|
|
#include "BKE_fcurve.h"
|
|
#include "BKE_layer.h"
|
|
#include "BKE_report.h"
|
|
#include "BKE_object.h"
|
|
|
|
#include "WM_api.h"
|
|
#include "WM_types.h"
|
|
|
|
#include "ED_object.h"
|
|
#include "ED_screen.h"
|
|
#include "ED_select_utils.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"
|
|
|
|
#include "DEG_depsgraph.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(View3D *v3d, 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(v3d, 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(View3D *v3d, 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(v3d, 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;
|
|
}
|
|
|
|
bool ED_curve_nurb_select_all(const Nurb *nu)
|
|
{
|
|
bool changed = false;
|
|
int i;
|
|
if (nu->bezt) {
|
|
BezTriple *bezt;
|
|
for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
|
|
if (bezt->hide == 0) {
|
|
if (BEZT_ISSEL_ALL(bezt) == false) {
|
|
BEZT_SEL_ALL(bezt);
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else if (nu->bp) {
|
|
BPoint *bp;
|
|
for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
|
|
if (bp->hide == 0) {
|
|
if ((bp->f1 & SELECT) == 0) {
|
|
bp->f1 |= SELECT;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
bool ED_curve_select_all(EditNurb *editnurb)
|
|
{
|
|
bool changed = false;
|
|
for (Nurb *nu = editnurb->nurbs.first; nu; nu = nu->next) {
|
|
changed |= ED_curve_nurb_select_all(nu);
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
bool ED_curve_nurb_deselect_all(const Nurb *nu)
|
|
{
|
|
bool changed = false;
|
|
int i;
|
|
if (nu->bezt) {
|
|
BezTriple *bezt;
|
|
for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
|
|
if (BEZT_ISSEL_ANY(bezt)) {
|
|
BEZT_DESEL_ALL(bezt);
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
else if (nu->bp) {
|
|
BPoint *bp;
|
|
for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
|
|
if (bp->f1 & SELECT) {
|
|
bp->f1 &= ~SELECT;
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
int ED_curve_select_count(View3D *v3d, struct EditNurb *editnurb)
|
|
{
|
|
int sel = 0;
|
|
Nurb *nu;
|
|
|
|
for (nu = editnurb->nurbs.first; nu; nu = nu->next) {
|
|
sel += ED_curve_nurb_select_count(v3d, nu);
|
|
}
|
|
|
|
return sel;
|
|
}
|
|
|
|
bool ED_curve_select_check(View3D *v3d, struct EditNurb *editnurb)
|
|
{
|
|
Nurb *nu;
|
|
|
|
for (nu = editnurb->nurbs.first; nu; nu = nu->next) {
|
|
if (ED_curve_nurb_select_check(v3d, nu)) {
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
bool ED_curve_deselect_all(EditNurb *editnurb)
|
|
{
|
|
bool changed = false;
|
|
for (Nurb *nu = editnurb->nurbs.first; nu; nu = nu->next) {
|
|
changed |= ED_curve_nurb_deselect_all(nu);
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
bool ED_curve_deselect_all_multi_ex(Base **bases, int bases_len)
|
|
{
|
|
bool changed_multi = false;
|
|
for (uint base_index = 0; base_index < bases_len; base_index++) {
|
|
Object *obedit = bases[base_index]->object;
|
|
Curve *cu = obedit->data;
|
|
changed_multi |= ED_curve_deselect_all(cu->editnurb);
|
|
DEG_id_tag_update(&cu->id, ID_RECALC_SELECT);
|
|
}
|
|
return changed_multi;
|
|
}
|
|
|
|
bool ED_curve_deselect_all_multi(struct bContext *C)
|
|
{
|
|
ViewContext vc;
|
|
ED_view3d_viewcontext_init(C, &vc);
|
|
uint bases_len = 0;
|
|
Base **bases = BKE_view_layer_array_from_bases_in_edit_mode_unique_data(vc.view_layer, vc.v3d, &bases_len);
|
|
bool changed_multi = ED_curve_deselect_all_multi_ex(bases, bases_len);
|
|
MEM_freeN(bases);
|
|
return changed_multi;
|
|
}
|
|
|
|
bool ED_curve_select_swap(EditNurb *editnurb, bool hide_handles)
|
|
{
|
|
Nurb *nu;
|
|
BPoint *bp;
|
|
BezTriple *bezt;
|
|
int a;
|
|
bool changed = false;
|
|
|
|
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;
|
|
}
|
|
changed = true;
|
|
}
|
|
bezt++;
|
|
}
|
|
}
|
|
else {
|
|
bp = nu->bp;
|
|
a = nu->pntsu * nu->pntsv;
|
|
while (a--) {
|
|
if (bp->hide == 0) {
|
|
swap_selection_bpoint(bp);
|
|
changed = true;
|
|
}
|
|
bp++;
|
|
}
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
/**
|
|
* \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 == false) && (bezt->hide == 0) && ((bezt->f2 & SELECT) || (selstatus == DESELECT))) {
|
|
bezt += next;
|
|
if (!(bezt->f2 & SELECT) || (selstatus == DESELECT)) {
|
|
bool sel = select_beztriple(bezt, selstatus, SELECT, VISIBLE);
|
|
if (sel && !cont) {
|
|
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 == false) && (bp->hide == 0) && ((bp->f1 & SELECT) || (selstatus == DESELECT))) {
|
|
bp += next;
|
|
if (!(bp->f1 & SELECT) || (selstatus == DESELECT)) {
|
|
bool sel = select_bpoint(bp, selstatus, SELECT, VISIBLE);
|
|
if (sel && !cont) {
|
|
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))
|
|
{
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
selectend_nurb(obedit, FIRST, true, DESELECT);
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
BKE_curve_nurb_vert_active_validate(obedit->data);
|
|
}
|
|
MEM_freeN(objects);
|
|
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))
|
|
{
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
selectend_nurb(obedit, LAST, true, DESELECT);
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
BKE_curve_nurb_vert_active_validate(obedit->data);
|
|
}
|
|
|
|
MEM_freeN(objects);
|
|
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)
|
|
{
|
|
int action = RNA_enum_get(op->ptr, "action");
|
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
View3D *v3d = CTX_wm_view3d(C);
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
if (action == SEL_TOGGLE) {
|
|
action = SEL_SELECT;
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
Curve *cu = obedit->data;
|
|
|
|
if (ED_curve_select_check(v3d, cu->editnurb)) {
|
|
action = SEL_DESELECT;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
Curve *cu = obedit->data;
|
|
bool changed = false;
|
|
|
|
switch (action) {
|
|
case SEL_SELECT:
|
|
changed = ED_curve_select_all(cu->editnurb);
|
|
break;
|
|
case SEL_DESELECT:
|
|
changed = ED_curve_deselect_all(cu->editnurb);
|
|
break;
|
|
case SEL_INVERT:
|
|
changed = ED_curve_select_swap(cu->editnurb, (v3d->overlay.edit_flag & V3D_OVERLAY_EDIT_CU_HANDLES) == 0);
|
|
break;
|
|
}
|
|
|
|
if (changed) {
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
BKE_curve_nurb_vert_active_validate(cu);
|
|
}
|
|
}
|
|
|
|
MEM_freeN(objects);
|
|
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))
|
|
{
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
View3D *v3d = CTX_wm_view3d(C);
|
|
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
Curve *cu = obedit->data;
|
|
EditNurb *editnurb = cu->editnurb;
|
|
ListBase *nurbs = &editnurb->nurbs;
|
|
Nurb *nu;
|
|
bool changed = false;
|
|
|
|
for (nu = nurbs->first; nu; nu = nu->next) {
|
|
if (ED_curve_nurb_select_check(v3d, nu)) {
|
|
changed |= ED_curve_nurb_select_all(nu);
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
}
|
|
}
|
|
MEM_freeN(objects);
|
|
|
|
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 the current selection";
|
|
|
|
/* 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)
|
|
{
|
|
ViewContext vc;
|
|
Nurb *nu;
|
|
BezTriple *bezt;
|
|
BPoint *bp;
|
|
int a;
|
|
const bool select = !RNA_boolean_get(op->ptr, "deselect");
|
|
Base *basact = NULL;
|
|
|
|
view3d_operator_needs_opengl(C);
|
|
ED_view3d_viewcontext_init(C, &vc);
|
|
copy_v2_v2_int(vc.mval, event->mval);
|
|
|
|
if (!ED_curve_pick_vert(&vc, 1, &nu, &bezt, &bp, NULL, &basact)) {
|
|
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++;
|
|
}
|
|
}
|
|
|
|
Object *obedit = basact->object;
|
|
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
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);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
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))
|
|
{
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
ListBase *editnurb = object_editcurve_get(obedit);
|
|
select_adjacent_cp(editnurb, 1, 0, SELECT);
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
}
|
|
MEM_freeN(objects);
|
|
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))
|
|
{
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
ListBase *editnurb = object_editcurve_get(obedit);
|
|
select_adjacent_cp(editnurb, -1, 0, SELECT);
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
}
|
|
MEM_freeN(objects);
|
|
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 void curve_select_more(Object *obedit)
|
|
{
|
|
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);
|
|
}
|
|
}
|
|
|
|
static int curve_select_more_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
curve_select_more(obedit);
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
}
|
|
MEM_freeN(objects);
|
|
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 = curve_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 void curve_select_less(Object *obedit)
|
|
{
|
|
ListBase *editnurb = object_editcurve_get(obedit);
|
|
Nurb *nu;
|
|
BPoint *bp;
|
|
BezTriple *bezt;
|
|
int a;
|
|
int sel = 0;
|
|
bool 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 == false) && (bp->hide == 0) && (bp->f1 & SELECT)) {
|
|
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++;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static int curve_select_less_exec(bContext *C, wmOperator *UNUSED(op))
|
|
{
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
curve_select_less(obedit);
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
}
|
|
MEM_freeN(objects);
|
|
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 = curve_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, int seed, bool select)
|
|
{
|
|
Nurb *nu;
|
|
BezTriple *bezt;
|
|
BPoint *bp;
|
|
int a;
|
|
|
|
RNG *rng = BLI_rng_new_srandom(seed);
|
|
|
|
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_rng_get_float(rng) < randfac) {
|
|
select_beztriple(bezt, select, SELECT, VISIBLE);
|
|
}
|
|
}
|
|
bezt++;
|
|
}
|
|
}
|
|
else {
|
|
bp = nu->bp;
|
|
a = nu->pntsu * nu->pntsv;
|
|
|
|
while (a--) {
|
|
if (!bp->hide) {
|
|
if (BLI_rng_get_float(rng) < randfac) {
|
|
select_bpoint(bp, select, SELECT, VISIBLE);
|
|
}
|
|
}
|
|
bp++;
|
|
}
|
|
}
|
|
}
|
|
|
|
BLI_rng_free(rng);
|
|
}
|
|
|
|
static int curve_select_random_exec(bContext *C, wmOperator *op)
|
|
{
|
|
const bool select = (RNA_enum_get(op->ptr, "action") == SEL_SELECT);
|
|
const float randfac = RNA_float_get(op->ptr, "percent") / 100.0f;
|
|
const int seed = WM_operator_properties_select_random_seed_increment_get(op);
|
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
ListBase *editnurb = object_editcurve_get(obedit);
|
|
int seed_iter = seed;
|
|
|
|
/* This gives a consistent result regardless of object order. */
|
|
if (ob_index) {
|
|
seed_iter += BLI_ghashutil_strhash_p(obedit->id.name);
|
|
}
|
|
|
|
curve_select_random(editnurb, randfac, seed_iter, select);
|
|
BKE_curve_nurb_vert_active_validate(obedit->data);
|
|
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
}
|
|
|
|
MEM_freeN(objects);
|
|
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 */
|
|
WM_operator_properties_select_random(ot);
|
|
}
|
|
|
|
/********************* every nth number of point *******************/
|
|
|
|
static void select_nth_bezt(Nurb *nu, BezTriple *bezt, const struct CheckerIntervalParams *params)
|
|
{
|
|
int a, start;
|
|
|
|
start = bezt - nu->bezt;
|
|
a = nu->pntsu;
|
|
bezt = &nu->bezt[a - 1];
|
|
|
|
while (a--) {
|
|
const int depth = abs(start - a);
|
|
if (WM_operator_properties_checker_interval_test(params, depth)) {
|
|
select_beztriple(bezt, DESELECT, SELECT, HIDDEN);
|
|
}
|
|
|
|
bezt--;
|
|
}
|
|
}
|
|
|
|
static void select_nth_bp(Nurb *nu, BPoint *bp, const struct CheckerIntervalParams *params)
|
|
{
|
|
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 (WM_operator_properties_checker_interval_test(params, depth)) {
|
|
select_bpoint(bp, DESELECT, SELECT, HIDDEN);
|
|
}
|
|
|
|
pnt--;
|
|
if (pnt < 0) {
|
|
pnt = nu->pntsu - 1;
|
|
row--;
|
|
}
|
|
|
|
bp--;
|
|
}
|
|
}
|
|
|
|
static bool ed_curve_select_nth(Curve *cu, const struct CheckerIntervalParams *params)
|
|
{
|
|
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, params);
|
|
}
|
|
else {
|
|
select_nth_bp(nu, vert, params);
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static int select_nth_exec(bContext *C, wmOperator *op)
|
|
{
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
Object *obact = CTX_data_edit_object(C);
|
|
View3D *v3d = CTX_wm_view3d(C);
|
|
bool changed = false;
|
|
|
|
struct CheckerIntervalParams op_params;
|
|
WM_operator_properties_checker_interval_from_op(op, &op_params);
|
|
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
Curve *cu = obedit->data;
|
|
|
|
if (!ED_curve_select_check(v3d, cu->editnurb)) {
|
|
continue;
|
|
}
|
|
|
|
if (ed_curve_select_nth(obedit->data, &op_params) == true) {
|
|
changed = true;
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
}
|
|
}
|
|
MEM_freeN(objects);
|
|
|
|
if (!changed) {
|
|
if (obact->type == OB_SURF) {
|
|
BKE_report(op->reports, RPT_ERROR,
|
|
(objects_len == 1 ?
|
|
"Surface has no active point" :
|
|
"Surfaces have no active point"));
|
|
}
|
|
else {
|
|
BKE_report(op->reports, RPT_ERROR,
|
|
(objects_len == 1 ?
|
|
"Curve has no active point" :
|
|
"Curves have no active point"));
|
|
}
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
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;
|
|
|
|
WM_operator_properties_checker_interval(ot, false);
|
|
}
|
|
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/* Select Similar */
|
|
|
|
/** \name Select Similar
|
|
* \{ */
|
|
|
|
static const 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 const 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 void nurb_bezt_direction_worldspace_get(Object *ob, Nurb *nu, BezTriple *bezt, float r_dir[3])
|
|
{
|
|
float rsmat[3][3];
|
|
BKE_nurb_bezt_calc_normal(nu, bezt, r_dir);
|
|
copy_m3_m4(rsmat, ob->obmat);
|
|
mul_m3_v3(rsmat, r_dir);
|
|
normalize_v3(r_dir);
|
|
}
|
|
|
|
static void nurb_bpoint_direction_worldspace_get(Object *ob, Nurb *nu, BPoint *bp, float r_dir[3])
|
|
{
|
|
float rsmat[3][3];
|
|
BKE_nurb_bpoint_calc_normal(nu, bp, r_dir);
|
|
copy_m3_m4(rsmat, ob->obmat);
|
|
mul_m3_v3(rsmat, r_dir);
|
|
normalize_v3(r_dir);
|
|
}
|
|
|
|
static void curve_nurb_selected_type_get(
|
|
Object *ob, Nurb *nu, const int type, KDTree_1d *tree_1d, KDTree_3d *tree_3d)
|
|
{
|
|
float tree_entry[3] = {0.0f, 0.0f, 0.0f};
|
|
|
|
if (nu->type == CU_BEZIER) {
|
|
BezTriple *bezt;
|
|
int i;
|
|
int tree_index = 0;
|
|
|
|
for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
|
|
if ((!bezt->hide) && (bezt->f1 & SELECT)) {
|
|
|
|
switch (type) {
|
|
case SIMCURHAND_RADIUS:
|
|
{
|
|
float radius_ref = bezt->radius;
|
|
tree_entry[0] = radius_ref;
|
|
break;
|
|
}
|
|
case SIMCURHAND_WEIGHT:
|
|
{
|
|
float weight_ref = bezt->weight;
|
|
tree_entry[0] = weight_ref;
|
|
break;
|
|
}
|
|
case SIMCURHAND_DIRECTION:
|
|
{
|
|
nurb_bezt_direction_worldspace_get(ob, nu, bezt, tree_entry);
|
|
break;
|
|
}
|
|
}
|
|
if (tree_1d) {
|
|
BLI_kdtree_1d_insert(tree_1d, tree_index++, tree_entry);
|
|
}
|
|
else {
|
|
BLI_kdtree_3d_insert(tree_3d, tree_index++, tree_entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
BPoint *bp;
|
|
int i;
|
|
int tree_index = 0;
|
|
|
|
for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
|
|
if (!bp->hide && bp->f1 & SELECT) {
|
|
switch (type) {
|
|
case SIMCURHAND_RADIUS:
|
|
{
|
|
float radius_ref = bp->radius;
|
|
tree_entry[0] = radius_ref;
|
|
break;
|
|
}
|
|
case SIMCURHAND_WEIGHT:
|
|
{
|
|
float weight_ref = bp->weight;
|
|
tree_entry[0] = weight_ref;
|
|
break;
|
|
}
|
|
case SIMCURHAND_DIRECTION:
|
|
{
|
|
nurb_bpoint_direction_worldspace_get(ob, nu, bp, tree_entry);
|
|
break;
|
|
}
|
|
}
|
|
if (tree_1d) {
|
|
BLI_kdtree_1d_insert(tree_1d, tree_index++, tree_entry);
|
|
}
|
|
else {
|
|
BLI_kdtree_3d_insert(tree_3d, tree_index++, tree_entry);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
static bool curve_nurb_select_similar_type(
|
|
Object *ob, Nurb *nu, const int type,
|
|
const KDTree_1d *tree_1d, const KDTree_3d *tree_3d,
|
|
const float thresh, const int compare)
|
|
{
|
|
const float thresh_cos = cosf(thresh * (float)M_PI_2);
|
|
bool changed = false;
|
|
|
|
if (nu->type == CU_BEZIER) {
|
|
BezTriple *bezt;
|
|
int i;
|
|
|
|
for (i = nu->pntsu, bezt = nu->bezt; i--; bezt++) {
|
|
if (!bezt->hide) {
|
|
bool select = false;
|
|
|
|
switch (type) {
|
|
case SIMCURHAND_RADIUS:
|
|
{
|
|
float radius_ref = bezt->radius;
|
|
if (ED_select_similar_compare_float_tree(tree_1d, radius_ref, thresh, compare)) {
|
|
select = true;
|
|
}
|
|
break;
|
|
}
|
|
case SIMCURHAND_WEIGHT:
|
|
{
|
|
float weight_ref = bezt->weight;
|
|
if (ED_select_similar_compare_float_tree(tree_1d, weight_ref, thresh, compare)) {
|
|
select = true;
|
|
}
|
|
break;
|
|
}
|
|
case SIMCURHAND_DIRECTION:
|
|
{
|
|
float dir[3];
|
|
nurb_bezt_direction_worldspace_get(ob, nu, bezt, dir);
|
|
KDTreeNearest_3d nearest;
|
|
if (BLI_kdtree_3d_find_nearest(tree_3d, dir, &nearest) != -1) {
|
|
float orient = angle_normalized_v3v3(dir, nearest.co);
|
|
float delta = thresh_cos - fabsf(cosf(orient));
|
|
if (ED_select_similar_compare_float(delta, thresh, compare)) {
|
|
select = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (select) {
|
|
select_beztriple(bezt, SELECT, SELECT, VISIBLE);
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
BPoint *bp;
|
|
int i;
|
|
|
|
for (i = nu->pntsu * nu->pntsv, bp = nu->bp; i--; bp++) {
|
|
if (!bp->hide) {
|
|
bool select = false;
|
|
|
|
switch (type) {
|
|
case SIMCURHAND_RADIUS:
|
|
{
|
|
float radius_ref = bp->radius;
|
|
if (ED_select_similar_compare_float_tree(tree_1d, radius_ref, thresh, compare)) {
|
|
select = true;
|
|
}
|
|
break;
|
|
}
|
|
case SIMCURHAND_WEIGHT:
|
|
{
|
|
float weight_ref = bp->weight;
|
|
if (ED_select_similar_compare_float_tree(tree_1d, weight_ref, thresh, compare)) {
|
|
select = true;
|
|
}
|
|
break;
|
|
}
|
|
case SIMCURHAND_DIRECTION:
|
|
{
|
|
float dir[3];
|
|
nurb_bpoint_direction_worldspace_get(ob, nu, bp, dir);
|
|
KDTreeNearest_3d nearest;
|
|
if (BLI_kdtree_3d_find_nearest(tree_3d, dir, &nearest) != -1) {
|
|
float orient = angle_normalized_v3v3(dir, nearest.co);
|
|
float delta = fabsf(cosf(orient)) - thresh_cos;
|
|
if (ED_select_similar_compare_float(delta, thresh, compare)) {
|
|
select = true;
|
|
}
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (select) {
|
|
select_bpoint(bp, SELECT, SELECT, VISIBLE);
|
|
changed = true;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return changed;
|
|
}
|
|
|
|
static int curve_select_similar_exec(bContext *C, wmOperator *op)
|
|
{
|
|
/* Get props. */
|
|
const int optype = RNA_enum_get(op->ptr, "type");
|
|
const float thresh = RNA_float_get(op->ptr, "threshold");
|
|
const int compare = RNA_enum_get(op->ptr, "compare");
|
|
|
|
ViewLayer *view_layer = CTX_data_view_layer(C);
|
|
View3D *v3d = CTX_wm_view3d(C);
|
|
int tot_nurbs_selected_all = 0;
|
|
uint objects_len = 0;
|
|
Object **objects = BKE_view_layer_array_from_objects_in_edit_mode_unique_data(view_layer, CTX_wm_view3d(C), &objects_len);
|
|
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
Curve *cu = obedit->data;
|
|
tot_nurbs_selected_all += ED_curve_select_count(v3d, cu->editnurb);
|
|
}
|
|
|
|
if (tot_nurbs_selected_all == 0) {
|
|
BKE_report(op->reports, RPT_ERROR, "No control point selected");
|
|
MEM_freeN(objects);
|
|
return OPERATOR_CANCELLED;
|
|
}
|
|
|
|
KDTree_1d *tree_1d = NULL;
|
|
KDTree_3d *tree_3d = NULL;
|
|
short type_ref = 0;
|
|
|
|
switch (optype) {
|
|
case SIMCURHAND_RADIUS:
|
|
case SIMCURHAND_WEIGHT:
|
|
tree_1d = BLI_kdtree_1d_new(tot_nurbs_selected_all);
|
|
break;
|
|
case SIMCURHAND_DIRECTION:
|
|
tree_3d = BLI_kdtree_3d_new(tot_nurbs_selected_all);
|
|
break;
|
|
}
|
|
|
|
/* Get type of selected control points. */
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
Curve *cu = obedit->data;
|
|
EditNurb *editnurb = cu->editnurb;
|
|
|
|
Nurb *nu;
|
|
for (nu = editnurb->nurbs.first; nu; nu = nu->next) {
|
|
if (!ED_curve_nurb_select_check(v3d, nu)) {
|
|
continue;
|
|
}
|
|
switch (optype) {
|
|
case SIMCURHAND_TYPE:
|
|
{
|
|
type_ref |= nu->type;
|
|
break;
|
|
}
|
|
case SIMCURHAND_RADIUS:
|
|
case SIMCURHAND_WEIGHT:
|
|
case SIMCURHAND_DIRECTION:
|
|
curve_nurb_selected_type_get(obedit, nu, optype, tree_1d, tree_3d);
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (tree_1d != NULL) {
|
|
BLI_kdtree_1d_deduplicate(tree_1d);
|
|
BLI_kdtree_1d_balance(tree_1d);
|
|
}
|
|
if (tree_3d != NULL) {
|
|
BLI_kdtree_3d_deduplicate(tree_3d);
|
|
BLI_kdtree_3d_balance(tree_3d);
|
|
}
|
|
|
|
/* Select control points with desired type. */
|
|
for (uint ob_index = 0; ob_index < objects_len; ob_index++) {
|
|
Object *obedit = objects[ob_index];
|
|
Curve *cu = obedit->data;
|
|
EditNurb *editnurb = cu->editnurb;
|
|
bool changed = false;
|
|
Nurb *nu;
|
|
|
|
for (nu = editnurb->nurbs.first; nu; nu = nu->next) {
|
|
switch (optype) {
|
|
case SIMCURHAND_TYPE:
|
|
{
|
|
if (nu->type & type_ref) {
|
|
changed |= ED_curve_nurb_select_all(nu);
|
|
}
|
|
break;
|
|
}
|
|
case SIMCURHAND_RADIUS:
|
|
case SIMCURHAND_WEIGHT:
|
|
case SIMCURHAND_DIRECTION:
|
|
changed = curve_nurb_select_similar_type(
|
|
obedit, nu, optype, tree_1d, tree_3d, thresh, compare);
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (changed) {
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data);
|
|
}
|
|
}
|
|
|
|
MEM_freeN(objects);
|
|
|
|
if (tree_1d != NULL) {
|
|
BLI_kdtree_1d_free(tree_1d);
|
|
}
|
|
if (tree_3d != NULL) {
|
|
BLI_kdtree_3d_free(tree_3d);
|
|
}
|
|
return OPERATOR_FINISHED;
|
|
|
|
}
|
|
|
|
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)
|
|
{
|
|
HeapSimple *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_heapsimple_new();
|
|
|
|
vert_curr = data[vert_src].vert;
|
|
BLI_heapsimple_insert(heap, 0.0f, &data[vert_src].vert);
|
|
data[vert_src].cost = 0.0f;
|
|
data[vert_src].vert_prev = vert_src; /* nop */
|
|
|
|
while (!BLI_heapsimple_is_empty(heap)) {
|
|
int axis, sign;
|
|
int u, v;
|
|
|
|
vert_curr = *((int *)BLI_heapsimple_pop_min(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_heapsimple_insert(heap, data[vert_other].cost, &data[vert_other].vert);
|
|
}
|
|
data[vert_other].vert_prev = vert_curr;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
BLI_heapsimple_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)
|
|
{
|
|
ViewContext vc;
|
|
Nurb *nu_dst;
|
|
BezTriple *bezt_dst;
|
|
BPoint *bp_dst;
|
|
int vert_dst;
|
|
void *vert_dst_p;
|
|
Base *basact = NULL;
|
|
|
|
view3d_operator_needs_opengl(C);
|
|
ED_view3d_viewcontext_init(C, &vc);
|
|
copy_v2_v2_int(vc.mval, event->mval);
|
|
|
|
if (!ED_curve_pick_vert(&vc, 1, &nu_dst, &bezt_dst, &bp_dst, NULL, &basact)) {
|
|
return OPERATOR_PASS_THROUGH;
|
|
}
|
|
|
|
ED_view3d_viewcontext_init_object(&vc, basact->object);
|
|
Object *obedit = basact->object;
|
|
Curve *cu = obedit->data;
|
|
Nurb *nu_src = BKE_curve_nurb_active_get(cu);
|
|
int vert_src = cu->actvert;
|
|
|
|
if (vert_src == CU_ACT_NONE) {
|
|
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);
|
|
|
|
if (vc.view_layer->basact != basact) {
|
|
ED_object_base_activate(C, basact);
|
|
}
|
|
|
|
DEG_id_tag_update(obedit->data, ID_RECALC_SELECT);
|
|
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;
|
|
}
|
|
|
|
/** \} */
|