1 /*
2  * This program is free software; you can redistribute it and/or
3  * modify it under the terms of the GNU General Public License
4  * as published by the Free Software Foundation; either version 2
5  * of the License, or (at your option) any later version.
6  *
7  * This program is distributed in the hope that it will be useful,
8  * but WITHOUT ANY WARRANTY; without even the implied warranty of
9  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
10  * GNU General Public License for more details.
11  *
12  * You should have received a copy of the GNU General Public License
13  * along with this program; if not, write to the Free Software Foundation,
14  * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
15  *
16  * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV.
17  * All rights reserved.
18  */
19 
20 /** \file
21  * \ingroup edtransform
22  */
23 
24 #include "DNA_meshdata_types.h"
25 
26 #include "MEM_guardedalloc.h"
27 
28 #include "BLI_bitmap.h"
29 #include "BLI_linklist_stack.h"
30 #include "BLI_math.h"
31 
32 #include "BKE_context.h"
33 #include "BKE_editmesh.h"
34 #include "BKE_mesh_mapping.h"
35 
36 #include "ED_image.h"
37 #include "ED_mesh.h"
38 #include "ED_uvedit.h"
39 
40 #include "WM_api.h" /* for WM_event_add_notifier to deal with stabilization nodes */
41 
42 #include "transform.h"
43 #include "transform_convert.h"
44 
45 /* -------------------------------------------------------------------- */
46 /** \name UVs Transform Creation
47  *
48  * \{ */
49 
UVsToTransData(const float aspect[2],TransData * td,TransData2D * td2d,float * uv,const float * center,float calc_dist,bool selected)50 static void UVsToTransData(const float aspect[2],
51                            TransData *td,
52                            TransData2D *td2d,
53                            float *uv,
54                            const float *center,
55                            float calc_dist,
56                            bool selected)
57 {
58   /* UV coords are scaled by aspects. this is needed for rotations and
59    * proportional editing to be consistent with the stretched UV coords
60    * that are displayed. this also means that for display and number-input,
61    * and when the UV coords are flushed, these are converted each time. */
62   td2d->loc[0] = uv[0] * aspect[0];
63   td2d->loc[1] = uv[1] * aspect[1];
64   td2d->loc[2] = 0.0f;
65   td2d->loc2d = uv;
66 
67   td->flag = 0;
68   td->loc = td2d->loc;
69   copy_v2_v2(td->center, center ? center : td->loc);
70   td->center[2] = 0.0f;
71   copy_v3_v3(td->iloc, td->loc);
72 
73   memset(td->axismtx, 0, sizeof(td->axismtx));
74   td->axismtx[2][2] = 1.0f;
75 
76   td->ext = NULL;
77   td->val = NULL;
78 
79   if (selected) {
80     td->flag |= TD_SELECTED;
81     td->dist = 0.0;
82   }
83   else {
84     td->dist = calc_dist;
85   }
86   unit_m3(td->mtx);
87   unit_m3(td->smtx);
88 }
89 
90 /**
91  * \param dists: Store the closest connected distance to selected vertices.
92  */
uv_set_connectivity_distance(BMesh * bm,float * dists,const float aspect[2])93 static void uv_set_connectivity_distance(BMesh *bm, float *dists, const float aspect[2])
94 {
95   /* Mostly copied from #editmesh_set_connectivity_distance. */
96   BLI_LINKSTACK_DECLARE(queue, BMLoop *);
97 
98   /* Any BM_ELEM_TAG'd loop is added to 'queue_next', this makes sure that we don't add things
99    * twice. */
100   BLI_LINKSTACK_DECLARE(queue_next, BMLoop *);
101 
102   BLI_LINKSTACK_INIT(queue);
103   BLI_LINKSTACK_INIT(queue_next);
104 
105   const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV);
106   BMIter fiter, liter;
107   BMVert *f;
108   BMLoop *l;
109 
110   BM_mesh_elem_index_ensure(bm, BM_LOOP);
111 
112   BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
113     /* Visible faces was tagged in #createTransUVs. */
114     if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
115       continue;
116     }
117 
118     BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
119       float dist;
120       MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
121 
122       bool uv_vert_sel = luv->flag & MLOOPUV_VERTSEL;
123 
124       if (uv_vert_sel) {
125         BLI_LINKSTACK_PUSH(queue, l);
126         dist = 0.0f;
127       }
128       else {
129         dist = FLT_MAX;
130       }
131 
132       /* Make sure all loops are in a clean tag state. */
133       BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0);
134 
135       int loop_idx = BM_elem_index_get(l);
136 
137       dists[loop_idx] = dist;
138     }
139   }
140 
141   /* Need to be very careful of feedback loops here, store previous dist's to avoid feedback. */
142   float *dists_prev = MEM_dupallocN(dists);
143 
144   do {
145     while ((l = BLI_LINKSTACK_POP(queue))) {
146       BLI_assert(dists[BM_elem_index_get(l)] != FLT_MAX);
147 
148       BMLoop *l_other, *l_connected;
149       BMIter l_connected_iter;
150 
151       MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
152       float l_uv[2];
153 
154       copy_v2_v2(l_uv, luv->uv);
155       mul_v2_v2(l_uv, aspect);
156 
157       BM_ITER_ELEM (l_other, &liter, l->f, BM_LOOPS_OF_FACE) {
158         if (l_other == l) {
159           continue;
160         }
161         float other_uv[2], edge_vec[2];
162         MLoopUV *luv_other = BM_ELEM_CD_GET_VOID_P(l_other, cd_loop_uv_offset);
163 
164         copy_v2_v2(other_uv, luv_other->uv);
165         mul_v2_v2(other_uv, aspect);
166 
167         sub_v2_v2v2(edge_vec, l_uv, other_uv);
168 
169         const int i = BM_elem_index_get(l);
170         const int i_other = BM_elem_index_get(l_other);
171         float dist = len_v2(edge_vec) + dists_prev[i];
172 
173         if (dist < dists[i_other]) {
174           dists[i_other] = dist;
175         }
176         else {
177           /* The face loop already has a shorter path to it. */
178           continue;
179         }
180 
181         bool other_vert_sel, connected_vert_sel;
182 
183         other_vert_sel = luv_other->flag & MLOOPUV_VERTSEL;
184 
185         BM_ITER_ELEM (l_connected, &l_connected_iter, l_other->v, BM_LOOPS_OF_VERT) {
186           if (l_connected == l_other) {
187             continue;
188           }
189           /* Visible faces was tagged in #createTransUVs. */
190           if (!BM_elem_flag_test(l_connected->f, BM_ELEM_TAG)) {
191             continue;
192           }
193 
194           MLoopUV *luv_connected = BM_ELEM_CD_GET_VOID_P(l_connected, cd_loop_uv_offset);
195           connected_vert_sel = luv_connected->flag & MLOOPUV_VERTSEL;
196 
197           /* Check if this loop is connected in UV space.
198            * If the uv loops share the same selection state (if not, they are not connected as
199            * they have been ripped or other edit commands have separated them). */
200           bool connected = other_vert_sel == connected_vert_sel &&
201                            equals_v2v2(luv_other->uv, luv_connected->uv);
202           if (!connected) {
203             continue;
204           }
205 
206           /* The loop vert is occupying the same space, so it has the same distance. */
207           const int i_connected = BM_elem_index_get(l_connected);
208           dists[i_connected] = dist;
209 
210           if (BM_elem_flag_test(l_connected, BM_ELEM_TAG) == 0) {
211             BM_elem_flag_enable(l_connected, BM_ELEM_TAG);
212             BLI_LINKSTACK_PUSH(queue_next, l_connected);
213           }
214         }
215       }
216     }
217 
218     /* Clear elem flags for the next loop. */
219     for (LinkNode *lnk = queue_next; lnk; lnk = lnk->next) {
220       BMLoop *l_link = lnk->link;
221       const int i = BM_elem_index_get(l_link);
222 
223       BM_elem_flag_disable(l_link, BM_ELEM_TAG);
224 
225       /* Store all new dist values. */
226       dists_prev[i] = dists[i];
227     }
228 
229     BLI_LINKSTACK_SWAP(queue, queue_next);
230 
231   } while (BLI_LINKSTACK_SIZE(queue));
232 
233 #ifndef NDEBUG
234   /* Check that we didn't leave any loops tagged */
235   BM_ITER_MESH (f, &fiter, bm, BM_FACES_OF_MESH) {
236     /* Visible faces was tagged in #createTransUVs. */
237     if (!BM_elem_flag_test(f, BM_ELEM_TAG)) {
238       continue;
239     }
240 
241     BM_ITER_ELEM (l, &liter, f, BM_LOOPS_OF_FACE) {
242       BLI_assert(BM_elem_flag_test(l, BM_ELEM_TAG) == 0);
243     }
244   }
245 #endif
246 
247   BLI_LINKSTACK_FREE(queue);
248   BLI_LINKSTACK_FREE(queue_next);
249 
250   MEM_freeN(dists_prev);
251 }
252 
createTransUVs(bContext * C,TransInfo * t)253 void createTransUVs(bContext *C, TransInfo *t)
254 {
255   SpaceImage *sima = CTX_wm_space_image(C);
256   Scene *scene = t->scene;
257   ToolSettings *ts = CTX_data_tool_settings(C);
258 
259   const bool is_prop_edit = (t->flag & T_PROP_EDIT) != 0;
260   const bool is_prop_connected = (t->flag & T_PROP_CONNECTED) != 0;
261   const bool is_island_center = (t->around == V3D_AROUND_LOCAL_ORIGINS);
262 
263   FOREACH_TRANS_DATA_CONTAINER (t, tc) {
264 
265     TransData *td = NULL;
266     TransData2D *td2d = NULL;
267     BMEditMesh *em = BKE_editmesh_from_object(tc->obedit);
268     BMFace *efa;
269     BMIter iter, liter;
270     UvElementMap *elementmap = NULL;
271     struct {
272       float co[2];
273       int co_num;
274     } *island_center = NULL;
275     int count = 0, countsel = 0;
276     const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV);
277 
278     if (!ED_space_image_show_uvedit(sima, tc->obedit)) {
279       continue;
280     }
281 
282     /* count */
283     if (is_island_center) {
284       /* create element map with island information */
285       const bool use_facesel = (ts->uv_flag & UV_SYNC_SELECTION) == 0;
286       elementmap = BM_uv_element_map_create(em->bm, scene, use_facesel, true, false, true);
287       if (elementmap == NULL) {
288         continue;
289       }
290 
291       island_center = MEM_callocN(sizeof(*island_center) * elementmap->totalIslands, __func__);
292     }
293 
294     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
295       BMLoop *l;
296 
297       if (!uvedit_face_visible_test(scene, efa)) {
298         BM_elem_flag_disable(efa, BM_ELEM_TAG);
299         continue;
300       }
301 
302       BM_elem_flag_enable(efa, BM_ELEM_TAG);
303       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
304         /* Make sure that the loop element flag is cleared for when we use it in
305          * uv_set_connectivity_distance later. */
306         BM_elem_flag_disable(l, BM_ELEM_TAG);
307         if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) {
308           countsel++;
309 
310           if (island_center) {
311             UvElement *element = BM_uv_element_get(elementmap, efa, l);
312 
313             if (element->flag == false) {
314               MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
315               add_v2_v2(island_center[element->island].co, luv->uv);
316               island_center[element->island].co_num++;
317               element->flag = true;
318             }
319           }
320         }
321 
322         if (is_prop_edit) {
323           count++;
324         }
325       }
326     }
327 
328     float *prop_dists = NULL;
329 
330     /* Support other objects using PET to adjust these, unless connected is enabled. */
331     if (((is_prop_edit && !is_prop_connected) ? count : countsel) == 0) {
332       goto finally;
333     }
334 
335     if (is_island_center) {
336       int i;
337 
338       for (i = 0; i < elementmap->totalIslands; i++) {
339         mul_v2_fl(island_center[i].co, 1.0f / island_center[i].co_num);
340         mul_v2_v2(island_center[i].co, t->aspect);
341       }
342     }
343 
344     tc->data_len = (is_prop_edit) ? count : countsel;
345     tc->data = MEM_callocN(tc->data_len * sizeof(TransData), "TransObData(UV Editing)");
346     /* for each 2d uv coord a 3d vector is allocated, so that they can be
347      * treated just as if they were 3d verts */
348     tc->data_2d = MEM_callocN(tc->data_len * sizeof(TransData2D), "TransObData2D(UV Editing)");
349 
350     if (sima->flag & SI_CLIP_UV) {
351       t->flag |= T_CLIP_UV;
352     }
353 
354     td = tc->data;
355     td2d = tc->data_2d;
356 
357     if (is_prop_connected) {
358       prop_dists = MEM_callocN(em->bm->totloop * sizeof(float), "TransObPropDists(UV Editing)");
359 
360       uv_set_connectivity_distance(em->bm, prop_dists, t->aspect);
361     }
362 
363     BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) {
364       BMLoop *l;
365 
366       if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) {
367         continue;
368       }
369 
370       BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) {
371         const bool selected = uvedit_uv_select_test(scene, l, cd_loop_uv_offset);
372         MLoopUV *luv;
373         const float *center = NULL;
374         float prop_distance = FLT_MAX;
375 
376         if (!is_prop_edit && !selected) {
377           continue;
378         }
379 
380         if (is_prop_connected) {
381           const int idx = BM_elem_index_get(l);
382           prop_distance = prop_dists[idx];
383         }
384 
385         if (is_island_center) {
386           UvElement *element = BM_uv_element_get(elementmap, efa, l);
387           if (element) {
388             center = island_center[element->island].co;
389           }
390         }
391 
392         luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset);
393         UVsToTransData(t->aspect, td++, td2d++, luv->uv, center, prop_distance, selected);
394       }
395     }
396 
397     if (sima->flag & SI_LIVE_UNWRAP) {
398       ED_uvedit_live_unwrap_begin(t->scene, tc->obedit);
399     }
400 
401   finally:
402     if (is_prop_connected) {
403       MEM_SAFE_FREE(prop_dists);
404     }
405     if (is_island_center) {
406       BM_uv_element_map_free(elementmap);
407 
408       MEM_freeN(island_center);
409     }
410   }
411 }
412 
413 /** \} */
414 
415 /* -------------------------------------------------------------------- */
416 /** \name UVs Transform Flush
417  *
418  * \{ */
419 
flushTransUVs(TransInfo * t)420 static void flushTransUVs(TransInfo *t)
421 {
422   SpaceImage *sima = t->area->spacedata.first;
423   const bool use_pixel_snap = ((sima->pixel_snap_mode != SI_PIXEL_SNAP_DISABLED) &&
424                                (t->state != TRANS_CANCEL));
425 
426   FOREACH_TRANS_DATA_CONTAINER (t, tc) {
427     TransData2D *td;
428     int a;
429     float aspect_inv[2], size[2];
430 
431     aspect_inv[0] = 1.0f / t->aspect[0];
432     aspect_inv[1] = 1.0f / t->aspect[1];
433 
434     if (use_pixel_snap) {
435       int size_i[2];
436       ED_space_image_get_size(sima, &size_i[0], &size_i[1]);
437       size[0] = size_i[0];
438       size[1] = size_i[1];
439     }
440 
441     /* flush to 2d vector from internally used 3d vector */
442     for (a = 0, td = tc->data_2d; a < tc->data_len; a++, td++) {
443       td->loc2d[0] = td->loc[0] * aspect_inv[0];
444       td->loc2d[1] = td->loc[1] * aspect_inv[1];
445 
446       if (use_pixel_snap) {
447         td->loc2d[0] *= size[0];
448         td->loc2d[1] *= size[1];
449 
450         switch (sima->pixel_snap_mode) {
451           case SI_PIXEL_SNAP_CENTER:
452             td->loc2d[0] = roundf(td->loc2d[0] - 0.5f) + 0.5f;
453             td->loc2d[1] = roundf(td->loc2d[1] - 0.5f) + 0.5f;
454             break;
455           case SI_PIXEL_SNAP_CORNER:
456             td->loc2d[0] = roundf(td->loc2d[0]);
457             td->loc2d[1] = roundf(td->loc2d[1]);
458             break;
459         }
460 
461         td->loc2d[0] /= size[0];
462         td->loc2d[1] /= size[1];
463       }
464     }
465   }
466 }
467 
468 /* helper for recalcData() - for Image Editor transforms */
recalcData_uv(TransInfo * t)469 void recalcData_uv(TransInfo *t)
470 {
471   SpaceImage *sima = t->area->spacedata.first;
472 
473   flushTransUVs(t);
474   if (sima->flag & SI_LIVE_UNWRAP) {
475     ED_uvedit_live_unwrap_re_solve();
476   }
477 
478   FOREACH_TRANS_DATA_CONTAINER (t, tc) {
479     if (tc->data_len) {
480       DEG_id_tag_update(tc->obedit->data, 0);
481     }
482   }
483 }
484 
485 /** \} */
486