Transform Orientations
[blender.git] / source / blender / src / transform_orientations.c
1 /**
2  * $Id: 
3  *
4  * ***** BEGIN GPL LICENSE BLOCK *****
5  *
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.
10  *
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.
15  *
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.
19  *
20  * Contributor(s): Martin Poirier
21  *
22  * ***** END GPL LICENSE BLOCK *****
23  */
24
25 #include <string.h>
26
27 #include "MEM_guardedalloc.h"
28
29 #include "DNA_armature_types.h"
30 #include "DNA_curve_types.h"
31 #include "DNA_listBase.h"
32 #include "DNA_object_types.h"
33 #include "DNA_meshdata_types.h"
34 #include "DNA_mesh_types.h"
35 #include "DNA_meta_types.h"
36 #include "DNA_scene_types.h"
37 #include "DNA_space_types.h"
38 #include "DNA_view3d_types.h"
39
40 #include "BKE_global.h"
41 #include "BKE_utildefines.h"
42
43 #include "BLI_arithb.h"
44 #include "BLI_blenlib.h"
45 #include "BLI_editVert.h"
46
47 #include "BIF_editmesh.h"
48 #include "BIF_editarmature.h"
49 #include "BIF_interface.h"
50 #include "BIF_space.h"
51 #include "BIF_toolbox.h"
52
53 #include "blendef.h"
54
55
56 #include "transform.h"
57
58
59 /* *********************** TransSpace ************************** */
60
61 void BIF_clearTransformOrientation(void)
62 {
63         ListBase *transform_spaces = &G.scene->transform_spaces;
64         BLI_freelistN(transform_spaces);
65         
66         if (G.vd->twmode >= V3D_MANIP_CUSTOM)
67                 G.vd->twmode = V3D_MANIP_GLOBAL;        /* fallback to global   */
68 }
69  
70 void BIF_manageTransformOrientation(int confirm, int set) {
71         int index = -1; 
72         
73         if (G.obedit) {
74                 if (G.obedit->type == OB_MESH)
75                         index = manageMeshSpace(confirm, set);
76         }
77         else {
78                 index = manageObjectSpace(confirm, set);
79         }
80         
81         if (set && index != -1)
82         {
83                 BIF_selectTransformOrientationFromIndex(index);
84         }
85 }
86
87 int manageObjectSpace(int confirm, int set) {
88         Base *base = BASACT;
89
90         if (base == NULL)
91                 return -1;
92
93         if (confirm == 0) {
94                 if (set && pupmenu("Custom Orientation %t|Add and Use Active Object%x1") != 1) {
95                         return -1;
96                 }
97                 else if (set == 0 && pupmenu("Custom Orientation %t|Add Active Object%x1") != 1) {
98                         return -1;
99                 }
100         }
101
102         return addObjectSpace(base->object);
103 }
104
105 /* return 1 on confirm */
106 int confirmSpace(int set, char text[])
107 {
108         char menu[64];
109         
110         if (set) {
111                 sprintf(menu, "Custom Orientation %%t|Add and Use %s%%x1", text);
112         }
113         else {
114                 sprintf(menu, "Custom Orientation %%t|Add %s%%x1", text);
115         }
116         
117         if (pupmenu(menu) == 1) {
118                 return 1;
119         }
120         else {
121                 return 0;
122         }
123 }
124
125
126 int manageMeshSpace(int confirm, int set) {
127         float mat[3][3];
128         float normal[3], plane[3];
129         char name[36] = "";
130         int index;
131         int type;
132
133         type = getTransformOrientation(normal, plane, 0);
134         
135         switch (type)
136         {
137                 case ORIENTATION_VERT:
138                         if (confirm == 0 && confirmSpace(set, "vertex") == 0) {
139                                 return -1;
140                         }
141         
142                         if (createSpaceNormal(mat, normal) == 0) {
143                                 error("Cannot use vertex with zero-length normal");
144                                 return -1;
145                         }
146         
147                         strcpy(name, "Vertex");
148                         break;
149                 case ORIENTATION_EDGE:
150                         if (confirm == 0 && confirmSpace(set, "Edge") == 0) {
151                                 return -1;
152                         }
153         
154                         if (createSpaceNormalTangent(mat, normal, plane) == 0) {
155                                 error("Cannot use zero-length edge");
156                                 return -1;
157                         }
158         
159                         strcpy(name, "Edge");
160                         break;
161                 case ORIENTATION_FACE:
162                         if (confirm == 0 && confirmSpace(set, "Face") == 0) {
163                                 return -1;
164                         }
165         
166                         if (createSpaceNormalTangent(mat, normal, plane) == 0) {
167                                 error("Cannot use zero-area face");
168                                 return -1;
169                         }
170         
171                         strcpy(name, "Face");
172                         break;
173                 default:
174                         return -1;
175                         break;
176         }
177
178         /* Input name */
179         sbutton(name, 1, 35, "name: ");
180
181         index = addMatrixSpace(mat, name);
182         return index;
183 }
184
185 int createSpaceNormal(float mat[3][3], float normal[3])
186 {
187         float tangent[3] = {0.0f, 0.0f, 1.0f};
188         
189         VECCOPY(mat[2], normal);
190         if (Normalize(mat[2]) == 0.0f) {
191                 return 0; /* error return */
192         }
193
194         Crossf(mat[0], mat[2], tangent);
195         if (Inpf(mat[0], mat[0]) == 0.0f) {
196                 tangent[0] = 1.0f;
197                 tangent[1] = tangent[2] = 0.0f;
198                 Crossf(mat[0], tangent, mat[2]);
199         }
200
201         Crossf(mat[1], mat[2], mat[0]);
202
203         Mat3Ortho(mat);
204         
205         return 1;
206 }
207
208 int createSpaceNormalTangent(float mat[3][3], float normal[3], float tangent[3])
209 {
210         VECCOPY(mat[2], normal);
211         if (Normalize(mat[2]) == 0.0f) {
212                 return 0; /* error return */
213         }
214         
215         /* preempt zero length tangent from causing trouble */
216         if (tangent[0] == 0 && tangent[1] == 0 && tangent[2] == 0)
217         {
218                 tangent[2] = 1;
219         }
220
221         Crossf(mat[0], mat[2], tangent);
222         if (Normalize(mat[0]) == 0.0f) {
223                 return 0; /* error return */
224         }
225         
226         Crossf(mat[1], mat[2], mat[0]);
227
228         Mat3Ortho(mat);
229         
230         return 1;
231 }
232
233
234 int addObjectSpace(Object *ob) {
235         float mat[3][3];
236         char name[36] = "";
237
238         Mat3CpyMat4(mat, ob->obmat);
239         Mat3Ortho(mat);
240
241         strncpy(name, ob->id.name+2, 35);
242
243         /* Input name */
244         sbutton(name, 1, 35, "name: ");
245
246         return addMatrixSpace(mat, name);
247 }
248
249 int addMatrixSpace(float mat[3][3], char name[]) {
250         ListBase *transform_spaces = &G.scene->transform_spaces;
251         TransformOrientation *ts;
252         int index = 0;
253
254         /* if name is found in list, reuse that transform space */      
255         for (index = 0, ts = transform_spaces->first; ts; ts = ts->next, index++) {
256                 if (strncmp(ts->name, name, 35) == 0) {
257                         break;
258                 }
259         }
260
261         /* if not, create a new one */
262         if (ts == NULL)
263         {
264                 ts = MEM_callocN(sizeof(TransformOrientation), "UserTransSpace from matrix");
265                 BLI_addtail(transform_spaces, ts);
266                 strncpy(ts->name, name, 35);
267         }
268
269         /* copy matrix into transform space */
270         Mat3CpyMat3(ts->mat, mat);
271
272         BIF_undo_push("Add/Update Transform Orientation");
273         
274         return index;
275 }
276
277 void BIF_removeTransformOrientation(TransformOrientation *target) {
278         ListBase *transform_spaces = &G.scene->transform_spaces;
279         TransformOrientation *ts = transform_spaces->first;
280         int selected_index = (G.vd->twmode - V3D_MANIP_CUSTOM);
281         int i;
282         
283         for (i = 0, ts = transform_spaces->first; ts; ts = ts->next, i++) {
284                 if (ts == target) {
285                         if (selected_index == i) {
286                                 G.vd->twmode = V3D_MANIP_GLOBAL;        /* fallback to global   */
287                         }
288                         else if (selected_index > i)
289                                 G.vd->twmode--;
290
291                         BLI_freelinkN(transform_spaces, ts);
292                         break;
293                 }
294         }
295         BIF_undo_push("Remove Transform Orientation");
296 }
297
298 void BIF_selectTransformOrientation(TransformOrientation *target) {
299         ListBase *transform_spaces = &G.scene->transform_spaces;
300         TransformOrientation *ts = transform_spaces->first;
301         int i;
302         
303         for (i = 0, ts = transform_spaces->first; ts; ts = ts->next, i++) {
304                 if (ts == target) {
305                         G.vd->twmode = V3D_MANIP_CUSTOM + i;
306                         break;
307                 }
308         }
309 }
310
311 void BIF_selectTransformOrientationFromIndex(int index) {
312         G.vd->twmode = V3D_MANIP_CUSTOM + index;
313 }
314
315 char * BIF_menustringTransformOrientation(char *title) {
316         char menu[] = "%t|Global%x0|Local%x1|Normal%x2|View%x3";
317         ListBase *transform_spaces = &G.scene->transform_spaces;
318         TransformOrientation *ts;
319         int i = V3D_MANIP_CUSTOM;
320         char *str_menu, *p;
321         
322         
323         str_menu = MEM_callocN(strlen(menu) + strlen(title) + 40 * BIF_countTransformOrientation(), "UserTransSpace from matrix");
324         p = str_menu;
325         
326         p += sprintf(str_menu, "%s", title);
327         p += sprintf(p, "%s", menu);
328         
329         for (ts = transform_spaces->first; ts; ts = ts->next) {
330                 p += sprintf(p, "|%s%%x%d", ts->name, i++);
331         }
332         
333         return str_menu;
334 }
335
336 int BIF_countTransformOrientation() {
337         ListBase *transform_spaces = &G.scene->transform_spaces;
338         TransformOrientation *ts;
339         int count = 0;
340
341         for (ts = transform_spaces->first; ts; ts = ts->next) {
342                 count++;
343         }
344         
345         return count;
346 }
347
348 void applyTransformOrientation() {
349         TransInfo *t = BIF_GetTransInfo();
350         TransformOrientation *ts;
351         int selected_index = (G.vd->twmode - V3D_MANIP_CUSTOM);
352         int i;
353         
354         if (selected_index >= 0) {
355                 for (i = 0, ts = G.scene->transform_spaces.first; ts; ts = ts->next, i++) {
356                         if (selected_index == i) {
357                                 strcpy(t->spacename, ts->name);
358                                 Mat3CpyMat3(t->spacemtx, ts->mat);
359                                 Mat4CpyMat3(G.vd->twmat, ts->mat);
360                                 break;
361                         }
362                 }
363         }
364 }
365
366
367 int getTransformOrientation(float normal[3], float plane[3], int activeOnly)
368 {
369         Base *base;
370         Object *ob = OBACT;
371         int result = ORIENTATION_NONE;
372
373         normal[0] = normal[1] = normal[2] = 0;
374         plane[0] = plane[1] = plane[2] = 0;
375
376         if(G.obedit)
377         {
378                 ob= G.obedit;
379
380                 if(G.obedit->type==OB_MESH)
381                 {
382                         EditMesh *em = G.editMesh;
383                         EditVert *eve;
384                         EditSelection ese;
385                         float vec[3]= {0,0,0};
386                         
387                         /* USE LAST SELECTED WITH ACTIVE */
388                         if (activeOnly && EM_get_actSelection(&ese))
389                         {
390                                 EM_editselection_normal(normal, &ese);
391                                 EM_editselection_plane(plane, &ese);
392                                 
393                                 switch (ese.type)
394                                 {
395                                         case EDITVERT:
396                                                 result = ORIENTATION_VERT;
397                                                 break;
398                                         case EDITEDGE:
399                                                 result = ORIENTATION_EDGE;
400                                                 break;
401                                         case EDITFACE:
402                                                 result = ORIENTATION_FACE;
403                                                 break;
404                                 }
405                         }
406                         else
407                         {
408                                 if (G.totfacesel >= 1)
409                                 {
410                                         EditFace *efa;
411                                         
412                                         for(efa= em->faces.first; efa; efa= efa->next)
413                                         {
414                                                 if(efa->f & SELECT)
415                                                 {
416                                                         VECADD(normal, normal, efa->n);
417                                                         VecSubf(vec, efa->v2->co, efa->v1->co);
418                                                         VECADD(plane, plane, vec);
419                                                 }
420                                         }
421                                         
422                                         result = ORIENTATION_FACE;
423                                 }
424                                 else if (G.totvertsel == 3)
425                                 {
426                                         EditVert *v1 = NULL, *v2 = NULL, *v3 = NULL;
427                                         float cotangent[3];
428                                         
429                                         for (eve = em->verts.first; eve; eve = eve->next)
430                                         {
431                                                 if ( eve->f & SELECT ) {
432                                                         if (v1 == NULL) {
433                                                                 v1 = eve; 
434                                                         }
435                                                         else if (v2 == NULL) {
436                                                                 v2 = eve;
437                                                         }
438                                                         else {
439                                                                 v3 = eve;
440
441                                                                 VecSubf(plane, v2->co, v1->co);
442                                                                 VecSubf(cotangent, v3->co, v2->co);
443                                                                 Crossf(normal, cotangent, plane);
444                                                                 break;
445                                                         }
446                                                 }
447                                         }
448
449                                         /* if there's an edge available, use that for the tangent */
450                                         if (G.totedgesel >= 1)
451                                         {
452                                                 EditEdge *eed = NULL;
453         
454                                                 for(eed= em->edges.first; eed; eed= eed->next) {
455                                                         if(eed->f & SELECT) {
456                                                                 VecSubf(plane, eed->v2->co, eed->v1->co);
457                                                                 break;
458                                                         }
459                                                 }
460                                         }
461
462                                         result = ORIENTATION_FACE;
463                                 }
464                                 else if (G.totedgesel == 1)
465                                 {
466                                         EditEdge *eed;
467
468                                         for(eed= em->edges.first; eed; eed= eed->next) {
469                                                 if(eed->f & SELECT) {
470                                                         /* use average vert normals as plane and edge vector as normal */
471                                                         VECCOPY(plane, eed->v1->no);
472                                                         VECADD(plane, plane, eed->v2->no);
473                                                         VecSubf(normal, eed->v2->co, eed->v1->co);
474                                                         break;
475                                                 }
476                                         }
477                                         result = ORIENTATION_EDGE;
478                                 }
479                                 else if (G.totvertsel == 2)
480                                 {
481                                         EditVert *v1 = NULL, *v2 = NULL;
482                 
483                                         for (eve = em->verts.first; eve; eve = eve->next)
484                                         {
485                                                 if ( eve->f & SELECT ) {
486                                                         if (v1 == NULL) {
487                                                                 v1 = eve; 
488                                                         }
489                                                         else {
490                                                                 v2 = eve;
491                                                                 
492                                                                 VECCOPY(plane, v1->no);
493                                                                 VECADD(plane, plane, v2->no);
494                                                                 VecSubf(normal, v2->co, v1->co);
495                                                                 break; 
496                                                         }
497                                                 }
498                                         }
499                                         result = ORIENTATION_EDGE;
500                                 }
501                                 else if (G.totvertsel == 1)
502                                 {
503                                         for (eve = em->verts.first; eve; eve = eve->next)
504                                         {
505                                                 if ( eve->f & SELECT ) {
506                                                         VECCOPY(normal, eve->no);
507                                                         break;
508                                                 }
509                                         }
510                                         result = ORIENTATION_VERT;
511                                 }
512                                 else if (G.totvertsel > 3)
513                                 {
514                                         normal[0] = normal[1] = normal[2] = 0;
515                                         
516                                         for (eve = em->verts.first; eve; eve = eve->next)
517                                         {
518                                                 if ( eve->f & SELECT ) {
519                                                         VecAddf(normal, normal, eve->no);
520                                                 }
521                                         }
522                                         Normalize(normal);
523                                         result = ORIENTATION_VERT;
524                                 }
525                         }
526                 } /* end editmesh */
527                 else if ELEM3(G.obedit->type, OB_CURVE, OB_SURF, OB_FONT)
528                 {
529                         extern ListBase editNurb; /* BOOO! go away stupid extern */
530                         Nurb *nu;
531                         BezTriple *bezt;
532                         int a;
533                         
534                         for (nu = editNurb.first; nu; nu = nu->next)
535                         {
536                                 /* only bezier has a normal */
537                                 if((nu->type & 7) == CU_BEZIER)
538                                 {
539                                         bezt= nu->bezt;
540                                         a= nu->pntsu;
541                                         while(a--)
542                                         {
543                                                 /* exception */
544                                                 if ( (bezt->f1 & SELECT) + (bezt->f2 & SELECT) + (bezt->f3 & SELECT) > SELECT )
545                                                 {
546                                                         VecSubf(normal, bezt->vec[0], bezt->vec[2]);
547                                                 }
548                                                 else
549                                                 {
550                                                         if(bezt->f1)
551                                                         {
552                                                                 VecSubf(normal, bezt->vec[0], bezt->vec[1]);
553                                                         }
554                                                         if(bezt->f2)
555                                                         {
556                                                                 VecSubf(normal, bezt->vec[0], bezt->vec[2]);
557                                                         }
558                                                         if(bezt->f3)
559                                                         {
560                                                                 VecSubf(normal, bezt->vec[1], bezt->vec[2]);
561                                                         }
562                                                 }
563                                                 bezt++;
564                                         }
565                                 }
566                         }
567                         
568                         if (normal[0] != 0 || normal[1] != 0 || normal[2] != 0)
569                         {
570                                 result = ORIENTATION_NORMAL;
571                         }
572                 }
573                 else if(G.obedit->type==OB_MBALL)
574                 {
575                         /* editmball.c */
576                         extern ListBase editelems;  /* go away ! */
577                         MetaElem *ml, *ml_sel = NULL;
578         
579                         /* loop and check that only one element is selected */  
580                         for (ml = editelems.first; ml; ml = ml->next)
581                         {
582                                 if (ml->flag & SELECT) {
583                                         if (ml_sel == NULL)
584                                         {
585                                                 ml_sel = ml;
586                                         }
587                                         else
588                                         {
589                                                 ml_sel = NULL;
590                                                 break;
591                                         }
592                                 }
593                         }
594                         
595                         if (ml_sel)
596                         {       
597                                 float mat[4][4];
598
599                                 /* Rotation of MetaElem is stored in quat */
600                                 QuatToMat4(ml_sel->quat, mat);
601
602                                 VECCOPY(normal, mat[2]);
603                                 VECCOPY(plane, mat[1]);
604
605                                 VecMulf(plane, -1.0);
606                                 
607                                 result = ORIENTATION_NORMAL;
608                         }
609                 }
610                 else if (G.obedit->type == OB_ARMATURE)
611                 {
612                         bArmature *arm = G.obedit->data;
613                         EditBone *ebone;
614                         
615                         for (ebone = G.edbo.first; ebone; ebone=ebone->next) {
616                                 if (arm->layer & ebone->layer)
617                                 {
618                                         if (ebone->flag & BONE_SELECTED)
619                                         {
620                                                 float vec[3];
621                                                 VecSubf(vec, ebone->tail, ebone->head);
622                                                 Normalize(vec);
623                                                 VecAddf(normal, normal, vec);
624                                         }
625                                 }
626                         }
627                         
628                         Normalize(normal);
629                         Crossf(plane, G.obedit->obmat[0], normal);
630                         
631                         if (Inpf(plane, plane) < FLT_EPSILON)
632                         {
633                                 Crossf(plane, G.obedit->obmat[1], normal);
634                         } 
635
636                         if (plane[0] != 0 || plane[1] != 0 || plane[2] != 0)
637                         {
638                                 result = ORIENTATION_EDGE;
639                         }
640
641                 }
642                 
643                 Mat4Mul3Vecfl(G.obedit->obmat, plane);
644                 Mat4Mul3Vecfl(G.obedit->obmat, normal);
645         }
646         else if(ob && (ob->flag & OB_POSEMODE))
647         {
648         }
649         else if(G.f & (G_VERTEXPAINT + G_TEXTUREPAINT + G_WEIGHTPAINT + G_SCULPTMODE))
650         {
651         }
652         else if(G.f & G_PARTICLEEDIT)
653         {
654         }
655         else {
656                 /* we need the one selected object, if its not active */
657                 ob = OBACT;
658                 if(ob && !(ob->flag & SELECT)) ob = NULL;
659                 
660                 for(base= G.scene->base.first; base; base= base->next) {
661                         if TESTBASELIB(base) {
662                                 if(ob == NULL) { 
663                                         ob= base->object;
664                                         break;
665                                 }
666                         }
667                 }
668                 
669                 VECCOPY(normal, ob->obmat[2]);
670                 VECCOPY(plane, ob->obmat[1]);
671                 result = ORIENTATION_NORMAL;
672         }
673         
674         return result;
675 }