Blender  V2.59
loopcut.c
Go to the documentation of this file.
00001 /*
00002  * $Id: loopcut.c 36490 2011-05-04 20:34:30Z campbellbarton $
00003  *
00004  * ***** BEGIN GPL LICENSE BLOCK *****
00005  *
00006  * This program is free software; you can redistribute it and/or
00007  * modify it under the terms of the GNU General Public License
00008  * as published by the Free Software Foundation; either version 2
00009  * of the License, or (at your option) any later version. 
00010  *
00011  * This program is distributed in the hope that it will be useful,
00012  * but WITHOUT ANY WARRANTY; without even the implied warranty of
00013  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
00014  * GNU General Public License for more details.
00015  *
00016  * You should have received a copy of the GNU General Public License
00017  * along with this program; if not, write to the Free Software Foundation,
00018  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
00019  *
00020  * The Original Code is Copyright (C) 2007 Blender Foundation.
00021  * All rights reserved.
00022  *
00023  * 
00024  * Contributor(s): Joseph Eagar, Joshua Leung
00025  *
00026  * ***** END GPL LICENSE BLOCK *****
00027  */
00028 
00034 #include <float.h>
00035 #include <string.h>
00036 #include <ctype.h>
00037 #include <stdio.h>
00038 
00039 #include "DNA_ID.h"
00040 #include "DNA_object_types.h"
00041 #include "DNA_screen_types.h"
00042 #include "DNA_scene_types.h"
00043 #include "DNA_userdef_types.h"
00044 #include "DNA_windowmanager_types.h"
00045 
00046 #include "MEM_guardedalloc.h"
00047 
00048 #include "PIL_time.h"
00049 
00050 #include "BLI_blenlib.h"
00051 #include "BLI_dynstr.h" /*for WM_operator_pystring */
00052 #include "BLI_editVert.h"
00053 #include "BLI_math.h"
00054 #include "BLI_utildefines.h"
00055 
00056 #include "BKE_blender.h"
00057 #include "BKE_context.h"
00058 #include "BKE_depsgraph.h"
00059 #include "BKE_mesh.h"
00060 #include "BKE_modifier.h"
00061 #include "BKE_report.h"
00062 #include "BKE_scene.h"
00063 #include "BKE_array_mallocn.h"
00064 
00065 #include "BIF_gl.h"
00066 #include "BIF_glutil.h" /* for paint cursor */
00067 
00068 #include "IMB_imbuf_types.h"
00069 
00070 #include "ED_screen.h"
00071 #include "ED_space_api.h"
00072 #include "ED_view3d.h"
00073 #include "ED_mesh.h"
00074 
00075 #include "RNA_access.h"
00076 #include "RNA_define.h"
00077 
00078 #include "UI_interface.h"
00079 
00080 #include "WM_api.h"
00081 #include "WM_types.h"
00082 
00083 #include "mesh_intern.h"
00084 
00085 /* ringsel operator */
00086 
00087 /* struct for properties used while drawing */
00088 typedef struct tringselOpData {
00089         ARegion *ar;            /* region that ringsel was activated in */
00090         void *draw_handle;      /* for drawing preview loop */
00091         
00092         float (*edges)[2][3];
00093         int totedge;
00094 
00095         ViewContext vc;
00096 
00097         Object *ob;
00098         EditMesh *em;
00099         EditEdge *eed;
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         em_setup_viewcontext(C, &lcd->vc);
00351 
00352         ED_region_tag_redraw(lcd->ar);
00353 
00354         return 1;
00355 }
00356 
00357 static int ringcut_cancel (bContext *UNUSED(C), wmOperator *op)
00358 {
00359         /* this is just a wrapper around exit() */
00360         ringsel_exit(op);
00361         return OPERATOR_CANCELLED;
00362 }
00363 
00364 static int ringsel_invoke (bContext *C, wmOperator *op, wmEvent *evt)
00365 {
00366         tringselOpData *lcd;
00367         EditEdge *edge;
00368         int dist = 75;
00369         
00370         view3d_operator_needs_opengl(C);
00371 
00372         if (!ringsel_init(C, op, 0))
00373                 return OPERATOR_CANCELLED;
00374         
00375         lcd = op->customdata;
00376         
00377         if (lcd->em->selectmode == SCE_SELECT_FACE) {
00378                 ringsel_exit(op);
00379                 WM_operator_name_call(C, "MESH_OT_loop_select", WM_OP_INVOKE_REGION_WIN, NULL);
00380                 return OPERATOR_CANCELLED;
00381         }
00382 
00383         lcd->vc.mval[0] = evt->mval[0];
00384         lcd->vc.mval[1] = evt->mval[1];
00385         
00386         edge = findnearestedge(&lcd->vc, &dist);
00387         if(!edge) {
00388                 ringsel_exit(op);
00389                 return OPERATOR_CANCELLED;
00390         }
00391 
00392         lcd->eed = edge;
00393         ringsel_find_edge(lcd, 1);
00394 
00395         ringsel_finish(C, op);
00396         ringsel_exit(op);
00397 
00398         return OPERATOR_FINISHED;
00399 }
00400 
00401 static int ringcut_invoke (bContext *C, wmOperator *op, wmEvent *evt)
00402 {
00403         Object *obedit= CTX_data_edit_object(C);
00404         tringselOpData *lcd;
00405         EditEdge *edge;
00406         int dist = 75;
00407 
00408         if(modifiers_isDeformedByLattice(obedit) || modifiers_isDeformedByArmature(obedit))
00409                 BKE_report(op->reports, RPT_WARNING, "Loop cut doesn't work well on deformed edit mesh display");
00410         
00411         view3d_operator_needs_opengl(C);
00412 
00413         if (!ringsel_init(C, op, 1))
00414                 return OPERATOR_CANCELLED;
00415         
00416         /* add a modal handler for this operator - handles loop selection */
00417         WM_event_add_modal_handler(C, op);
00418 
00419         lcd = op->customdata;
00420         lcd->vc.mval[0] = evt->mval[0];
00421         lcd->vc.mval[1] = evt->mval[1];
00422         
00423         edge = findnearestedge(&lcd->vc, &dist);
00424         if (edge != lcd->eed) {
00425                 lcd->eed = edge;
00426                 ringsel_find_edge(lcd, 1);
00427         }
00428         ED_area_headerprint(CTX_wm_area(C), "Select a ring to be cut, use mouse-wheel or page-up/down for number of cuts");
00429         
00430         return OPERATOR_RUNNING_MODAL;
00431 }
00432 
00433 static int ringcut_modal (bContext *C, wmOperator *op, wmEvent *event)
00434 {
00435         int cuts= RNA_int_get(op->ptr,"number_cuts");
00436         tringselOpData *lcd= op->customdata;
00437 
00438         view3d_operator_needs_opengl(C);
00439 
00440 
00441         switch (event->type) {
00442                 case LEFTMOUSE: /* confirm */ // XXX hardcoded
00443                         if (event->val == KM_PRESS) {
00444                                 /* finish */
00445                                 ED_region_tag_redraw(lcd->ar);
00446                                 
00447                                 ringsel_finish(C, op);
00448                                 ringsel_exit(op);
00449                                 ED_area_headerprint(CTX_wm_area(C), NULL);
00450                                 
00451                                 return OPERATOR_FINISHED;
00452                         }
00453                         
00454                         ED_region_tag_redraw(lcd->ar);
00455                         break;
00456                 case RIGHTMOUSE: /* abort */ // XXX hardcoded
00457                 case ESCKEY:
00458                         if (event->val == KM_RELEASE) {
00459                                 /* cancel */
00460                                 ED_region_tag_redraw(lcd->ar);
00461                                 ED_area_headerprint(CTX_wm_area(C), NULL);
00462                                 
00463                                 return ringcut_cancel(C, op);
00464                         }
00465                         
00466                         ED_region_tag_redraw(lcd->ar);
00467                         break;
00468                 case WHEELUPMOUSE:  /* change number of cuts */
00469                 case PAGEUPKEY:
00470                         if (event->val == KM_PRESS) {
00471                                 cuts++;
00472                                 RNA_int_set(op->ptr, "number_cuts",cuts);
00473                                 ringsel_find_edge(lcd, cuts);
00474                                 
00475                                 ED_region_tag_redraw(lcd->ar);
00476                         }
00477                         break;
00478                 case WHEELDOWNMOUSE:  /* change number of cuts */
00479                 case PAGEDOWNKEY:
00480                         if (event->val == KM_PRESS) {
00481                                 cuts=MAX2(cuts-1,1);
00482                                 RNA_int_set(op->ptr,"number_cuts",cuts);
00483                                 ringsel_find_edge(lcd, cuts);
00484                                 
00485                                 ED_region_tag_redraw(lcd->ar);
00486                         }
00487                         break;
00488                 case MOUSEMOVE: { /* mouse moved somewhere to select another loop */
00489                         int dist = 75;
00490                         EditEdge *edge;
00491 
00492                         lcd->vc.mval[0] = event->mval[0];
00493                         lcd->vc.mval[1] = event->mval[1];
00494                         edge = findnearestedge(&lcd->vc, &dist);
00495 
00496                         if (edge != lcd->eed) {
00497                                 lcd->eed = edge;
00498                                 ringsel_find_edge(lcd, cuts);
00499                         }
00500 
00501                         ED_region_tag_redraw(lcd->ar);
00502                         break;
00503                 }                       
00504         }
00505         
00506         /* keep going until the user confirms */
00507         return OPERATOR_RUNNING_MODAL;
00508 }
00509 
00510 void MESH_OT_edgering_select (wmOperatorType *ot)
00511 {
00512         /* description */
00513         ot->name= "Edge Ring Select";
00514         ot->idname= "MESH_OT_edgering_select";
00515         ot->description= "Select an edge ring";
00516         
00517         /* callbacks */
00518         ot->invoke= ringsel_invoke;
00519         ot->poll= ED_operator_editmesh_region_view3d; 
00520         
00521         /* flags */
00522         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
00523 
00524         RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend the selection");
00525 }
00526 
00527 void MESH_OT_loopcut (wmOperatorType *ot)
00528 {
00529         /* description */
00530         ot->name= "Loop Cut";
00531         ot->idname= "MESH_OT_loopcut";
00532         ot->description= "Add a new loop between existing loops";
00533         
00534         /* callbacks */
00535         ot->invoke= ringcut_invoke;
00536         ot->modal= ringcut_modal;
00537         ot->cancel= ringcut_cancel;
00538         ot->poll= ED_operator_editmesh_region_view3d;
00539         
00540         /* flags */
00541         ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO|OPTYPE_BLOCKING;
00542 
00543         /* properties */
00544         RNA_def_int(ot->srna, "number_cuts", 1, 1, INT_MAX, "Number of Cuts", "", 1, 10);
00545 }