Blender V2.61 - r43446

loopcut.c

Go to the documentation of this file.
00001 /*
00002  * ***** BEGIN GPL LICENSE BLOCK *****
00003  *
00004  * This program is free software; you can redistribute it and/or
00005  * modify it under the terms of the GNU General Public License
00006  * as published by the Free Software Foundation; either version 2
00007  * of the License, or (at your option) any later version. 
00008  *
00009  * This program is distributed in the hope that it will be useful,
00010  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00011  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00012  * GNU General Public License for more details.
00013  *
00014  * You should have received a copy of the GNU General Public License
00015  * along with this program; if not, write to the Free Software Foundation,
00016  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00017  *
00018  * The Original Code is Copyright (C) 2007 Blender Foundation.
00019  * All rights reserved.
00020  *
00021  * 
00022  * Contributor(s): Joseph Eagar, Joshua Leung
00023  *
00024  * ***** END GPL LICENSE BLOCK *****
00025  */
00026 
00032 #include <float.h>
00033 #include <string.h>
00034 #include <ctype.h>
00035 #include <stdio.h>
00036 
00037 #include "DNA_ID.h"
00038 #include "DNA_object_types.h"
00039 #include "DNA_screen_types.h"
00040 #include "DNA_scene_types.h"
00041 #include "DNA_userdef_types.h"
00042 #include "DNA_windowmanager_types.h"
00043 
00044 #include "MEM_guardedalloc.h"
00045 
00046 #include "PIL_time.h"
00047 
00048 #include "BLI_blenlib.h"
00049 #include "BLI_dynstr.h" /*for WM_operator_pystring */
00050 #include "BLI_editVert.h"
00051 #include "BLI_math.h"
00052 #include "BLI_utildefines.h"
00053 
00054 #include "BKE_blender.h"
00055 #include "BKE_context.h"
00056 #include "BKE_depsgraph.h"
00057 #include "BKE_mesh.h"
00058 #include "BKE_modifier.h"
00059 #include "BKE_report.h"
00060 #include "BKE_scene.h"
00061 #include "BKE_array_mallocn.h"
00062 
00063 #include "BIF_gl.h"
00064 #include "BIF_glutil.h" /* for paint cursor */
00065 
00066 #include "IMB_imbuf_types.h"
00067 
00068 #include "ED_screen.h"
00069 #include "ED_space_api.h"
00070 #include "ED_view3d.h"
00071 #include "ED_mesh.h"
00072 #include "ED_numinput.h"
00073 
00074 #include "RNA_access.h"
00075 #include "RNA_define.h"
00076 
00077 #include "UI_interface.h"
00078 
00079 #include "WM_api.h"
00080 #include "WM_types.h"
00081 
00082 #include "mesh_intern.h"
00083 
00084 /* ringsel operator */
00085 
00086 /* struct for properties used while drawing */
00087 typedef struct tringselOpData {
00088     ARegion *ar;        /* region that ringsel was activated in */
00089     void *draw_handle;  /* for drawing preview loop */
00090     
00091     float (*edges)[2][3];
00092     int totedge;
00093 
00094     ViewContext vc;
00095 
00096     Object *ob;
00097     EditMesh *em;
00098     EditEdge *eed;
00099     NumInput num;
00100 
00101     int extend;
00102     int do_cut;
00103 } tringselOpData;
00104 
00105 /* modal loop selection drawing callback */
00106 static void ringsel_draw(const bContext *C, ARegion *UNUSED(ar), void *arg)
00107 {
00108     View3D *v3d = CTX_wm_view3d(C);
00109     tringselOpData *lcd = arg;
00110     int i;
00111     
00112     if (lcd->totedge > 0) {
00113         if(v3d && v3d->zbuf)
00114             glDisable(GL_DEPTH_TEST);
00115 
00116         glPushMatrix();
00117         glMultMatrixf(lcd->ob->obmat);
00118 
00119         glColor3ub(255, 0, 255);
00120         glBegin(GL_LINES);
00121         for (i=0; i<lcd->totedge; i++) {
00122             glVertex3fv(lcd->edges[i][0]);
00123             glVertex3fv(lcd->edges[i][1]);
00124         }
00125         glEnd();
00126 
00127         glPopMatrix();
00128         if(v3d && v3d->zbuf)
00129             glEnable(GL_DEPTH_TEST);
00130     }
00131 }
00132 
00133 static void edgering_sel(tringselOpData *lcd, int previewlines, int select)
00134 {
00135     EditMesh *em = lcd->em;
00136     EditEdge *startedge = lcd->eed;
00137     EditEdge *eed;
00138     EditFace *efa;
00139     EditVert *v[2][2];
00140     float (*edges)[2][3] = NULL;
00141     V_DYNDECLARE(edges);
00142     float co[2][3];
00143     int looking=1, i, tot=0;
00144     
00145     if (!startedge)
00146         return;
00147 
00148     if (lcd->edges) {
00149         MEM_freeN(lcd->edges);
00150         lcd->edges = NULL;
00151         lcd->totedge = 0;
00152     }
00153 
00154     if (!lcd->extend) {
00155         EM_clear_flag_all(lcd->em, SELECT);
00156     }
00157 
00158     /* in eed->f1 we put the valence (amount of faces in edge) */
00159     /* in eed->f2 we put tagged flag as correct loop */
00160     /* in efa->f1 we put tagged flag as correct to select */
00161 
00162     for(eed= em->edges.first; eed; eed= eed->next) {
00163         eed->f1= 0;
00164         eed->f2= 0;
00165     }
00166 
00167     for(efa= em->faces.first; efa; efa= efa->next) {
00168         efa->f1= 0;
00169         if(efa->h==0) {
00170             efa->e1->f1++;
00171             efa->e2->f1++;
00172             efa->e3->f1++;
00173             if(efa->e4) efa->e4->f1++;
00174         }
00175     }
00176     
00177     // tag startedge OK
00178     startedge->f2= 1;
00179     
00180     while(looking) {
00181         looking= 0;
00182         
00183         for(efa= em->faces.first; efa; efa= efa->next) {
00184             if(efa->e4 && efa->f1==0 && efa->h == 0) {  // not done quad
00185                 if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { // valence ok
00186 
00187                     // if edge tagged, select opposing edge and mark face ok
00188                     if(efa->e1->f2) {
00189                         efa->e3->f2= 1;
00190                         efa->f1= 1;
00191                         looking= 1;
00192                     }
00193                     else if(efa->e2->f2) {
00194                         efa->e4->f2= 1;
00195                         efa->f1= 1;
00196                         looking= 1;
00197                     }
00198                     if(efa->e3->f2) {
00199                         efa->e1->f2= 1;
00200                         efa->f1= 1;
00201                         looking= 1;
00202                     }
00203                     if(efa->e4->f2) {
00204                         efa->e2->f2= 1;
00205                         efa->f1= 1;
00206                         looking= 1;
00207                     }
00208                 }
00209             }
00210         }
00211     }
00212     
00213     if(previewlines > 0 && !select){
00214             for(efa= em->faces.first; efa; efa= efa->next) {
00215                 if(efa->v4 == NULL) {  continue; }
00216                 if(efa->h == 0){
00217                     if(efa->e1->f2 == 1){
00218                         if(efa->e1->h == 1 || efa->e3->h == 1 )
00219                             continue;
00220                         
00221                         v[0][0] = efa->v1;
00222                         v[0][1] = efa->v2;
00223                         v[1][0] = efa->v4;
00224                         v[1][1] = efa->v3;
00225                     } else if(efa->e2->f2 == 1){
00226                         if(efa->e2->h == 1 || efa->e4->h == 1)
00227                             continue;
00228                         v[0][0] = efa->v2;
00229                         v[0][1] = efa->v3;
00230                         v[1][0] = efa->v1;
00231                         v[1][1] = efa->v4;                  
00232                     } else { continue; }
00233                                           
00234                     for(i=1;i<=previewlines;i++){
00235                         co[0][0] = (v[0][1]->co[0] - v[0][0]->co[0])*(i/((float)previewlines+1))+v[0][0]->co[0];
00236                         co[0][1] = (v[0][1]->co[1] - v[0][0]->co[1])*(i/((float)previewlines+1))+v[0][0]->co[1];
00237                         co[0][2] = (v[0][1]->co[2] - v[0][0]->co[2])*(i/((float)previewlines+1))+v[0][0]->co[2];
00238 
00239                         co[1][0] = (v[1][1]->co[0] - v[1][0]->co[0])*(i/((float)previewlines+1))+v[1][0]->co[0];
00240                         co[1][1] = (v[1][1]->co[1] - v[1][0]->co[1])*(i/((float)previewlines+1))+v[1][0]->co[1];
00241                         co[1][2] = (v[1][1]->co[2] - v[1][0]->co[2])*(i/((float)previewlines+1))+v[1][0]->co[2];                    
00242                         
00243                         V_GROW(edges);
00244                         VECCOPY(edges[tot][0], co[0]);
00245                         VECCOPY(edges[tot][1], co[1]);
00246                         tot++;
00247                     }
00248                 }
00249             }
00250     } else {
00251         select = (startedge->f & SELECT) == 0;
00252 
00253         /* select the edges */
00254         for(eed= em->edges.first; eed; eed= eed->next) {
00255             if(eed->f2) EM_select_edge(eed, select);
00256         }
00257     }
00258 
00259     lcd->edges = edges;
00260     lcd->totedge = tot;
00261 }
00262 
00263 static void ringsel_find_edge(tringselOpData *lcd, int cuts)
00264 {
00265     if (lcd->eed) {
00266         edgering_sel(lcd, cuts, 0);
00267     } else if(lcd->edges) {
00268         MEM_freeN(lcd->edges);
00269         lcd->edges = NULL;
00270         lcd->totedge = 0;
00271     }
00272 }
00273 
00274 static void ringsel_finish(bContext *C, wmOperator *op)
00275 {
00276     tringselOpData *lcd= op->customdata;
00277     int cuts= (lcd->do_cut)? RNA_int_get(op->ptr,"number_cuts"): 0;
00278 
00279     if (lcd->eed) {
00280         EditMesh *em = BKE_mesh_get_editmesh(lcd->ob->data);
00281         
00282         edgering_sel(lcd, cuts, 1);
00283         
00284         if (lcd->do_cut) {
00285 
00286             esubdivideflag(lcd->ob, em, SELECT, 0.0f, 0.0f, 0, cuts, 0, SUBDIV_SELECT_LOOPCUT);
00287 
00288             /* force edge slide to edge select mode in in face select mode */
00289             if (em->selectmode & SCE_SELECT_FACE) {
00290                 if (em->selectmode == SCE_SELECT_FACE)
00291                     em->selectmode = SCE_SELECT_EDGE;
00292                 else
00293                     em->selectmode &= ~SCE_SELECT_FACE;
00294                 CTX_data_tool_settings(C)->selectmode= em->selectmode;
00295                 EM_selectmode_set(em);
00296 
00297                 WM_event_add_notifier(C, NC_SCENE|ND_TOOLSETTINGS, CTX_data_scene(C));
00298             }
00299             
00300             DAG_id_tag_update(lcd->ob->data, 0);
00301             WM_event_add_notifier(C, NC_GEOM|ND_DATA, lcd->ob->data);
00302         }
00303         else {
00304             
00305             /* sets as active, useful for other tools */
00306             if(em->selectmode & SCE_SELECT_VERTEX)
00307                 EM_store_selection(em, lcd->eed->v1, EDITVERT);
00308             if(em->selectmode & SCE_SELECT_EDGE)
00309                 EM_store_selection(em, lcd->eed, EDITEDGE);
00310             
00311             EM_selectmode_flush(lcd->em);
00312             WM_event_add_notifier(C, NC_GEOM|ND_SELECT, lcd->ob->data);
00313         }
00314     }
00315 }
00316 
00317 /* called when modal loop selection is done... */
00318 static void ringsel_exit(wmOperator *op)
00319 {
00320     tringselOpData *lcd= op->customdata;
00321 
00322     /* deactivate the extra drawing stuff in 3D-View */
00323     ED_region_draw_cb_exit(lcd->ar->type, lcd->draw_handle);
00324     
00325     if (lcd->edges)
00326         MEM_freeN(lcd->edges);
00327 
00328     ED_region_tag_redraw(lcd->ar);
00329 
00330     /* free the custom data */
00331     MEM_freeN(lcd);
00332     op->customdata= NULL;
00333 }
00334 
00335 /* called when modal loop selection gets set up... */
00336 static int ringsel_init (bContext *C, wmOperator *op, int do_cut)
00337 {
00338     tringselOpData *lcd;
00339     
00340     /* alloc new customdata */
00341     lcd= op->customdata= MEM_callocN(sizeof(tringselOpData), "ringsel Modal Op Data");
00342     
00343     /* assign the drawing handle for drawing preview line... */
00344     lcd->ar= CTX_wm_region(C);
00345     lcd->draw_handle= ED_region_draw_cb_activate(lcd->ar->type, ringsel_draw, lcd, REGION_DRAW_POST_VIEW);
00346     lcd->ob = CTX_data_edit_object(C);
00347     lcd->em= BKE_mesh_get_editmesh((Mesh *)lcd->ob->data);
00348     lcd->extend = do_cut ? 0 : RNA_boolean_get(op->ptr, "extend");
00349     lcd->do_cut = do_cut;
00350     
00351     initNumInput(&lcd->num);
00352     lcd->num.idx_max = 0;
00353     lcd->num.flag |= NUM_NO_NEGATIVE | NUM_NO_FRACTION;
00354     
00355     em_setup_viewcontext(C, &lcd->vc);
00356 
00357     ED_region_tag_redraw(lcd->ar);
00358 
00359     return 1;
00360 }
00361 
00362 static int ringcut_cancel (bContext *UNUSED(C), wmOperator *op)
00363 {
00364     /* this is just a wrapper around exit() */
00365     ringsel_exit(op);
00366     return OPERATOR_CANCELLED;
00367 }
00368 
00369 static int ringsel_invoke (bContext *C, wmOperator *op, wmEvent *evt)
00370 {
00371     tringselOpData *lcd;
00372     EditEdge *edge;
00373     int dist = 75;
00374     
00375     view3d_operator_needs_opengl(C);
00376 
00377     if (!ringsel_init(C, op, 0))
00378         return OPERATOR_CANCELLED;
00379     
00380     lcd = op->customdata;
00381     
00382     if (lcd->em->selectmode == SCE_SELECT_FACE) {
00383         ringsel_exit(op);
00384         WM_operator_name_call(C, "MESH_OT_loop_select", WM_OP_INVOKE_REGION_WIN, NULL);
00385         return OPERATOR_CANCELLED;
00386     }
00387 
00388     lcd->vc.mval[0] = evt->mval[0];
00389     lcd->vc.mval[1] = evt->mval[1];
00390     
00391     edge = findnearestedge(&lcd->vc, &dist);
00392     if(!edge) {
00393         ringsel_exit(op);
00394         return OPERATOR_CANCELLED;
00395     }
00396 
00397     lcd->eed = edge;
00398     ringsel_find_edge(lcd, 1);
00399 
00400     ringsel_finish(C, op);
00401     ringsel_exit(op);
00402 
00403     return OPERATOR_FINISHED;
00404 }
00405 
00406 static int ringcut_invoke (bContext *C, wmOperator *op, wmEvent *evt)
00407 {
00408     Object *obedit= CTX_data_edit_object(C);
00409     tringselOpData *lcd;
00410     EditEdge *edge;
00411     int dist = 75;
00412 
00413     if(modifiers_isDeformedByLattice(obedit) || modifiers_isDeformedByArmature(obedit))
00414         BKE_report(op->reports, RPT_WARNING, "Loop cut doesn't work well on deformed edit mesh display");
00415     
00416     view3d_operator_needs_opengl(C);
00417 
00418     if (!ringsel_init(C, op, 1))
00419         return OPERATOR_CANCELLED;
00420     
00421     /* add a modal handler for this operator - handles loop selection */
00422     WM_event_add_modal_handler(C, op);
00423 
00424     lcd = op->customdata;
00425     lcd->vc.mval[0] = evt->mval[0];
00426     lcd->vc.mval[1] = evt->mval[1];
00427     
00428     edge = findnearestedge(&lcd->vc, &dist);
00429     if (edge != lcd->eed) {
00430         lcd->eed = edge;
00431         ringsel_find_edge(lcd, 1);
00432     }
00433     ED_area_headerprint(CTX_wm_area(C), "Select a ring to be cut, use mouse-wheel or page-up/down for number of cuts");
00434     
00435     return OPERATOR_RUNNING_MODAL;
00436 }
00437 
00438 static int ringcut_modal (bContext *C, wmOperator *op, wmEvent *event)
00439 {
00440     int cuts= RNA_int_get(op->ptr,"number_cuts");
00441     tringselOpData *lcd= op->customdata;
00442 
00443     view3d_operator_needs_opengl(C);
00444 
00445 
00446     switch (event->type) {
00447         case LEFTMOUSE: /* confirm */ // XXX hardcoded
00448             if (event->val == KM_PRESS) {
00449                 /* finish */
00450                 ED_region_tag_redraw(lcd->ar);
00451                 
00452                 ringsel_finish(C, op);
00453                 ringsel_exit(op);
00454                 ED_area_headerprint(CTX_wm_area(C), NULL);
00455                 
00456                 return OPERATOR_FINISHED;
00457             }
00458             
00459             ED_region_tag_redraw(lcd->ar);
00460             break;
00461         case RIGHTMOUSE: /* abort */ // XXX hardcoded
00462         case ESCKEY:
00463             if (event->val == KM_RELEASE) {
00464                 /* cancel */
00465                 ED_region_tag_redraw(lcd->ar);
00466                 ED_area_headerprint(CTX_wm_area(C), NULL);
00467                 
00468                 return ringcut_cancel(C, op);
00469             }
00470             
00471             ED_region_tag_redraw(lcd->ar);
00472             break;
00473         case WHEELUPMOUSE:  /* change number of cuts */
00474         case PADPLUSKEY:
00475         case PAGEUPKEY:
00476             if (event->val == KM_PRESS) {
00477                 cuts++;
00478                 RNA_int_set(op->ptr, "number_cuts",cuts);
00479                 ringsel_find_edge(lcd, cuts);
00480                 
00481                 ED_region_tag_redraw(lcd->ar);
00482             }
00483             break;
00484         case WHEELDOWNMOUSE:  /* change number of cuts */
00485         case PADMINUS:
00486         case PAGEDOWNKEY:
00487             if (event->val == KM_PRESS) {
00488                 cuts=MAX2(cuts-1,1);
00489                 RNA_int_set(op->ptr,"number_cuts",cuts);
00490                 ringsel_find_edge(lcd, cuts);
00491                 
00492                 ED_region_tag_redraw(lcd->ar);
00493             }
00494             break;
00495         case MOUSEMOVE: { /* mouse moved somewhere to select another loop */
00496             int dist = 75;
00497             EditEdge *edge;
00498 
00499             lcd->vc.mval[0] = event->mval[0];
00500             lcd->vc.mval[1] = event->mval[1];
00501             edge = findnearestedge(&lcd->vc, &dist);
00502 
00503             if (edge != lcd->eed) {
00504                 lcd->eed = edge;
00505                 ringsel_find_edge(lcd, cuts);
00506             }
00507 
00508             ED_region_tag_redraw(lcd->ar);
00509             break;
00510         }           
00511     }
00512     
00513     /* using the keyboard to input the number of cuts */
00514     if (event->val==KM_PRESS) {
00515         float value;
00516         
00517         if (handleNumInput(&lcd->num, event))
00518         {
00519             applyNumInput(&lcd->num, &value);
00520             
00521             cuts= CLAMPIS(value, 1, 32);
00522             
00523             RNA_int_set(op->ptr,"number_cuts",cuts);
00524             ringsel_find_edge(lcd, cuts);
00525             
00526             ED_region_tag_redraw(lcd->ar);
00527         }
00528     }
00529     
00530     /* keep going until the user confirms */
00531     return OPERATOR_RUNNING_MODAL;
00532 }
00533 
00534 void MESH_OT_edgering_select (wmOperatorType *ot)
00535 {
00536     /* description */
00537     ot->name= "Edge Ring Select";
00538     ot->idname= "MESH_OT_edgering_select";
00539     ot->description= "Select an edge ring";
00540     
00541     /* callbacks */
00542     ot->invoke= ringsel_invoke;
00543     ot->poll= ED_operator_editmesh_region_view3d; 
00544     
00545     /* flags */
00546     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00547 
00548     RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
00549 }
00550 
00551 void MESH_OT_loopcut (wmOperatorType *ot)
00552 {
00553     /* description */
00554     ot->name= "Loop Cut";
00555     ot->idname= "MESH_OT_loopcut";
00556     ot->description= "Add a new loop between existing loops";
00557     
00558     /* callbacks */
00559     ot->invoke= ringcut_invoke;
00560     ot->modal= ringcut_modal;
00561     ot->cancel= ringcut_cancel;
00562     ot->poll= ED_operator_editmesh_region_view3d;
00563     
00564     /* flags */
00565     ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
00566 
00567     /* properties */
00568     RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);
00569 }