BMesh: Connect path, use select order

Could connect a pair of verts previously,
now connect all vertices along the path, running a second time closes the loop.

Can also be used for without faces to connect edges between selected points.
This commit is contained in:
Campbell Barton 2015-02-06 15:46:38 +11:00
parent e2573aea9b
commit b1dbda143d
Notes: blender-bot 2023-02-14 09:28:33 +01:00
Referenced by issue #43689, OpenGL render: alpha lost with AO or DOF enabled
4 changed files with 191 additions and 4 deletions

View File

@ -2218,7 +2218,8 @@ class VIEW3D_MT_edit_mesh_vertices(Menu):
layout.operator("mesh.rip_edge_move")
layout.operator("mesh.split")
layout.operator_menu_enum("mesh.separate", "type")
layout.operator("mesh.vert_connect", text="Connect")
layout.operator("mesh.vert_connect_path", text="Connect Vertex Path")
layout.operator("mesh.vert_connect", text="Connect Vertices")
layout.operator("transform.vert_slide", text="Slide")
layout.separator()

View File

@ -883,7 +883,6 @@ static int edbm_vert_connect_exec(bContext *C, wmOperator *op)
const int verts_len = bm->totvertsel;
BMVert **verts;
verts = MEM_mallocN(sizeof(*verts) * verts_len, __func__);
{
BMIter iter;
@ -954,7 +953,7 @@ void MESH_OT_vert_connect(wmOperatorType *ot)
/* identifiers */
ot->name = "Vertex Connect";
ot->idname = "MESH_OT_vert_connect";
ot->description = "Connect 2 vertices of a face by an edge, splitting the face in two";
ot->description = "Connect selected vertices of faces, splitting the face";
/* api callbacks */
ot->exec = edbm_vert_connect_exec;
@ -964,6 +963,191 @@ void MESH_OT_vert_connect(wmOperatorType *ot)
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
/**
* check that endpoints are verts and only have a single selected edge connected.
*/
static bool bm_vert_is_select_history_open(BMesh *bm)
{
BMEditSelection *ele_a = bm->selected.first;
BMEditSelection *ele_b = bm->selected.last;
if ((ele_a->htype == BM_VERT) &&
(ele_b->htype == BM_VERT))
{
if ((BM_iter_elem_count_flag(BM_EDGES_OF_VERT, (BMVert *)ele_a->ele, BM_ELEM_SELECT, true) == 1) &&
(BM_iter_elem_count_flag(BM_EDGES_OF_VERT, (BMVert *)ele_b->ele, BM_ELEM_SELECT, true) == 1))
{
return true;
}
}
return false;
}
static bool bm_vert_connect_pair(BMesh *bm, BMVert *v_a, BMVert *v_b)
{
BMOperator bmop;
BMVert **verts;
const int totedge_orig = bm->totedge;
BMO_op_init(bm, &bmop, BMO_FLAG_DEFAULTS, "connect_vert_pair");
verts = BMO_slot_buffer_alloc(&bmop, bmop.slots_in, "verts", 2);
verts[0] = v_a;
verts[1] = v_b;
BM_vert_normal_update(verts[0]);
BM_vert_normal_update(verts[1]);
BMO_op_exec(bm, &bmop);
BMO_slot_buffer_hflag_enable(bm, bmop.slots_out, "edges.out", BM_EDGE, BM_ELEM_SELECT, true);
BMO_op_finish(bm, &bmop);
return (bm->totedge != totedge_orig);
}
static bool bm_vert_connect_select_history(BMesh *bm)
{
/* Logic is as follows:
*
* - If there are any isolated/wire verts - connect as edges.
* - Otherwise connect faces.
* - If all edges have been created already, closed the loop.
*/
if (BLI_listbase_count_ex(&bm->selected, 2) == 2 && (bm->totvertsel > 2)) {
BMEditSelection *ese;
int tot = 0;
bool changed = false;
bool has_wire = false;
// bool all_verts;
/* ensure all verts have history */
for (ese = bm->selected.first; ese; ese = ese->next, tot++) {
BMVert *v;
if (ese->htype != BM_VERT) {
break;
}
v = (BMVert *)ese->ele;
if ((has_wire == false) && ((v->e == NULL) || BM_vert_is_wire(v))) {
has_wire = true;
}
}
// all_verts = (ese == NULL);
if (has_wire == false) {
/* all verts have faces , connect verts via faces! */
if (tot == bm->totvertsel) {
BMEditSelection *ese_last;
ese_last = bm->selected.first;
ese = ese_last->next;
do {
if (BM_edge_exists((BMVert *)ese_last->ele, (BMVert *)ese->ele)) {
/* pass, edge exists (and will be selected) */
}
else {
changed |= bm_vert_connect_pair(bm, (BMVert *)ese_last->ele, (BMVert *)ese->ele);
}
} while ((ese_last = ese),
(ese = ese->next));
if (changed) {
return true;
}
}
if (changed == false) {
/* existing loops: close the selection */
if (bm_vert_is_select_history_open(bm)) {
changed |= bm_vert_connect_pair(
bm,
(BMVert *)((BMEditSelection *)bm->selected.first)->ele,
(BMVert *)((BMEditSelection *)bm->selected.last)->ele);
if (changed) {
return true;
}
}
}
}
else {
/* no faces, simply connect the verts by edges */
BMEditSelection *ese_prev;
ese_prev = bm->selected.first;
ese = ese_prev->next;
do {
if (BM_edge_exists((BMVert *)ese_prev->ele, (BMVert *)ese->ele)) {
/* pass, edge exists (and will be selected) */
}
else {
BMEdge *e;
e = BM_edge_create(bm, (BMVert *)ese_prev->ele, (BMVert *)ese->ele, NULL, 0);
BM_edge_select_set(bm, e, true);
changed = true;
}
} while ((ese_prev = ese),
(ese = ese->next));
if (changed == false) {
/* existing loops: close the selection */
if (bm_vert_is_select_history_open(bm)) {
BMEdge *e;
ese_prev = bm->selected.first;
ese = bm->selected.last;
e = BM_edge_create(bm, (BMVert *)ese_prev->ele, (BMVert *)ese->ele, NULL, 0);
BM_edge_select_set(bm, e, true);
}
}
return true;
}
}
return false;
}
static int edbm_vert_connect_path_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);
BMEditMesh *em = BKE_editmesh_from_object(obedit);
bool is_pair = (em->bm->totvertsel == 2);
/* when there is only 2 vertices, we can ignore selection order */
if (is_pair) {
return edbm_vert_connect_exec(C, op);
}
if (bm_vert_connect_select_history(em->bm)) {
EDBM_selectmode_flush(em);
EDBM_update_generic(em, true, true);
return OPERATOR_FINISHED;
}
else {
BKE_report(op->reports, RPT_ERROR, "Invalid selection order");
return OPERATOR_CANCELLED;
}
}
void MESH_OT_vert_connect_path(wmOperatorType *ot)
{
/* identifiers */
ot->name = "Vertex Connect Path";
ot->idname = "MESH_OT_vert_connect_path";
ot->description = "Connect vertices by their selection order, creating edges, splitting faces";
/* api callbacks */
ot->exec = edbm_vert_connect_path_exec;
ot->poll = ED_operator_editmesh;
/* flags */
ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO;
}
static int edbm_vert_connect_concave_exec(bContext *C, wmOperator *op)
{
Object *obedit = CTX_data_edit_object(C);

View File

@ -175,6 +175,7 @@ void MESH_OT_normals_make_consistent(struct wmOperatorType *ot);
void MESH_OT_vertices_smooth(struct wmOperatorType *ot);
void MESH_OT_vertices_smooth_laplacian(struct wmOperatorType *ot);
void MESH_OT_vert_connect(struct wmOperatorType *ot);
void MESH_OT_vert_connect_path(struct wmOperatorType *ot);
void MESH_OT_vert_connect_concave(struct wmOperatorType *ot);
void MESH_OT_vert_connect_nonplanar(struct wmOperatorType *ot);
void MESH_OT_edge_split(struct wmOperatorType *ot);

View File

@ -163,6 +163,7 @@ void ED_operatortypes_mesh(void)
WM_operatortype_append(MESH_OT_solidify);
WM_operatortype_append(MESH_OT_select_nth);
WM_operatortype_append(MESH_OT_vert_connect);
WM_operatortype_append(MESH_OT_vert_connect_path);
WM_operatortype_append(MESH_OT_vert_connect_concave);
WM_operatortype_append(MESH_OT_vert_connect_nonplanar);
WM_operatortype_append(MESH_OT_knife_tool);
@ -401,7 +402,7 @@ void ED_keymap_mesh(wmKeyConfig *keyconf)
WM_keymap_add_item(keymap, "MESH_OT_separate", PKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "MESH_OT_split", YKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "MESH_OT_vert_connect", JKEY, KM_PRESS, 0, 0);
WM_keymap_add_item(keymap, "MESH_OT_vert_connect_path", JKEY, KM_PRESS, 0, 0);
/* Vertex Slide */
WM_keymap_add_item(keymap, "TRANSFORM_OT_vert_slide", VKEY, KM_PRESS, KM_SHIFT, 0);