4 * ***** BEGIN GPL LICENSE BLOCK *****
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version 2
9 * of the License, or (at your option) any later version.
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software Foundation,
18 * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
20 * The Original Code is Copyright (C) 2004 by Blender Foundation.
21 * All rights reserved.
23 * The Original Code is: all of this file.
25 * Contributor(s): none yet.
27 * ***** END GPL LICENSE BLOCK *****
32 editmesh_loop: tools with own drawing subloops, select, knife, subdiv
40 #include "MEM_guardedalloc.h"
43 #include "DNA_mesh_types.h"
44 #include "DNA_meshdata_types.h"
45 #include "DNA_object_types.h"
46 #include "DNA_scene_types.h"
47 #include "DNA_screen_types.h"
48 #include "DNA_view3d_types.h"
50 #include "BLI_blenlib.h"
51 #include "BLI_arithb.h"
52 #include "BLI_editVert.h"
53 #include "BLI_ghash.h"
55 #include "BKE_context.h"
56 #include "BKE_depsgraph.h"
57 #include "BKE_displist.h"
58 #include "BKE_global.h"
59 #include "BKE_library.h"
61 #include "BKE_object.h"
62 #include "BKE_utildefines.h"
68 #include "RNA_access.h"
69 #include "RNA_define.h"
75 #include "ED_screen.h"
76 #include "ED_view3d.h"
78 #include "mesh_intern.h"
81 /* **** XXX ******** */
82 static void BIF_undo_push() {}
83 static void BIF_undo() {}
84 static void error() {}
85 static int qtest() {return 0;}
86 /* **** XXX ******** */
90 static void edgering_sel(EditMesh *em, EditEdge *startedge, int select, int previewlines)
98 /* in eed->f1 we put the valence (amount of faces in edge) */
99 /* in eed->f2 we put tagged flag as correct loop */
100 /* in efa->f1 we put tagged flag as correct to select */
102 for(eed= em->edges.first; eed; eed= eed->next) {
106 for(efa= em->faces.first; efa; efa= efa->next) {
112 if(efa->e4) efa->e4->f1++;
122 for(efa= em->faces.first; efa; efa= efa->next) {
123 if(efa->e4 && efa->f1==0 && efa->h == 0) { // not done quad
124 if(efa->e1->f1<=2 && efa->e2->f1<=2 && efa->e3->f1<=2 && efa->e4->f1<=2) { // valence ok
126 // if edge tagged, select opposing edge and mark face ok
132 else if(efa->e2->f2) {
152 if(previewlines > 0 && select == 0){
153 // XXX persp(PERSP_VIEW);
154 // XXX glPushMatrix();
155 // XXX mymultmatrix(obedit->obmat);
157 for(efa= em->faces.first; efa; efa= efa->next) {
158 if(efa->v4 == NULL) { continue; }
160 if(efa->e1->f2 == 1){
161 if(efa->e1->h == 1 || efa->e3->h == 1 )
168 } else if(efa->e2->f2 == 1){
169 if(efa->e2->h == 1 || efa->e4->h == 1)
177 for(i=1;i<=previewlines;i++){
178 co[0][0] = (v[0][1]->co[0] - v[0][0]->co[0])*(i/((float)previewlines+1))+v[0][0]->co[0];
179 co[0][1] = (v[0][1]->co[1] - v[0][0]->co[1])*(i/((float)previewlines+1))+v[0][0]->co[1];
180 co[0][2] = (v[0][1]->co[2] - v[0][0]->co[2])*(i/((float)previewlines+1))+v[0][0]->co[2];
182 co[1][0] = (v[1][1]->co[0] - v[1][0]->co[0])*(i/((float)previewlines+1))+v[1][0]->co[0];
183 co[1][1] = (v[1][1]->co[1] - v[1][0]->co[1])*(i/((float)previewlines+1))+v[1][0]->co[1];
184 co[1][2] = (v[1][1]->co[2] - v[1][0]->co[2])*(i/((float)previewlines+1))+v[1][0]->co[2];
185 glColor3ub(255, 0, 255);
187 glVertex3f(co[0][0],co[0][1],co[0][2]);
188 glVertex3f(co[1][0],co[1][1],co[1][2]);
196 /* (de)select the edges */
197 for(eed= em->edges.first; eed; eed= eed->next) {
198 if(eed->f2) EM_select_edge(eed, select);
202 void CutEdgeloop(Object *obedit, EditMesh *em, int numcuts)
204 ViewContext vc; // XXX
205 EditEdge *nearest=NULL, *eed;
207 int keys = 0, holdnum=0, selectmode, dist;
208 short mvalo[2] = {0,0}, mval[2] = {0, 0};
209 short event=0, val, choosing=1, cancel=0, cuthalf = 0, smooth=0;
213 selectmode = em->selectmode;
215 if(em->selectmode & SCE_SELECT_FACE){
216 em->selectmode = SCE_SELECT_EDGE;
217 EM_selectmode_set(em);
221 BIF_undo_push("Loopcut Begin");
222 while(choosing && !cancel){
223 // XXX getmouseco_areawin(mval);
224 if (mval[0] != mvalo[0] || mval[1] != mvalo[1]) {
228 nearest = findnearestedge(&vc, &dist); // returns actual distance in dist
229 // scrarea_do_windraw(curarea); // after findnearestedge, backbuf!
231 sprintf(msg,"Number of Cuts: %d",numcuts);
233 sprintf(msg,"%s (S)mooth: on",msg);
235 sprintf(msg,"%s (S)mooth: off",msg);
239 /* Need to figure preview */
241 edgering_sel(em, nearest, 0, numcuts);
243 // XXX screen_swapbuffers();
245 /* backbuffer refresh for non-apples (no aux) */
247 // XXX if(G.vd->drawtype>OB_WIRE && (G.vd->flag & V3D_ZBUF_SELECT)) {
248 // backdrawview3d(0);
252 else PIL_sleep_ms(10); // idle
258 // XXX event= extern_qread(&val);
259 if(val && (event == MOUSEX || event == MOUSEY)){ ; }
260 else if(val && ((event==LEFTMOUSE || event==RETKEY) || (event == MIDDLEMOUSE || event==PADENTER)))
262 if(event == MIDDLEMOUSE){
270 else if(val && (event==ESCKEY || event==RIGHTMOUSE ))
276 else if(val && (event==PADPLUSKEY || event==WHEELUPMOUSE))
281 else if(val && (event==PADMINUS || event==WHEELDOWNMOUSE))
288 else if(val && event==SKEY)
290 if(smooth){smooth=0;}
331 if(holdnum >= 0 && numcuts*10 < 130){
332 if(keys == 0){ // first level numeric entry
337 } else if(keys > 0){//highrt level numeric entry
342 } else if (holdnum == -2){// backspace
353 } // End Numeric Entry
354 } //End while(qtest())
360 /* clean selection */
361 for(eed=em->edges.first; eed; eed = eed->next){
362 EM_select_edge(eed,0);
364 /* select edge ring */
365 edgering_sel(em, nearest, 1, 0);
367 /* now cut the loops */
370 // XXX if(fbutton(&fac, 0.0f, 5.0f, 10, 10, "Smooth:")==0) return;
372 esubdivideflag(obedit, em, SELECT,fac,B_SMOOTH,numcuts,SUBDIV_SELECT_LOOPCUT);
374 esubdivideflag(obedit, em, SELECT,0,0,numcuts,SUBDIV_SELECT_LOOPCUT);
376 /* if this was a single cut, enter edgeslide mode */
377 if(numcuts == 1 && hasHidden == 0){
379 EdgeSlide(em, 1,0.0);
381 if(EdgeSlide(em, 0,0.0) == -1){
387 if(em->selectmode != selectmode){
388 em->selectmode = selectmode;
389 EM_selectmode_set(em);
392 // DAG_object_flush_update(scene, obedit, OB_RECALC_DATA);
397 /* *************** LOOP SELECT ************* */
399 static short edgeFaces(EditMesh *em, EditEdge *e)
401 EditFace *search=NULL;
404 search = em->faces.first;
406 if((search->e1 == e || search->e2 == e) || (search->e3 == e || search->e4 == e))
408 search = search->next;
416 /* ***************** TRAIL ************************
418 Read a trail of mouse coords and return them as an array of CutCurve structs
419 len returns number of mouse coords read before commiting with RETKEY
420 It is up to the caller to free the block when done with it,
422 XXX Is only used here, so local inside this file (ton)
425 #define TRAIL_POLYLINE 1 /* For future use, They don't do anything yet */
426 #define TRAIL_FREEHAND 2
427 #define TRAIL_MIXED 3 /* (1|2) */
429 #define TRAIL_MIDPOINTS 8
431 typedef struct CutCurve {
437 /* ******************************************************************** */
438 /* Knife Subdivide Tool. Subdivides edges intersected by a mouse trail
441 Currently mapped to KKey when in MeshEdit mode.
443 Hit Shift K, Select Centers or Exact
444 Hold LMB down to draw path, hit RETKEY.
445 ESC cancels as expected.
447 Contributed by Robert Wenzlaff (Det. Thorn).
450 - non modal (no menu before cutting)
451 - exit on mouse release
452 - polygon/segment drawing can become handled by WM cb later
456 #define KNIFE_EXACT 1
457 #define KNIFE_MIDPOINT 2
458 #define KNIFE_MULTICUT 3
460 static EnumPropertyItem knife_items[]= {
461 {KNIFE_EXACT, "EXACT", "Exact", ""},
462 {KNIFE_MIDPOINT, "MIDPOINTS", "Midpoints", ""},
463 {KNIFE_MULTICUT, "MULTICUT", "Multicut", ""},
467 /* seg_intersect() Determines if and where a mouse trail intersects an EditEdge */
469 static float seg_intersect(EditEdge *e, CutCurve *c, int len, char mode, struct GHash *gh)
471 #define MAXSLOPE 100000
472 float x11, y11, x12=0, y12=0, x2max, x2min, y2max;
473 float y2min, dist, lastdist=0, xdiff2, xdiff1;
474 float m1, b1, m2, b2, x21, x22, y21, y22, xi;
475 float yi, x1min, x1max, y1max, y1min, perc=0;
477 float threshold = 0.0;
480 //threshold = 0.000001; /*tolerance for vertex intersection*/
481 // XXX threshold = scene->toolsettings->select_thresh / 100;
483 /* Get screen coords of verts */
484 scr = BLI_ghash_lookup(gh, e->v1);
488 scr = BLI_ghash_lookup(gh, e->v2);
495 b2= ((x22*y21)-(x21*y22))/xdiff2;
498 m2=MAXSLOPE; /* Verticle slope */
502 /*check for *exact* vertex intersection first*/
503 if(mode!=KNIFE_MULTICUT){
504 for (i=0; i<len; i++){
517 if((x11 == x21 && y11 == y21) || (x12 == x21 && y12 == y21)){
523 else if((x11 == x22 && y11 == y22) || (x12 == x22 && y12 == y22)){
531 /*now check for edge interesect (may produce vertex intersection as well)*/
532 for (i=0; i<len; i++){
544 /* Perp. Distance from point to line */
545 if (m2!=MAXSLOPE) dist=(y12-m2*x12-b2);/* /sqrt(m2*m2+1); Only looking for */
546 /* change in sign. Skip extra math */
549 if (i==0) lastdist=dist;
551 /* if dist changes sign, and intersect point in edge's Bound Box*/
552 if ((lastdist*dist)<=0){
553 xdiff1=(x12-x11); /* Equation of line between last 2 points */
556 b1= ((x12*y11)-(x11*y12))/xdiff1;
562 x2max=MAX2(x21,x22)+0.001; /* prevent missed edges */
563 x2min=MIN2(x21,x22)-0.001; /* due to round off error */
564 y2max=MAX2(y21,y22)+0.001;
565 y2min=MIN2(y21,y22)-0.001;
567 /* Found an intersect, calc intersect point */
568 if (m1==m2){ /* co-incident lines */
569 /* cut at 50% of overlap area*/
570 x1max=MAX2(x11, x12);
571 x1min=MIN2(x11, x12);
572 xi= (MIN2(x2max,x1max)+MAX2(x2min,x1min))/2.0;
574 y1max=MAX2(y11, y12);
575 y1min=MIN2(y11, y12);
576 yi= (MIN2(y2max,y1max)+MAX2(y2min,y1min))/2.0;
578 else if (m2==MAXSLOPE){
582 else if (m1==MAXSLOPE){
588 yi=(b1*m2-m1*b2)/(m2-m1);
591 /* Intersect inside bounding box of edge?*/
592 if ((xi>=x2min)&&(xi<=x2max)&&(yi<=y2max)&&(yi>=y2min)){
593 /*test for vertex intersect that may be 'close enough'*/
594 if(mode!=KNIFE_MULTICUT){
595 if(xi <= (x21 + threshold) && xi >= (x21 - threshold)){
596 if(yi <= (y21 + threshold) && yi >= (y21 - threshold)){
602 if(xi <= (x22 + threshold) && xi >= (x22 - threshold)){
603 if(yi <= (y22 + threshold) && yi >= (y22 - threshold)){
610 if ((m2<=1.0)&&(m2>=-1.0)) perc = (xi-x21)/(x22-x21);
611 else perc=(yi-y21)/(y22-y21); /*lower slope more accurate*/
612 //isect=32768.0*(perc+0.0000153); /* Percentage in 1/32768ths */
623 static float bm_seg_intersect(BMEdge *e, CutCurve *c, int len, char mode,
624 struct GHash *gh, int *isected)
626 #define MAXSLOPE 100000
627 float x11, y11, x12=0, y12=0, x2max, x2min, y2max;
628 float y2min, dist, lastdist=0, xdiff2, xdiff1;
629 float m1, b1, m2, b2, x21, x22, y21, y22, xi;
630 float yi, x1min, x1max, y1max, y1min, perc=0;
632 float threshold = 0.0;
635 //threshold = 0.000001; /*tolerance for vertex intersection*/
636 // XXX threshold = scene->toolsettings->select_thresh / 100;
638 /* Get screen coords of verts */
639 scr = BLI_ghash_lookup(gh, e->v1);
643 scr = BLI_ghash_lookup(gh, e->v2);
650 b2= ((x22*y21)-(x21*y22))/xdiff2;
653 m2=MAXSLOPE; /* Verticle slope */
659 /*check for *exact* vertex intersection first*/
660 if(mode!=KNIFE_MULTICUT){
661 for (i=0; i<len; i++){
674 if((x11 == x21 && y11 == y21) || (x12 == x21 && y12 == y21)){
680 else if((x11 == x22 && y11 == y22) || (x12 == x22 && y12 == y22)){
688 /*now check for edge interesect (may produce vertex intersection as well)*/
689 for (i=0; i<len; i++){
701 /* Perp. Distance from point to line */
702 if (m2!=MAXSLOPE) dist=(y12-m2*x12-b2);/* /sqrt(m2*m2+1); Only looking for */
703 /* change in sign. Skip extra math */
706 if (i==0) lastdist=dist;
708 /* if dist changes sign, and intersect point in edge's Bound Box*/
709 if ((lastdist*dist)<=0){
710 xdiff1=(x12-x11); /* Equation of line between last 2 points */
713 b1= ((x12*y11)-(x11*y12))/xdiff1;
719 x2max=MAX2(x21,x22)+0.001; /* prevent missed edges */
720 x2min=MIN2(x21,x22)-0.001; /* due to round off error */
721 y2max=MAX2(y21,y22)+0.001;
722 y2min=MIN2(y21,y22)-0.001;
724 /* Found an intersect, calc intersect point */
725 if (m1==m2){ /* co-incident lines */
726 /* cut at 50% of overlap area*/
727 x1max=MAX2(x11, x12);
728 x1min=MIN2(x11, x12);
729 xi= (MIN2(x2max,x1max)+MAX2(x2min,x1min))/2.0;
731 y1max=MAX2(y11, y12);
732 y1min=MIN2(y11, y12);
733 yi= (MIN2(y2max,y1max)+MAX2(y2min,y1min))/2.0;
735 else if (m2==MAXSLOPE){
739 else if (m1==MAXSLOPE){
745 yi=(b1*m2-m1*b2)/(m2-m1);
748 /* Intersect inside bounding box of edge?*/
749 if ((xi>=x2min)&&(xi<=x2max)&&(yi<=y2max)&&(yi>=y2min)){
750 /*test for vertex intersect that may be 'close enough'*/
751 if(mode!=KNIFE_MULTICUT){
752 if(xi <= (x21 + threshold) && xi >= (x21 - threshold)){
753 if(yi <= (y21 + threshold) && yi >= (y21 - threshold)){
759 if(xi <= (x22 + threshold) && xi >= (x22 - threshold)){
760 if(yi <= (y22 + threshold) && yi >= (y22 - threshold)){
767 if ((m2<=1.0)&&(m2>=-1.0)) perc = (xi-x21)/(x22-x21);
768 else perc=(yi-y21)/(y22-y21); /*lower slope more accurate*/
769 //isect=32768.0*(perc+0.0000153); /* Percentage in 1/32768ths */
781 static int knife_cut_exec(bContext *C, wmOperator *op)
783 Object *obedit= CTX_data_edit_object(C);
784 EditMesh *em= ((Mesh *)obedit->data)->edit_mesh, *em2;
786 ARegion *ar= CTX_wm_region(C);
789 BMEdge **edges = NULL, *be;
791 CutCurve curve[MAX_CUTS];
794 float *scr, co[4], *percents = NULL;
795 int len=0, isected, flag, i;
796 short numcuts=1, mode= RNA_int_get(op->ptr, "type");
798 if (EM_nvertices_selected(em) < 2) {
799 error("No edges are selected to operate on");
800 return OPERATOR_CANCELLED;;
803 /* get the cut curve */
804 RNA_BEGIN(op->ptr, itemptr, "path") {
806 RNA_float_get_array(&itemptr, "loc", (float *)&curve[len]);
808 if(len>= MAX_CUTS) break;
812 if(len<2) return OPERATOR_CANCELLED;
814 bm = editmesh_to_bmesh(em);
816 /*the floating point coordinates of verts in screen space will be stored in a hash table according to the vertices pointer*/
817 gh = BLI_ghash_new(BLI_ghashutil_ptrhash, BLI_ghashutil_ptrcmp);
818 for(bv=BMIter_New(&iter, bm, BM_VERTS, NULL);bv;bv=BMIter_Step(&iter)){
819 scr = MEM_mallocN(sizeof(float)*2, "Vertex Screen Coordinates");
822 Mat4MulVec4fl(obedit->obmat, co);
823 project_float(ar, co, scr);
824 BLI_ghash_insert(gh, bv, scr);
827 BMO_Init_Op(&bmop, BMOP_ESUBDIVIDE);
830 /*store percentage of edge cut for KNIFE_EXACT here.*/
831 for (be=BMIter_New(&iter, bm, BM_EDGES, NULL); be; be=BMIter_Step(&iter)) {
832 if( BM_Is_Selected(bm, be) ) {
833 isect= bm_seg_intersect(be, curve, len, mode, gh, &isected);
836 if (mode != KNIFE_MULTICUT) {
837 BMO_Insert_MapFloat(bm, &bmop,
838 BMOP_ESUBDIVIDE_PERCENT_EDGEMAP,
842 BMO_SetFlag(bm, be, 1);
847 BMO_Flag_To_Slot(bm, &bmop, BMOP_ESUBDIVIDE_EDGES, 1, BM_EDGE);
849 BMO_Set_Int(&bmop, BMOP_ESUBDIVIDE_NUMCUTS, numcuts);
851 if (mode == KNIFE_MIDPOINT) numcuts = 1;
852 BMO_Set_Int(&bmop, BMOP_ESUBDIVIDE_FLAG, flag);
853 BMO_Set_Float(&bmop, BMOP_ESUBDIVIDE_RADIUS, 0);
854 BMO_Set_Int(&bmop, BMOP_ESUBDIVIDE_SELACTION, SUBDIV_SELECT_ORIG);
856 BMO_Exec_Op(bm, &bmop);
857 BMO_Finish_Op(bm, &bmop);
862 //if (mode==KNIFE_MIDPOINT) esubdivideflag(obedit, em, SELECT, 0, B_KNIFE, 1, SUBDIV_SELECT_ORIG);
863 //else if (mode==KNIFE_MULTICUT) esubdivideflag(obedit, em, SELECT, 0, B_KNIFE, numcuts, SUBDIV_SELECT_ORIG);
864 //else esubdivideflag(obedit, em, SELECT, 0, B_KNIFE|B_PERCENTSUBD, 1, SUBDIV_SELECT_ORIG);
866 BLI_ghash_free(gh, NULL, (GHashValFreeFP)MEM_freeN);
870 em2 = bmesh_to_editmesh(bm);
876 return OPERATOR_FINISHED;
880 void MESH_OT_knife_cut(wmOperatorType *ot)
884 ot->name= "Knife Cut";
885 ot->idname= "MESH_OT_knife_cut";
887 ot->invoke= WM_gesture_lines_invoke;
888 ot->modal= WM_gesture_lines_modal;
889 ot->exec= knife_cut_exec;
891 ot->poll= ED_operator_editmesh;
894 ot->flag= OPTYPE_REGISTER|OPTYPE_UNDO;
896 RNA_def_enum(ot->srna, "type", knife_items, KNIFE_EXACT, "Type", "");
897 prop= RNA_def_property(ot->srna, "path", PROP_COLLECTION, PROP_NONE);
898 RNA_def_property_struct_runtime(prop, &RNA_OperatorMousePath);
901 RNA_def_int(ot->srna, "cursor", BC_KNIFECURSOR, 0, INT_MAX, "Cursor", "", 0, INT_MAX);
904 /* ******************************************************* */