diff options
17 files changed, 3121 insertions, 112 deletions
diff --git a/release/scripts/startup/bl_operators/uvcalc_follow_active.py b/release/scripts/startup/bl_operators/uvcalc_follow_active.py index 25ee5cafe81..7a4a1bc3601 100644 --- a/release/scripts/startup/bl_operators/uvcalc_follow_active.py +++ b/release/scripts/startup/bl_operators/uvcalc_follow_active.py @@ -217,6 +217,8 @@ def extend(obj, operator, EXTEND_MODE): def main(context, operator): obj = context.active_object + bpy.ops.uv.reveal() + extend(obj, operator, operator.properties.mode) @@ -252,4 +254,4 @@ class FollowActiveQuads(Operator): classes = ( FollowActiveQuads, -)
\ No newline at end of file +) diff --git a/release/scripts/startup/bl_operators/uvcalc_lightmap.py b/release/scripts/startup/bl_operators/uvcalc_lightmap.py index 8ee29d15d1b..173fe823b50 100644 --- a/release/scripts/startup/bl_operators/uvcalc_lightmap.py +++ b/release/scripts/startup/bl_operators/uvcalc_lightmap.py @@ -557,7 +557,9 @@ def lightmap_uvpack(meshes, def unwrap(operator, context, **kwargs): is_editmode = (context.object.mode == 'EDIT') + if is_editmode: + bpy.ops.uv.reveal() bpy.ops.object.mode_set(mode='OBJECT', toggle=False) PREF_ACT_ONLY = kwargs.pop("PREF_ACT_ONLY") @@ -672,4 +674,4 @@ class LightMapPack(Operator): classes = ( LightMapPack, -)
\ No newline at end of file +) diff --git a/release/scripts/startup/bl_operators/uvcalc_smart_project.py b/release/scripts/startup/bl_operators/uvcalc_smart_project.py index cc590ac9502..0ae600c8046 100644 --- a/release/scripts/startup/bl_operators/uvcalc_smart_project.py +++ b/release/scripts/startup/bl_operators/uvcalc_smart_project.py @@ -750,6 +750,7 @@ def main(context, is_editmode = (context.active_object.mode == 'EDIT') if is_editmode: + bpy.ops.uv.reveal() obList = [ob for ob in [context.active_object] if ob and ob.type == 'MESH'] else: obList = [ob for ob in context.selected_editable_objects if ob and ob.type == 'MESH'] diff --git a/release/scripts/startup/bl_ui/space_image.py b/release/scripts/startup/bl_ui/space_image.py index 340a6c807df..9868186e39c 100644 --- a/release/scripts/startup/bl_ui/space_image.py +++ b/release/scripts/startup/bl_ui/space_image.py @@ -130,6 +130,12 @@ class IMAGE_MT_view(Menu): layout.operator("screen.screen_full_area") layout.operator("screen.screen_full_area", text="Toggle Fullscreen Area").use_hide_panels = True +class IMAGE_MT_uvs_select_by_trait(Menu): + bl_label = "Select All by Trait" + + def draw(self, context): + layout = self.layout + layout.operator("uv.select_overlapping") class IMAGE_MT_select(Menu): bl_label = "Select" @@ -150,8 +156,11 @@ class IMAGE_MT_select(Menu): layout.operator("uv.select_pinned") layout.operator("uv.select_linked").extend = False + layout.operator("uv.select_shortest_path") layout.separator() + layout.menu("IMAGE_MT_uvs_select_by_trait") + layout.separator() layout.operator("uv.select_less", text="Less") layout.operator("uv.select_more", text="More") @@ -259,6 +268,16 @@ class IMAGE_MT_uvs_showhide(Menu): layout.operator("uv.hide", text="Hide Selected").unselected = False layout.operator("uv.hide", text="Hide Unselected").unselected = True +class IMAGE_MT_uvs_deselect_mesh(Menu): + bl_label = "De/Select 3D Mesh" + + def draw(self, context): + layout = self.layout + + layout.operator("uv.select_mesh") + layout.operator("uv.deselect_mesh", text="Deselect 3D Mesh (Selected)").unselected = False + layout.operator("uv.deselect_mesh", text="Deselect 3D Mesh (Unselected)").unselected = True + class IMAGE_MT_uvs_proportional(Menu): bl_label = "Proportional Editing" @@ -357,6 +376,7 @@ class IMAGE_MT_uvs(Menu): layout.separator() + layout.operator("uv.irregular_pack_islands") layout.operator("uv.pack_islands") layout.operator("uv.average_islands_scale") layout.operator("uv.minimize_stretch") @@ -365,6 +385,7 @@ class IMAGE_MT_uvs(Menu): layout.operator("uv.mark_seam", text="Clear Seam").clear = True layout.operator("uv.seams_from_islands") layout.operator("mesh.faces_mirror_uv") + layout.operator("uv.scale_to_bounds") layout.separator() @@ -380,6 +401,9 @@ class IMAGE_MT_uvs(Menu): layout.separator() layout.menu("IMAGE_MT_uvs_showhide") + + layout.menu("IMAGE_MT_uvs_deselect_mesh") + class IMAGE_MT_uvs_select_mode(Menu): diff --git a/source/blender/blenlib/BLI_math_geom.h b/source/blender/blenlib/BLI_math_geom.h index 0fef849c8fa..2893da9bce8 100644 --- a/source/blender/blenlib/BLI_math_geom.h +++ b/source/blender/blenlib/BLI_math_geom.h @@ -401,6 +401,8 @@ void accumulate_vertex_normals_poly( float **vertnos, const float polyno[3], const float **vertcos, float vdiffs[][3], const int nverts); +void edge_normal_v2_v2v2(float r[2], const float a[2], const float b[2], const bool left); + /********************************* Tangents **********************************/ void tangent_from_uv( diff --git a/source/blender/blenlib/intern/math_geom.c b/source/blender/blenlib/intern/math_geom.c index 53fcf9c745c..30164ff9f6a 100644 --- a/source/blender/blenlib/intern/math_geom.c +++ b/source/blender/blenlib/intern/math_geom.c @@ -4014,6 +4014,23 @@ void accumulate_vertex_normals_poly(float **vertnos, const float polyno[3], } } +/* Calculates the 2d normal for an edge ab. + * if left is true calculates the left normal (viewed in winding direction) */ +void edge_normal_v2_v2v2(float r[2], const float a[2], const float b[2], const bool left) +{ + float dx = b[0] - a[0]; + float dy = b[1] - a[1]; + + if (left) { + r[0] = -dy; + r[1] = dx; + } + else { + r[0] = dy; + r[1] = -dx; + } +} + /********************************* Tangents **********************************/ void tangent_from_uv( diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index c867df2d01a..b5e54a56996 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -128,6 +128,9 @@ void EDBM_flag_disable_all(struct BMEditMesh *em, const char hflag); bool BMBVH_EdgeVisible(struct BMBVHTree *tree, struct BMEdge *e, struct ARegion *ar, struct View3D *v3d, struct Object *obedit); +/* editmesh_path.c*/ +int EDBM_shortest_path_select(struct bContext *C, struct wmOperator *op); + /* editmesh_select.c */ void EDBM_select_mirrored( struct BMEditMesh *em, const int axis, const bool extend, diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h index 535683823bf..9c920d33053 100644 --- a/source/blender/editors/include/ED_uvedit.h +++ b/source/blender/editors/include/ED_uvedit.h @@ -59,6 +59,8 @@ void ED_object_assign_active_image(struct Main *bmain, struct Object *ob, int ma bool ED_uvedit_test(struct Object *obedit); +void ED_uvedit_reveal(struct BMEditMesh *em); + /* visibility and selection */ bool uvedit_face_visible_test(struct Scene *scene, struct Image *ima, struct BMFace *efa, struct MTexPoly *tf); bool uvedit_face_select_test(struct Scene *scene, struct BMFace *efa, @@ -106,6 +108,14 @@ void ED_uvedit_unwrap_cube_project(struct Object *ob, struct BMesh *bm, float cu /* single call up unwrap using scene settings, used for edge tag unwrapping */ void ED_unwrap_lscm(struct Scene *scene, struct Object *obedit, const short sel); +/* select by trait */ +void ED_uvedit_overlapping_select(struct Scene *scene, struct Object *ob, struct BMesh *bm, const bool extend); + +/* select shortest path */ +bool ED_uvedit_shortest_path_select(struct Scene *scene, struct Object *ob, struct BMesh *bm, bool topo_dist); + +/* scale to bounds */ +void ED_uvedit_scale_to_bounds(struct Scene *scene, struct Object *ob, struct BMesh *bm); /* uvedit_draw.c */ void ED_image_draw_cursor(struct ARegion *ar, const float cursor[2]); diff --git a/source/blender/editors/mesh/editmesh_path.c b/source/blender/editors/mesh/editmesh_path.c index 4431712e720..a044cb263e5 100644 --- a/source/blender/editors/mesh/editmesh_path.c +++ b/source/blender/editors/mesh/editmesh_path.c @@ -779,6 +779,11 @@ static int edbm_shortest_path_select_exec(bContext *C, wmOperator *op) } } +int EDBM_shortest_path_select(struct bContext *C, struct wmOperator *op) +{ + return edbm_shortest_path_select_exec(C, op); +} + void MESH_OT_shortest_path_select(wmOperatorType *ot) { /* identifiers */ diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c index 15be6ab3b78..d4ceaa39d3c 100644 --- a/source/blender/editors/uvedit/uvedit_draw.c +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -222,7 +222,9 @@ static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, BMEditMesh *em, MTe glBegin(GL_POLYGON); BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + glVertex2fv(luv->uv); + } } glEnd(); } @@ -260,7 +262,9 @@ static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, BMEditMesh *em, MTe glBegin(GL_POLYGON); BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + glVertex2fv(luv->uv); + } } glEnd(); } @@ -323,10 +327,12 @@ static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, BMEditMesh *em, MTe glBegin(GL_POLYGON); BM_ITER_ELEM_INDEX (l, &liter, efa, BM_LOOPS_OF_FACE, i) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - a = fabsf(uvang[i] - ang[i]) / (float)M_PI; - weight_to_rgb(col, 1.0f - pow2f(1.0f - a)); - glColor3fv(col); - glVertex2fv(luv->uv); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + a = fabsf(uvang[i] - ang[i]) / (float)M_PI; + weight_to_rgb(col, 1.0f - pow2f(1.0f - a)); + glColor3fv(col); + glVertex2fv(luv->uv); + } } glEnd(); } @@ -359,7 +365,9 @@ static void draw_uvs_lineloop_bmface(BMFace *efa, const int cd_loop_uv_offset) glBegin(GL_LINE_LOOP); BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + glVertex2fv(luv->uv); + } } glEnd(); } @@ -372,7 +380,7 @@ static void draw_uvs_lineloop_mpoly(Mesh *me, MPoly *mpoly) glBegin(GL_LINE_LOOP); mloopuv = &me->mloopuv[mpoly->loopstart]; for (i = mpoly->totloop; i != 0; i--, mloopuv++) { - glVertex2fv(mloopuv->uv); + glVertex2fv(mloopuv->uv); /* We don't check MLOOPUV_HIDDEN here since we always want to see other UVs */ } glEnd(); } @@ -537,7 +545,9 @@ static void draw_uvs_looptri(BMEditMesh *em, unsigned int *r_loop_index, const i unsigned int j; for (j = 0; j < 3; j++) { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(em->looptris[i][j], cd_loop_uv_offset); - glVertex2fv(luv->uv); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + glVertex2fv(luv->uv); + } } i++; } while (i != em->tottri && (f == em->looptris[i][0]->f)); @@ -813,7 +823,9 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) glColor4ubv(sel ? (GLubyte *)col1 : (GLubyte *)col2); luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + glVertex2fv(luv->uv); + } } glEnd(); } @@ -831,9 +843,13 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) lastsel = sel; } luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - glVertex2fv(luv->uv); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + glVertex2fv(luv->uv); + } luv = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - glVertex2fv(luv->uv); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + glVertex2fv(luv->uv); + } } glEnd(); } @@ -874,9 +890,13 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) continue; + tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + if (!uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { - uv_poly_center(efa, cent, cd_loop_uv_offset); - glVertex2fv(cent); + if (uv_poly_visible(efa, cd_loop_uv_offset)) { + uv_poly_center(efa, cent, cd_loop_uv_offset); + glVertex2fv(cent); + } } } @@ -887,9 +907,13 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) if (!BM_elem_flag_test(efa, BM_ELEM_TAG)) continue; + tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + if (uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { - uv_poly_center(efa, cent, cd_loop_uv_offset); - glVertex2fv(cent); + if (uv_poly_visible(efa, cd_loop_uv_offset)) { + uv_poly_center(efa, cent, cd_loop_uv_offset); + glVertex2fv(cent); + } } } @@ -911,7 +935,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (!uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) + if (!uvedit_uv_select_test(scene, l, cd_loop_uv_offset) && !(luv->flag & MLOOPUV_HIDDEN)) glVertex2fv(luv->uv); } } @@ -930,7 +954,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (luv->flag & MLOOPUV_PINNED) + if (luv->flag & MLOOPUV_PINNED && !(luv->flag & MLOOPUV_HIDDEN)) glVertex2fv(luv->uv); } } @@ -948,7 +972,7 @@ static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset)) + if (uvedit_uv_select_test(scene, l, cd_loop_uv_offset) && !(luv->flag & MLOOPUV_HIDDEN)) glVertex2fv(luv->uv); } } diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h index e028c08091c..5d02c68718c 100644 --- a/source/blender/editors/uvedit/uvedit_intern.h +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -48,6 +48,7 @@ bool uvedit_face_visible_nolocal(struct Scene *scene, struct BMFace *efa); /* geometric utilities */ void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len); void uv_poly_center(struct BMFace *f, float r_cent[2], const int cd_loop_uv_offset); +bool uv_poly_visible(struct BMFace *f, const int cd_loop_uv_offset); /* find nearest */ @@ -59,6 +60,14 @@ typedef struct NearestHit { int lindex; /* index of loop within face */ } NearestHit; +enum { + HANDLE_IMPLICIT = (1 << 0), + HANDLE_FILL = (1 << 1), + HANDLE_SELECTED = (1 << 2), + HANDLE_CORRECT_ASPECT = (1 << 3), + HANDLE_ALL_FACES = (1 << 4) +}; + void uv_find_nearest_vert(struct Scene *scene, struct Image *ima, struct BMEditMesh *em, const float co[2], const float penalty[2], struct NearestHit *hit); void uv_find_nearest_edge(struct Scene *scene, struct Image *ima, struct BMEditMesh *em, @@ -76,9 +85,13 @@ void UV_OT_cylinder_project(struct wmOperatorType *ot); void UV_OT_project_from_view(struct wmOperatorType *ot); void UV_OT_minimize_stretch(struct wmOperatorType *ot); void UV_OT_pack_islands(struct wmOperatorType *ot); +void UV_OT_irregular_pack_islands(struct wmOperatorType *ot); void UV_OT_reset(struct wmOperatorType *ot); void UV_OT_sphere_project(struct wmOperatorType *ot); void UV_OT_unwrap(struct wmOperatorType *ot); void UV_OT_stitch(struct wmOperatorType *ot); +/* XXX (SaphireS): Remove */ +void UV_OT_test(struct wmOperatorType *ot); + #endif /* __UVEDIT_INTERN_H__ */ diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c index 75294af08f9..9ae5145e53a 100644 --- a/source/blender/editors/uvedit/uvedit_ops.c +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -407,7 +407,9 @@ bool uvedit_face_select_enable(Scene *scene, BMEditMesh *em, BMFace *efa, const BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->flag |= MLOOPUV_VERTSEL; + if (!(luv->flag & MLOOPUV_HIDDEN)) { + luv->flag |= MLOOPUV_VERTSEL; + } } return true; @@ -505,8 +507,12 @@ void uvedit_edge_select_enable(BMEditMesh *em, Scene *scene, BMLoop *l, const bo luv1 = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); luv2 = BM_ELEM_CD_GET_VOID_P(l->next, cd_loop_uv_offset); - luv1->flag |= MLOOPUV_VERTSEL; - luv2->flag |= MLOOPUV_VERTSEL; + if (!(luv1->flag & MLOOPUV_HIDDEN)) { + luv1->flag |= MLOOPUV_VERTSEL; + } + if (!(luv2->flag & MLOOPUV_HIDDEN)) { + luv2->flag |= MLOOPUV_VERTSEL; + } } } @@ -582,7 +588,9 @@ void uvedit_uv_select_enable(BMEditMesh *em, Scene *scene, BMLoop *l, } else { MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); - luv->flag |= MLOOPUV_VERTSEL; + if (!(luv->flag & MLOOPUV_HIDDEN)) { + luv->flag |= MLOOPUV_VERTSEL; + } } } @@ -631,6 +639,24 @@ void uv_poly_center(BMFace *f, float r_cent[2], const int cd_loop_uv_offset) mul_v2_fl(r_cent, 1.0f / (float)f->len); } +bool uv_poly_visible(BMFace *f, const int cd_loop_uv_offset) +{ + BMLoop *l; + MLoopUV *luv; + BMIter liter; + bool visible = false; + + BM_ITER_ELEM(l, &liter, f, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if (!(luv->flag & MLOOPUV_HIDDEN)) { + visible = true; + break; + } + } + + return visible; +} + void uv_poly_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy, int len) { int i; @@ -1477,6 +1503,246 @@ static void UV_OT_select_less(wmOperatorType *ot) ot->poll = ED_operator_uvedit_space_image; } +/*********************** shortest path ***********************/ +static int uv_shortest_path_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + Image *ima = CTX_data_edit_image(C); + ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + BMFace *efa; + BMEdge *e; + BMIter iter, liter; + BMLoop *l; + MLoopUV *luv_src = NULL, *luv_dst = NULL; + BMElem *elem_src = NULL, *elem_dst = NULL; + int elem_sel = 0; + const bool topological_distance = RNA_boolean_get(op->ptr, "topological_distance"); + + + if (ts->uv_flag & UV_SYNC_SELECTION) { + return EDBM_shortest_path_select(C, op); + } + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); + + /* -------- Check for 2 selected elements of same type on the same UV island ---------- */ + /* Note: Only vertex path computation implemented for now, but Edge/Face checks already there*/ + if (ts->uv_selectmode & UV_SELECT_FACE) { + /* clear tags */ + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_elem_flag_disable(efa, BM_ELEM_TAG); + } + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + + if (uvedit_face_visible_test(scene, ima, efa, tf)) { + if (uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) { + elem_sel++; + + if (elem_src == NULL) { + elem_src = (BMElem *)efa; + } + else if ((elem_dst == NULL)) { + elem_dst = (BMElem *)efa; + } + } + } + } + } + else if (ts->uv_selectmode & UV_SELECT_EDGE) { + /* clear tags */ + BM_ITER_MESH(e, &iter, bm, BM_EDGES_OF_MESH) { + BM_elem_flag_disable(e, BM_ELEM_TAG); + } + + BM_ITER_MESH(e, &iter, bm, BM_EDGES_OF_MESH) { + if (uvedit_edge_select_test(scene, e->l, cd_loop_uv_offset)) { + + elem_sel++; + + if (elem_src == NULL) { + elem_src = (BMElem *)e; + } + else if ((elem_dst == NULL)) { + elem_dst = (BMElem *)e; + } + } + } + } + + else if (ts->uv_selectmode & UV_SELECT_VERTEX) { + /* clear tags */ + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_elem_flag_disable(l, BM_ELEM_TAG); + } + } + + BM_ITER_MESH (efa, &iter, bm, BM_FACES_OF_MESH) { + MTexPoly *tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + + if (uvedit_face_visible_test(scene, ima, efa, tf)) { + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + if ((luv->flag & MLOOPUV_VERTSEL) != 0) { + if (luv_src == NULL) { + luv_src = luv; + elem_sel++; + } + else if ((luv_dst == NULL) && (!compare_v2v2(luv->uv, luv_src->uv, 0.000003f))) { + luv_dst = luv; + elem_sel++; + } + else if ((!compare_v2v2(luv->uv, luv_src->uv, 0.000003f)) && + (!compare_v2v2(luv->uv, luv_dst->uv, 0.000003f))) { + elem_sel++; + } + } + } + } + } + } + + if (elem_sel != 2 || !(ts->uv_selectmode & UV_SELECT_VERTEX)) { + /* Not exactly 2 elements of same typ selected */ + BKE_report(op->reports, RPT_WARNING, + "Path selection requires exactly two vertices of the same island to be selected"); + return OPERATOR_CANCELLED; + } + + /* -------- Now select shortest path between the 2 found elements ---------- */ + + if (ED_uvedit_shortest_path_select(scene, obedit, bm, topological_distance)) { + + DAG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT | ND_DATA, obedit->data); + + return OPERATOR_FINISHED; + } + else { + /* No path found because the selected elements aren't part of the same uv island */ + BKE_report(op->reports, RPT_WARNING, + "Path selection requires exactly two vertices of the same island to be selected"); + return OPERATOR_CANCELLED; + } +} + +static void UV_OT_select_shortest_path(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Shortest Vertex Path"; + ot->description = "Select the shortest path between the current selected vertices"; + ot->idname = "UV_OT_select_shortest_path"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_shortest_path_exec; + ot->poll = ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, "topological_distance", 0, "Topological Distance", + "Find the minimum number of steps, ignoring spatial distance"); +} +/* ******************** scale to bounds operator **************** */ + +static int uv_scale_to_bounds_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + Image *ima = CTX_data_edit_image(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MTexPoly *tf; + float dx, dy, min[2], max[2]; + + const bool keep_aspect = RNA_boolean_get(op->ptr, "keep_aspect_ratio"); + const bool individual = RNA_boolean_get(op->ptr, "individual_islands"); + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); + + if (individual) { + ED_uvedit_scale_to_bounds(scene, obedit, bm); + + return OPERATOR_FINISHED; + } + + INIT_MINMAX2(min, max); + + BM_ITER_MESH(efa, &iter, bm, BM_FACES_OF_MESH) { + tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + + if (!uvedit_face_visible_test(scene, ima, efa, tf) || !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) + continue; + + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + minmax_v2v2_v2(min, max, luv->uv); + } + } + + /* rescale UV to be in 1/1 */ + dx = (max[0] - min[0]); + dy = (max[1] - min[1]); + + if (dx > 0.0f) + dx = 1.0f / dx; + if (dy > 0.0f) + dy = 1.0f / dy; + + if (keep_aspect) { + if (dx >= dy) dx = dy; + else if (dy > dx) dy = dx; + } + + BM_ITER_MESH(efa, &iter, bm, BM_FACES_OF_MESH) { + tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + + if (!uvedit_face_visible_test(scene, ima, efa, tf) || !uvedit_face_select_test(scene, efa, cd_loop_uv_offset)) + continue; + + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { + MLoopUV *luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + + luv->uv[0] = (luv->uv[0] - min[0]) * dx; + luv->uv[1] = (luv->uv[1] - min[1]) * dy; + } + } + + DAG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + + return OPERATOR_FINISHED; +} + +static void UV_OT_scale_to_bounds(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Scale To Bounds"; + ot->description = "Scale the selection to fit UV boundaries"; + ot->idname = "UV_OT_scale_to_bounds"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_scale_to_bounds_exec; + ot->poll = ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, "keep_aspect_ratio", 1, "Keep Aspect Ratio", "Keep the current aspect ratio of the selection"); + RNA_def_boolean(ot->srna, "individual_islands", 0, "Individual", "Scale individual islands or the selection as a whole"); +} + /* ******************** align operator **************** */ static void uv_weld_align(bContext *C, int tool) @@ -1990,13 +2256,19 @@ static void uv_select_all_perform(Scene *scene, Image *ima, BMEditMesh *em, int switch (action) { case SEL_SELECT: - luv->flag |= MLOOPUV_VERTSEL; + if (!(luv->flag & MLOOPUV_HIDDEN)) { /* Skip hidden loops */ + luv->flag |= MLOOPUV_VERTSEL; + } break; case SEL_DESELECT: - luv->flag &= ~MLOOPUV_VERTSEL; + if (!(luv->flag & MLOOPUV_HIDDEN)) { + luv->flag &= ~MLOOPUV_VERTSEL; + } break; case SEL_INVERT: - luv->flag ^= MLOOPUV_VERTSEL; + if (!(luv->flag & MLOOPUV_HIDDEN)) { + luv->flag ^= MLOOPUV_VERTSEL; + } break; } } @@ -3628,13 +3900,138 @@ static void UV_OT_select_pinned(wmOperatorType *ot) /********************** hide operator *********************/ +static int uv_hide_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima = CTX_wm_space_image(C); + Object *obedit = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + MTexPoly *tf; + const bool swap = RNA_boolean_get(op->ptr, "unselected"); + Image *ima = sima ? sima->image : NULL; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); + + if (ts->uv_flag & UV_SYNC_SELECTION) { + EDBM_mesh_hide(em, swap); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + + return OPERATOR_FINISHED; + } + + BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + + tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); + + if (!uvedit_face_visible_test(scene, ima, efa, tf)) { + continue; + } + + BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + if ((luv->flag & MLOOPUV_VERTSEL && !swap) || (!(luv->flag & MLOOPUV_VERTSEL) && swap)) { + luv->flag |= MLOOPUV_HIDDEN; + luv->flag &= ~MLOOPUV_VERTSEL; /* Deselect after hiding to avoid unwanted behaviour */ + } + } + } + + BM_select_history_validate(em->bm); + WM_event_add_notifier(C, NC_GEOM | ND_SELECT | ND_DATA, obedit->data); + + return OPERATOR_FINISHED; +} + +#undef UV_SEL_TEST + +static void UV_OT_hide(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Hide Selected"; + ot->description = "Hide (un)selected UV vertices"; + ot->idname = "UV_OT_hide"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_hide_exec; + ot->poll = ED_operator_uvmap; + + /* props */ + RNA_def_boolean(ot->srna, "unselected", 0, "Hide Unselected", "Hide unselected rather than selected"); +} + +/****************** reveal operator ******************/ + +void ED_uvedit_reveal(BMEditMesh *em) +{ + BMFace *efa; + BMLoop *l; + BMIter iter, liter; + MLoopUV *luv; + + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + + BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) { + + if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN)) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { + luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); + luv->flag &= ~MLOOPUV_HIDDEN; + } + } + } +} + +static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +{ + Object *obedit = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + ToolSettings *ts = scene->toolsettings; + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + /* call the mesh function if we are in mesh sync sel */ + if (ts->uv_flag & UV_SYNC_SELECTION) { + EDBM_mesh_reveal(em); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + + return OPERATOR_FINISHED; + } + + ED_uvedit_reveal(em); + + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + + return OPERATOR_FINISHED; +} + +static void UV_OT_reveal(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Reveal Hidden"; + ot->description = "Reveal all hidden UV vertices"; + ot->idname = "UV_OT_reveal"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = uv_reveal_exec; + ot->poll = ED_operator_uvmap; +} + +/********************** Deselect Mesh operator *********************/ + /* check if we are selected or unselected based on 'bool_test' arg, - * needed for select swap support */ +* needed for select swap support */ #define UV_SEL_TEST(luv, bool_test) ((((luv)->flag & MLOOPUV_VERTSEL) == MLOOPUV_VERTSEL) == bool_test) /* is every UV vert selected or unselected depending on bool_test */ static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, - const int cd_loop_uv_offset) + const int cd_loop_uv_offset) { BMLoop *l_iter; BMLoop *l_first; @@ -3650,7 +4047,7 @@ static bool bm_face_is_all_uv_sel(BMFace *f, bool select_test, return true; } -static int uv_hide_exec(bContext *C, wmOperator *op) +static int uv_deselect_mesh_exec(bContext *C, wmOperator *op) { SpaceImage *sima = CTX_wm_space_image(C); Object *obedit = CTX_data_edit_object(C); @@ -3666,7 +4063,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) Image *ima = sima ? sima->image : NULL; const int use_face_center = (ts->uv_selectmode == UV_SELECT_FACE); - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); const int cd_poly_tex_offset = CustomData_get_offset(&em->bm->pdata, CD_MTEXPOLY); if (ts->uv_flag & UV_SYNC_SELECTION) { @@ -3676,7 +4073,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) return OPERATOR_FINISHED; } - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) { int hide = 0; tf = BM_ELEM_CD_GET_VOID_P(efa, cd_poly_tex_offset); @@ -3685,7 +4082,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) continue; } - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); if (UV_SEL_TEST(luv, !swap)) { @@ -3696,7 +4093,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) if (hide) { /* note, a special case for edges could be used, - * for now edges act like verts and get flushed */ + * for now edges act like verts and get flushed */ if (use_face_center) { if (em->selectmode == SCE_SELECT_FACE) { /* check that every UV is selected */ @@ -3707,7 +4104,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) } else { if (bm_face_is_all_uv_sel(efa, true, cd_loop_uv_offset) == !swap) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); if (UV_SEL_TEST(luv, !swap)) { BM_vert_select_set(em->bm, l->v, false); @@ -3725,7 +4122,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) } } else { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); if (UV_SEL_TEST(luv, !swap)) { BM_vert_select_set(em->bm, l->v, false); @@ -3739,7 +4136,7 @@ static int uv_hide_exec(bContext *C, wmOperator *op) /* flush vertex selection changes */ if (em->selectmode != SCE_SELECT_FACE) EDBM_selectmode_flush_ex(em, SCE_SELECT_VERTEX | SCE_SELECT_EDGE); - + BM_select_history_validate(em->bm); WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); @@ -3748,25 +4145,26 @@ static int uv_hide_exec(bContext *C, wmOperator *op) #undef UV_SEL_TEST -static void UV_OT_hide(wmOperatorType *ot) +static void UV_OT_deselect_mesh(wmOperatorType *ot) { /* identifiers */ - ot->name = "Hide Selected"; - ot->description = "Hide (un)selected UV vertices"; - ot->idname = "UV_OT_hide"; + ot->name = "Deselect 3D Mesh"; + ot->description = "Deselect 3D mesh vertices corresponding to (un)selected UV vertices"; + ot->idname = "UV_OT_deselect_mesh"; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + /* api callbacks */ - ot->exec = uv_hide_exec; + ot->exec = uv_deselect_mesh_exec; ot->poll = ED_operator_uvedit; /* props */ - RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Hide unselected rather than selected"); + /* ToDo (SaphireS): Wording ... */ + RNA_def_boolean(ot->srna, "unselected", 0, "Unselected", "Deselect unselected rather than selected"); } -/****************** reveal operator ******************/ +/********************** Select Mesh operator *********************/ -static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) +static int uv_select_mesh_exec(bContext *C, wmOperator *UNUSED(op)) { SpaceImage *sima = CTX_wm_space_image(C); Object *obedit = CTX_data_edit_object(C); @@ -3780,10 +4178,10 @@ static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) const int use_face_center = (ts->uv_selectmode == UV_SELECT_FACE); const int stickymode = sima ? (sima->sticky != SI_STICKY_DISABLE) : 1; - const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); + const int cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); /* note on tagging, selecting faces needs to be delayed so it doesn't select the verts and - * confuse our checks on selected verts. */ + * confuse our checks on selected verts. */ /* call the mesh function if we are in mesh sync sel */ if (ts->uv_flag & UV_SYNC_SELECTION) { @@ -3794,10 +4192,10 @@ static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) } if (use_face_center) { if (em->selectmode == SCE_SELECT_FACE) { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_elem_flag_disable(efa, BM_ELEM_TAG); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); luv->flag |= MLOOPUV_VERTSEL; } @@ -3809,16 +4207,16 @@ static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) else { /* enable adjacent faces to have disconnected UV selections if sticky is disabled */ if (!stickymode) { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_elem_flag_disable(efa, BM_ELEM_TAG); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { int totsel = 0; - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { totsel += BM_elem_flag_test(l->v, BM_ELEM_SELECT); } - + if (!totsel) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); luv->flag |= MLOOPUV_VERTSEL; } @@ -3829,10 +4227,10 @@ static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) } } else { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_elem_flag_disable(efa, BM_ELEM_TAG); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) == 0) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); luv->flag |= MLOOPUV_VERTSEL; @@ -3846,10 +4244,10 @@ static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) } } else if (em->selectmode == SCE_SELECT_FACE) { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_elem_flag_disable(efa, BM_ELEM_TAG); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); luv->flag |= MLOOPUV_VERTSEL; } @@ -3859,10 +4257,10 @@ static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) } } else { - BM_ITER_MESH (efa, &iter, em->bm, BM_FACES_OF_MESH) { + BM_ITER_MESH(efa, &iter, em->bm, BM_FACES_OF_MESH) { BM_elem_flag_disable(efa, BM_ELEM_TAG); if (!BM_elem_flag_test(efa, BM_ELEM_HIDDEN) && !BM_elem_flag_test(efa, BM_ELEM_SELECT)) { - BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { + BM_ITER_ELEM(l, &liter, efa, BM_LOOPS_OF_FACE) { if (BM_elem_flag_test(l->v, BM_ELEM_SELECT) == 0) { luv = BM_ELEM_CD_GET_VOID_P(l, cd_loop_uv_offset); luv->flag |= MLOOPUV_VERTSEL; @@ -3873,7 +4271,7 @@ static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) } } } - + /* re-select tagged faces */ BM_mesh_elem_hflag_enable_test(em->bm, BM_FACE, BM_ELEM_SELECT, true, false, BM_ELEM_TAG); @@ -3882,17 +4280,49 @@ static int uv_reveal_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_FINISHED; } -static void UV_OT_reveal(wmOperatorType *ot) +static void UV_OT_select_mesh(wmOperatorType *ot) { /* identifiers */ - ot->name = "Reveal Hidden"; - ot->description = "Reveal all hidden UV vertices"; - ot->idname = "UV_OT_reveal"; + ot->name = "Select 3D Mesh"; + ot->description = "Select all of the 3D mesh vertices"; + ot->idname = "UV_OT_select_mesh"; ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; - + /* api callbacks */ - ot->exec = uv_reveal_exec; + ot->exec = uv_select_mesh_exec; + ot->poll = ED_operator_uvedit; +} + +/******************** select overlapping operator ********************/ + +static int UV_OT_select_overlapping_exec(bContext *C, wmOperator *op) +{ + Object *obedit = CTX_data_edit_object(C); + Scene *scene = CTX_data_scene(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + BMesh *bm = em->bm; + + const bool extend = RNA_boolean_get(op->ptr, "extend"); + + ED_uvedit_overlapping_select(scene, obedit, bm, extend); + + WM_event_add_notifier(C, NC_GEOM | ND_SELECT, obedit->data); + + return OPERATOR_FINISHED; +} +static void UV_OT_select_overlapping(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Select Overlapping UVs"; + ot->description = "Select all overlapping UV islands"; + ot->idname = "UV_OT_select_overlapping"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = UV_OT_select_overlapping_exec; ot->poll = ED_operator_uvedit; + + RNA_def_boolean(ot->srna, "extend", 0, "Extend", "Extend current selection"); } /******************** set 3d cursor operator ********************/ @@ -4251,10 +4681,13 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_circle_select); WM_operatortype_append(UV_OT_select_more); WM_operatortype_append(UV_OT_select_less); + WM_operatortype_append(UV_OT_select_shortest_path); + WM_operatortype_append(UV_OT_select_overlapping); WM_operatortype_append(UV_OT_snap_cursor); WM_operatortype_append(UV_OT_snap_selected); + WM_operatortype_append(UV_OT_scale_to_bounds); WM_operatortype_append(UV_OT_align); WM_operatortype_append(UV_OT_stitch); @@ -4271,15 +4704,20 @@ void ED_operatortypes_uvedit(void) WM_operatortype_append(UV_OT_project_from_view); WM_operatortype_append(UV_OT_minimize_stretch); WM_operatortype_append(UV_OT_pack_islands); + WM_operatortype_append(UV_OT_irregular_pack_islands); WM_operatortype_append(UV_OT_reset); WM_operatortype_append(UV_OT_sphere_project); WM_operatortype_append(UV_OT_unwrap); WM_operatortype_append(UV_OT_reveal); WM_operatortype_append(UV_OT_hide); + WM_operatortype_append(UV_OT_deselect_mesh); + WM_operatortype_append(UV_OT_select_mesh); WM_operatortype_append(UV_OT_cursor_set); WM_operatortype_append(UV_OT_tile_set); + + WM_operatortype_append(UV_OT_test); } void ED_keymap_uvedit(wmKeyConfig *keyconf) @@ -4347,6 +4785,7 @@ void ED_keymap_uvedit(wmKeyConfig *keyconf) WM_keymap_add_item(keymap, "UV_OT_unwrap", EKEY, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "UV_OT_minimize_stretch", VKEY, KM_PRESS, KM_CTRL, 0); WM_keymap_add_item(keymap, "UV_OT_pack_islands", PKEY, KM_PRESS, KM_CTRL, 0); + WM_keymap_add_item(keymap, "UV_OT_irregular_pack_islands", PKEY, KM_PRESS, KM_CTRL | KM_SHIFT, 0); WM_keymap_add_item(keymap, "UV_OT_average_islands_scale", AKEY, KM_PRESS, KM_CTRL, 0); /* hide */ @@ -4355,8 +4794,18 @@ void ED_keymap_uvedit(wmKeyConfig *keyconf) kmi = WM_keymap_add_item(keymap, "UV_OT_hide", HKEY, KM_PRESS, KM_SHIFT, 0); RNA_boolean_set(kmi->ptr, "unselected", true); + /* reveal */ WM_keymap_add_item(keymap, "UV_OT_reveal", HKEY, KM_PRESS, KM_ALT, 0); + /* deselect 3d mesh */ + kmi = WM_keymap_add_item(keymap, "UV_OT_deselect_mesh", HKEY, KM_PRESS, KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "unselected", false); + kmi = WM_keymap_add_item(keymap, "UV_OT_deselect_mesh", HKEY, KM_PRESS, KM_SHIFT | KM_CTRL, 0); + RNA_boolean_set(kmi->ptr, "unselected", true); + + /* select 3d mesh */ + WM_keymap_add_item(keymap, "UV_OT_select_mesh", HKEY, KM_PRESS, KM_ALT | KM_CTRL, 0); + /* cursor */ WM_keymap_add_item(keymap, "UV_OT_cursor_set", ACTIONMOUSE, KM_PRESS, 0, 0); WM_keymap_add_item(keymap, "UV_OT_tile_set", ACTIONMOUSE, KM_PRESS, KM_SHIFT, 0); diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c index 8c76d03035a..212c4c5ab7a 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.c +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -34,6 +34,8 @@ #include "BLI_heap.h" #include "BLI_boxpack2d.h" #include "BLI_convexhull2d.h" +#include "BLI_linklist.h" +#include "BLI_polyfill2d.h" #include "uvedit_parametrizer.h" @@ -59,6 +61,8 @@ { /*printf("Assertion %s:%d\n", __FILE__, __LINE__); abort();*/ } (void)0 # define param_warning(message) \ { /*printf("Warning %s:%d: %s\n", __FILE__, __LINE__, message);*/ } (void)0 +# define RAND_NFP_POS + # if 0 # define param_test_equals_ptr(str, a, b) \ if (a != b) \ @@ -95,6 +99,8 @@ struct PEdge; struct PFace; struct PChart; struct PHandle; +struct PConvexHull; +struct PNoFitPolygon; /* Simplices */ @@ -103,9 +109,10 @@ typedef struct PVert { union PVertUnion { PHashKey key; /* construct */ - int id; /* abf/lscm matrix index */ + int id; /* abf/lscm matrix index, also used for shortest path computation */ float distortion; /* area smoothing */ HeapNode *heaplink; /* edge collapsing */ + float delta_edge[2]; /* no fit polygon - packing */ } u; struct PEdge *edge; @@ -123,6 +130,7 @@ typedef struct PEdge { int id; /* abf matrix index */ HeapNode *heaplink; /* fill holes */ struct PEdge *nextcollapse; /* simplification */ + float horizontal_angle; /* No Fit Polygon construction */ } u; struct PVert *vert; @@ -130,6 +138,7 @@ typedef struct PEdge { struct PEdge *next; struct PFace *face; float *orig_uv, old_uv[2]; + int *orig_flag; unsigned short flag; } PEdge; @@ -148,12 +157,36 @@ typedef struct PFace { unsigned char flag; } PFace; +typedef struct PPointUV{ + float x; + float y; + float angle; /* irregular packing */ + float delta_edge[2]; /* irregular packing */ +} PPointUV; + +typedef struct PConvexHull { + struct PVert **h_verts; /* ToDo SaphireS: Get rid of this since now verts is in place */ + struct PPointUV **verts; + unsigned int nverts; + int right; /* ToDo Saphires: Can we get rid of this? */ + int ref_vert_index; /* vert with the highest y value, lowest y if reversed */ + float min_v[2]; + float max_v[2]; + bool placed; +} PConvexHull; + +typedef struct PNoFitPolygon { + unsigned int nverts; + struct PPointUV **final_pos; +} PNoFitPolygon; + enum PVertFlag { PVERT_PIN = 1, PVERT_SELECT = 2, PVERT_INTERIOR = 4, PVERT_COLLAPSE = 8, - PVERT_SPLIT = 16 + PVERT_SPLIT = 16, + PVERT_MARKED = 32 }; enum PEdgeFlag { @@ -165,7 +198,8 @@ enum PEdgeFlag { PEDGE_FILLED = 32, PEDGE_COLLAPSE = 64, PEDGE_COLLAPSE_EDGE = 128, - PEDGE_COLLAPSE_PAIR = 256 + PEDGE_COLLAPSE_PAIR = 256, + PEDGE_DIAG = 512 }; /* for flipping faces */ @@ -199,6 +233,15 @@ typedef struct PChart { float rescale, area; float size[2] /* , trans[2] */; } pack; + struct PChartIrregularPack { + PConvexHull *convex_hull; /* ToDo (SaphireS): Only convex for now */ + PConvexHull **tris; + PPointUV *best_pos, *cur_pos; + float area, last_scale; + float sa_params[3]; /* 0 = Theta, 1 = r, 2 = f according to Rotational placement of irregular polygons over containers with fixed dimensions using simulated annealing and no-fit polygons*/ + int ntris; + bool decomposed; + } ipack; } u; unsigned char flag; @@ -213,7 +256,8 @@ enum PHandleState { PHANDLE_STATE_ALLOCATED, PHANDLE_STATE_CONSTRUCTED, PHANDLE_STATE_LSCM, - PHANDLE_STATE_STRETCH + PHANDLE_STATE_STRETCH, + PHANDLE_STATE_PACK }; typedef struct PHandle { @@ -421,6 +465,31 @@ static float p_face_uv_area_signed(PFace *f) ((v3->uv[0] - v1->uv[0]) * (v2->uv[1] - v1->uv[1]))); } +static float p_chart_uv_area_signed(PChart *chart) +{ + PFace *f; + float used_area = 0.0f; + for (f = chart->faces; f; f = f->nextlink) { + used_area += fabsf(p_face_uv_area_signed(f)); + } + + return used_area; +} + +/* returns the sum of the areas of all charts */ +static float p_face_uv_area_combined(ParamHandle *handle) +{ + PHandle *phandle = (PHandle *)handle; + float used_area = 0.0f; + int i; + + for (i = 0; i < phandle->ncharts; i++) { + used_area += p_chart_uv_area_signed(phandle->charts[i]); + } + + return used_area; +} + static float p_edge_length(PEdge *e) { PVert *v1 = e->vert, *v2 = e->next->vert; @@ -459,6 +528,8 @@ static void p_chart_uv_scale(PChart *chart, float scale) { PVert *v; + printf("--uv_scaling chart with factor %f\n", scale); + for (v = chart->verts; v; v = v->nextlink) { v->uv[0] *= scale; v->uv[1] *= scale; @@ -504,6 +575,69 @@ static void p_chart_uv_to_array(PChart *chart, float (*points)[2]) } } +static void p_chart_uv_scale_origin(PChart *chart, float scale) +{ + float minv[2], maxv[2], trans[2]; + + /* Get the island center */ + p_chart_uv_bbox(chart, minv, maxv); + trans[0] = (minv[0] + maxv[0]) / -2.0f; + trans[1] = (minv[1] + maxv[1]) / -2.0f; + + /* Move center to 0,0 */ + p_chart_uv_translate(chart, trans); + p_chart_uv_scale(chart, scale); + + /* Move to original center */ + trans[0] = -trans[0]; + trans[1] = -trans[1]; + p_chart_uv_translate(chart, trans); +} + +static void p_chart_uv_rotate(PChart *chart, float angle) +{ + float sine = sinf(angle); + float cosine = cosf(angle); + PVert *v; + + for (v = chart->verts; v; v = v->nextlink) { + float oldu = v->uv[0], oldv = v->uv[1]; + v->uv[0] = cosine * oldu - sine * oldv; + v->uv[1] = sine * oldu + cosine * oldv; + } +} + +static void p_chart_uv_rotate_origin(PChart *chart, float angle) +{ + float minv[2], maxv[2], trans[2]; + + /* Get the island center */ + p_chart_uv_bbox(chart, minv, maxv); + trans[0] = (minv[0] + maxv[0]) / -2.0f; + trans[1] = (minv[1] + maxv[1]) / -2.0f; + + /* Move center to 0,0 */ + p_chart_uv_translate(chart, trans); + p_chart_uv_rotate(chart, angle); + + /* Move to original center */ + trans[0] = -trans[0]; + trans[1] = -trans[1]; + p_chart_uv_translate(chart, trans); +} + +static void UNUSED_FUNCTION(p_scale_charts)(PHandle *handle, float scale) +{ + PChart *chart; + int i; + + for (i = 0; i < handle->ncharts; i++) { + chart = handle->charts[i]; + + p_chart_uv_scale_origin(chart, scale); + } +} + static void UNUSED_FUNCTION(p_chart_uv_from_array)(PChart *chart, float (*points)[2]) { PVert *v; @@ -531,8 +665,7 @@ static PBool p_intersect_line_2d_dir(float *v1, float *dir1, float *v2, float *d return P_TRUE; } -#if 0 -static PBool p_intersect_line_2d(float *v1, float *v2, float *v3, float *v4, float *isect) +static PBool UNUSED_FUNCTION(p_intersect_line_2d)(float *v1, float *v2, float *v3, float *v4, float *isect) { float dir1[2], dir2[2]; @@ -552,7 +685,56 @@ static PBool p_intersect_line_2d(float *v1, float *v2, float *v3, float *v4, flo return P_TRUE; } -#endif + +static PBool p_intersect_line_segments_2d(float *a, float *b, float *c, float *d) +{ + float D = (b[0] - a[0]) * (d[1] - c[1]) - (b[1] - a[1]) * (d[0] - c[0]); + + if (compare_ff(D, 0.0f, FLT_EPSILON)) { + /* Line segments are parallel */ + return P_FALSE; + } + + float r = ((a[1] - c[1]) * (d[0] - c[0]) - (a[0] - c[0]) * (d[1] - c[1])) / D; + float s = ((a[1] - c[1]) * (b[0] - a[0]) - (a[0] - c[0]) * (b[1] - a[1])) / D; + + if (((-FLT_EPSILON <= r) && (r <= (1.0f + FLT_EPSILON))) && + ((-FLT_EPSILON <= s) && (s <= (1.0f + FLT_EPSILON)))) { + /* ToDo (SaphireS): return actual intersection data ?*/ + + return P_TRUE; + } + + return P_FALSE; +} + +static bool p_rect_intersect(float min1[2], float max1[2], float min2[2], float max2[2]) +{ + if (min1[0] > max2[0] || + max1[0] < min2[0] || + min1[1] > max2[1] || + max1[1] < min2[1]) { + return false; + } + + return true; +} + +/* Returns the interval of range in which f falls in */ +static int p_float_to_int_range(float f, int range) +{ + return (int)(f * (float)(range)); +} + +/* Returns the interval of range in which f falls in */ +/* re contains the remainder of f, linearized to 0-1 range */ +static int p_float_to_int_range_remainder(float f, int range, float *re) +{ + int val = p_float_to_int_range(f, range); + *re = f * range - val; + + return val; +} /* Topological Utilities */ @@ -685,6 +867,36 @@ static void p_flush_uvs(PHandle *handle, PChart *chart) } } +static void p_flush_uvs_selection(PHandle *handle, PChart *chart) +{ + PEdge *e; + int sel_flag = 0; + /* ToDo (SaphireS): Find sensible variable names*/ + int MLOOPEDGE_SELECTED = (1 << 0); /* MLOOPUV_EDGESEL*/ + int MLOOPVERT_SELECTED = (1 << 1); /* MLOOPUV_VERTSEL*/ + + for (e = chart->edges; e; e = e->nextlink) { + if (e->orig_uv) { + e->orig_uv[0] = e->vert->uv[0] / handle->aspx; + e->orig_uv[1] = e->vert->uv[1] / handle->aspy; + } + + if (e->flag & PEDGE_SELECT) { + + sel_flag = *e->orig_flag; + sel_flag |= MLOOPEDGE_SELECTED;/* MLOOPUV_EDGESEL*/ + sel_flag |= MLOOPVERT_SELECTED; /* MLOOPUV_VERTSEL*/ + *(e->orig_flag) = sel_flag; + } + else { + sel_flag = *e->orig_flag; + sel_flag &= ~MLOOPEDGE_SELECTED;/* MLOOPUV_EDGESEL*/ + sel_flag &= ~MLOOPVERT_SELECTED; /* MLOOPUV_VERTSEL*/ + *(e->orig_flag) = sel_flag; + } + } +} + static void p_flush_uvs_blend(PHandle *handle, PChart *chart, float blend) { PEdge *e; @@ -1108,7 +1320,7 @@ static PFace *p_face_add(PHandle *handle) static PFace *p_face_add_construct(PHandle *handle, ParamKey key, ParamKey *vkeys, float *co[4], float *uv[4], int i1, int i2, int i3, - ParamBool *pin, ParamBool *select) + ParamBool *pin, ParamBool *select, int **flag, int diag_edge) { PFace *f = p_face_add(handle); PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; @@ -1121,6 +1333,10 @@ static PFace *p_face_add_construct(PHandle *handle, ParamKey key, ParamKey *vkey e2->orig_uv = uv[i2]; e3->orig_uv = uv[i3]; + e1->orig_flag = flag[i1]; + e2->orig_flag = flag[i2]; + e3->orig_flag = flag[i3]; + if (pin) { if (pin[i1]) e1->flag |= PEDGE_PIN; if (pin[i2]) e2->flag |= PEDGE_PIN; @@ -1145,6 +1361,28 @@ static PFace *p_face_add_construct(PHandle *handle, ParamKey key, ParamKey *vkey phash_insert(handle->hash_edges, (PHashLink *)e2); phash_insert(handle->hash_edges, (PHashLink *)e3); + if (diag_edge != -1) { + switch (diag_edge) { + case 0: + { + e1->flag |= PEDGE_DIAG; + break; + } + case 1: + { + e2->flag |= PEDGE_DIAG; + break; + } + case 2: /* fall through */ + case 3: + { + e3->flag |= PEDGE_DIAG; + break; + } + default: break; + } + } + return f; } @@ -1363,6 +1601,48 @@ static void p_chart_fill_boundaries(PChart *chart, PEdge *outer) } } +static bool p_charts_intersect(PChart* a, PChart *b) +{ + PEdge *e1, *e2; + float min1[2], min2[2], max1[2], max2[2]; + + /* Check for overlapping bounding rectangles */ + p_chart_uv_bbox(a, min1, max1); + p_chart_uv_bbox(b, min2, max2); + + if (!p_rect_intersect(min1, max1, min2, max2)) { + return false; + } + + /* Check edges for intersections */ + for (e1 = a->edges; e1; e1 = e1->nextlink) { + for (e2 = b->edges; e2; e2 = e2->nextlink) { + if (e1->nextlink && e2->nextlink) { + if (p_intersect_line_segments_2d(e1->vert->uv, + e1->nextlink->vert->uv, + e2->vert->uv, + e2->nextlink->vert->uv)) { + return true; + } + } + } + } + + return false; +} + +static void p_chart_select(PChart *chart, bool select) +{ + PEdge *e; + + for (e = chart->edges; e; e = e->nextlink) { + if (select) + e->flag |= PEDGE_SELECT; + else + e->flag &= ~PEDGE_SELECT; + } +} + #if 0 /* Polygon kernel for inserting uv's non overlapping */ @@ -3600,15 +3880,7 @@ static float p_chart_minimum_area_angle(PChart *chart) static void p_chart_rotate_minimum_area(PChart *chart) { float angle = p_chart_minimum_area_angle(chart); - float sine = sinf(angle); - float cosine = cosf(angle); - PVert *v; - - for (v = chart->verts; v; v = v->nextlink) { - float oldu = v->uv[0], oldv = v->uv[1]; - v->uv[0] = cosine * oldu - sine * oldv; - v->uv[1] = sine * oldu + cosine * oldv; - } + p_chart_uv_rotate(chart, angle); } /* Area Smoothing */ @@ -3670,6 +3942,19 @@ static PBool p_triangle_inside(SmoothTriangle *t, float co[2]) return P_FALSE; } +static PBool p_triangle_inside_v3_v2(float tri_v1[2], float tri_v2[2], float tri_v3[2], float co[2]) +{ + float b[3]; + + p_barycentric_2d(tri_v1, tri_v2, tri_v3, co, b); + + if ((b[0] >= 0.0f) && (b[1] >= 0.0f) && (b[2] >= 0.0f)) { + return P_TRUE; + } + + return P_FALSE; +} + static SmoothNode *p_node_new(MemArena *arena, SmoothTriangle **tri, int ntri, float *bmin, float *bmax, int depth) { SmoothNode *node = BLI_memarena_alloc(arena, sizeof(*node)); @@ -4167,7 +4452,7 @@ void param_delete(ParamHandle *handle) static void p_add_ngon(ParamHandle *handle, ParamKey key, int nverts, ParamKey *vkeys, float **co, float **uv, - ParamBool *pin, ParamBool *select, const float normal[3]) + ParamBool *pin, ParamBool *select, const float normal[3], int **flag) { int *boundary = BLI_array_alloca(boundary, nverts); @@ -4221,10 +4506,11 @@ static void p_add_ngon(ParamHandle *handle, ParamKey key, int nverts, ParamKey tri_vkeys[3] = {vkeys[v0], vkeys[v1], vkeys[v2]}; float *tri_co[3] = {co[v0], co[v1], co[v2]}; float *tri_uv[3] = {uv[v0], uv[v1], uv[v2]}; + int *tri_flag[3] = {flag[v0], flag[v1], flag[v2]}; ParamBool tri_pin[3] = {pin[v0], pin[v1], pin[v2]}; ParamBool tri_select[3] = {select[v0], select[v1], select[v2]}; - param_face_add(handle, key, 3, tri_vkeys, tri_co, tri_uv, tri_pin, tri_select, NULL); + param_face_add(handle, key, 3, tri_vkeys, tri_co, tri_uv, tri_pin, tri_select, NULL, tri_flag); } /* remove corner */ @@ -4237,7 +4523,7 @@ static void p_add_ngon(ParamHandle *handle, ParamKey key, int nverts, void param_face_add(ParamHandle *handle, ParamKey key, int nverts, ParamKey *vkeys, float *co[4], float *uv[4], - ParamBool *pin, ParamBool *select, float normal[3]) + ParamBool *pin, ParamBool *select, float normal[3], int **flag) { PHandle *phandle = (PHandle *)handle; @@ -4247,22 +4533,22 @@ void param_face_add(ParamHandle *handle, ParamKey key, int nverts, if (nverts > 4) { /* ngon */ - p_add_ngon(handle, key, nverts, vkeys, co, uv, pin, select, normal); + p_add_ngon(handle, key, nverts, vkeys, co, uv, pin, select, normal, flag); } else if (nverts == 4) { /* quad */ if (p_quad_split_direction(phandle, co, vkeys)) { - p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 2, pin, select); - p_face_add_construct(phandle, key, vkeys, co, uv, 0, 2, 3, pin, select); + p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 2, pin, select, flag, 2); + p_face_add_construct(phandle, key, vkeys, co, uv, 0, 2, 3, pin, select, flag, 0); } else { - p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 3, pin, select); - p_face_add_construct(phandle, key, vkeys, co, uv, 1, 2, 3, pin, select); + p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 3, pin, select, flag, 1); + p_face_add_construct(phandle, key, vkeys, co, uv, 1, 2, 3, pin, select, flag, 3); } } else if (!p_face_exists(phandle, vkeys, 0, 1, 2)) { /* triangle */ - p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 2, pin, select); + p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 2, pin, select, flag, -1); } } @@ -4593,6 +4879,1346 @@ void param_pack(ParamHandle *handle, float margin, bool do_rotate) param_scale(handle, phandle->aspx, phandle->aspy); } +/* qsort function - sort largest to smallest */ +static int UNUSED_FUNCTION(vert_anglesort)(const void *p1, const void *p2) +{ + PVert *const* v1 = p1, *const* v2 = p2; + const float a1 = (*v1)->edge->u.horizontal_angle; + const float a2 = (*v2)->edge->u.horizontal_angle; + + if (a1 > a2) return -1; + else if (a1 < a2) return 1; + return 0; +} + +/* qsort function - sort largest to smallest */ +static int point_anglesort(const void *p1, const void *p2) +{ + PPointUV *const* v1 = p1, *const* v2 = p2; + const float a1 = (*v1)->angle; + const float a2 = (*v2)->angle; + + if (a1 > a2) return -1; + else if (a1 < a2) return 1; + return 0; +} + +/* qsort function - sort largest to smallest */ +static int chart_areasort(const void *p1, const void *p2) +{ + PChart *const* c1 = p1, *const* c2 = p2; + const float a1 = (*c1)->u.ipack.area; + const float a2 = (*c2)->u.ipack.area; + + if (a1 > a2) return -1; + else if (a1 < a2) return 1; + return 0; +} + +/* ToDo SaphireS: Make this function part of math_vector.c */ +static float UNUSED_FUNCTION(p_edge_horizontal_angle)(PVert *a, PVert *b) +{ + float angle = 0.0f; + float hori[2]; + hori[1] = a->uv[1]; + + if (a->uv[1] > b->uv[1] && !(compare_ff(a->uv[1], b->uv[1], FLT_EPSILON))) { + hori[0] = a->uv[0] - 1.0f; + angle = angle_v2v2v2(b->uv, a->uv, hori); + angle += M_PI; + } + else { + hori[0] = a->uv[0] + 1.0f; + angle = angle_v2v2v2(b->uv, a->uv, hori); + } + + return angle; +} + +static float p_edge_horizontal_angle_ppointuv(PPointUV *a, PPointUV *b) +{ + float angle = 0.0f; + float hori[2], pa[2], pb[2]; + hori[1] = a->y; + pa[0] = a->x; + pa[1] = a->y; + pb[0] = b->x; + pb[1] = b->y; + if (a->y > b->y && !(compare_ff(a->y, b->y, FLT_EPSILON))) { + hori[0] = a->x - 1.0f; + angle = angle_v2v2v2(pb, pa, hori); + angle += M_PI; + } + else { + hori[0] = a->x + 1.0f; + angle = angle_v2v2v2(pb, pa, hori); + } + + return angle; +} + +/* ToDo SaphireS: Put ConvexHull/NFP stuff in own file*/ +static PConvexHull *p_convex_hull_new(PChart *chart) +{ + PConvexHull *conv_hull = (PConvexHull *)MEM_callocN(sizeof(*conv_hull), "PConvexHull"); + PVert **points; + float maxy = -1.0e30f; + int npoint, right, i; + + if (!p_chart_convex_hull(chart, &points, &npoint, &right)) + printf("convex hull for chart failed!\n"); + + conv_hull->h_verts = points; + conv_hull->verts = (PPointUV **)MEM_callocN(sizeof(*conv_hull->verts) * npoint, "PConvexHullVerts"); + conv_hull->nverts = npoint; + conv_hull->right = right; + conv_hull->placed = false; + conv_hull->ref_vert_index = 0; + + /* Fill max/min for initial positioning, with this we also know the bounding rectangle */ + p_chart_uv_bbox(chart, conv_hull->min_v, conv_hull->max_v); + + /* get reference vertex */ + for (i = 0; i < conv_hull->nverts; i++) { + PPointUV *p = (PPointUV *)MEM_callocN(sizeof(*p), "PPointUV"); + p->x = conv_hull->h_verts[i]->uv[0]; + p->y = conv_hull->h_verts[i]->uv[1]; + conv_hull->verts[i] = p; + + /* Note: FLT_EPSILON is to exact and produces wrong results in this context, use custom max_diff instead */ + if (compare_ff(maxy, conv_hull->verts[i]->y, 0.00001f)) { + /* same y value, only take if x value is lower than for current ref vert */ + if (conv_hull->verts[i]->x < conv_hull->verts[conv_hull->ref_vert_index]->x) { + maxy = conv_hull->verts[i]->y; + conv_hull->ref_vert_index = i; + } + } + else if (conv_hull->verts[i]->y > maxy) { + /* higher y value */ + maxy = conv_hull->verts[i]->y; + conv_hull->ref_vert_index = i; + } + } + + return conv_hull; +} + +static PConvexHull *p_convex_hull_new_tri(PChart *chart, const float(*coords)[2]) +{ + PConvexHull *conv_hull = (PConvexHull *)MEM_callocN(sizeof(*conv_hull), "PConvexHull"); + PVert *v; + float pos[2], maxy = -1.0e30f; + int npoint = 3, right = 0, i; + + //printf("p_convex_hull_new_tri!\n"); + + conv_hull->h_verts = (PVert **)MEM_callocN(sizeof(conv_hull->h_verts) * npoint, "PConvHullPVerts"); + conv_hull->verts = (PPointUV **)MEM_callocN(sizeof(*conv_hull->verts) * npoint, "PConvexHullVerts"); + conv_hull->nverts = npoint; + conv_hull->right = right; + conv_hull->placed = false; + conv_hull->ref_vert_index = 0; + + /* Fill max/min for initial positioning, with this we also know the bounding rectangle */ + /* p_chart_uv_bbox(chart, conv_hull->min_v, conv_hull->max_v); */ + INIT_MINMAX2(conv_hull->min_v, conv_hull->max_v); + + //printf("-p_convex_hull_new_tri: inits done!\n"); + + /* get reference vertex */ + for (i = 0; i < conv_hull->nverts; i++) { + + /* ToDo Saphires: Also fill verts_h, needed for margin support */ + PPointUV *p = (PPointUV *)MEM_callocN(sizeof(*p), "PPointUV"); + p->x = coords[i][0]; + p->y = coords[i][1]; + //printf("-p_convex_hull_new_tri: p coords updated!\n"); + conv_hull->verts[i] = p; + //printf("-p_convex_hull_new_tri: p assigned to verts!\n"); + + /*PVert *v = (PVert *)MEM_callocN(sizeof(*v), "PVert"); + v->uv[0] = coords[i][0]; + v->uv[1] = coords[i][1]; + conv_hull->h_verts[i] = v; + printf("-p_convex_hull_new_tri: v assigned to verts_h!\n");*/ + + for (v = chart->verts; v; v = v->nextlink) { + if (compare_ff(v->uv[0],coords[i][0], 0.0001f)) { + if (compare_ff(v->uv[1], coords[i][1], 0.0001f)) { + conv_hull->h_verts[i] = v; + //printf("-p_convex_hull_new_tri: v assigned to verts_h!\n"); + } + } + } + + /* Update bounds */ + pos[0] = conv_hull->verts[i]->x; + pos[1] = conv_hull->verts[i]->y; + minmax_v2v2_v2(conv_hull->min_v, conv_hull->max_v, pos); + //printf("-p_convex_hull_new_tri: bounds updated!\n"); + + /* Note: FLT_EPSILON is to exact and produces wrong results in this context, use custom max_diff instead */ + if (compare_ff(maxy, conv_hull->verts[i]->y, 0.00001f)) { + /* same y value, only take if x value is lower than for current ref vert */ + if (conv_hull->verts[i]->x < conv_hull->verts[conv_hull->ref_vert_index]->x) { + maxy = conv_hull->verts[i]->y; + conv_hull->ref_vert_index = i; + } + } + else if (conv_hull->verts[i]->y > maxy) { + /* higher y value */ + maxy = conv_hull->verts[i]->y; + conv_hull->ref_vert_index = i; + } + //printf("-p_convex_hull_new_tri: ref vert updated!\n"); + } + + return conv_hull; +} + +/* Update bounds and recalculate ref vertex (highest y value) */ +static void p_convex_hull_update(PConvexHull *conv_hull, bool update_points) +{ + int i; + float maxy = -1.0e30f, p[2]; + conv_hull->ref_vert_index = 0; + + + for (i = 0; i < conv_hull->nverts; i++) { + /* update points */ + if (update_points) { + conv_hull->verts[i]->x = conv_hull->h_verts[i]->uv[0]; + conv_hull->verts[i]->y = conv_hull->h_verts[i]->uv[1]; + } + + /* Update bounds */ + p[0] = conv_hull->verts[i]->x; + p[1] = conv_hull->verts[i]->y; + minmax_v2v2_v2(conv_hull->min_v, conv_hull->max_v, p); + + /* get reference vertex */ + /* Note: FLT_EPSILON is to exact and produces wrong results in this context, use custom max_diff instead */ + if (compare_ff(maxy, conv_hull->verts[i]->y, 0.00001f)) { + /* same y value, only take if x value is lower than for current ref vert */ + if (conv_hull->verts[i]->x < conv_hull->verts[conv_hull->ref_vert_index]->x) { + maxy = conv_hull->verts[i]->y; + conv_hull->ref_vert_index = i; + } + } + else if (conv_hull->verts[i]->y > maxy) { + /* higher y value */ + maxy = conv_hull->verts[i]->y; + conv_hull->ref_vert_index = i; + } + } +} + +static void p_convex_hull_delete(PConvexHull *c_hull) +{ + int i; + for (i = 0; i < c_hull->nverts; i++) { + if (c_hull->verts[i]) { + MEM_freeN(c_hull->verts[i]); + } + } + + MEM_freeN(c_hull->verts); + MEM_freeN(c_hull->h_verts); + MEM_freeN(c_hull); + c_hull = NULL; +} + +static bool p_convex_hull_intersect(PConvexHull *chull_a, PConvexHull *chull_b) +{ + /* Preliminary bounds check */ + if (!p_rect_intersect(chull_a->min_v, chull_a->max_v, chull_b->min_v, chull_b->max_v)) { + return false; + } + + int i, j; + + /* Check edges for intersctions */ + for (i = 0; i < chull_a->nverts; i++) { + for (j = 0; j < chull_b->nverts; j++) { + /* ToDo SaphireS: Use chull_a->verts instead */ + if (p_intersect_line_segments_2d(chull_a->h_verts[i]->uv, + chull_a->h_verts[i+1]->uv, + chull_b->h_verts[j]->uv, + chull_b->h_verts[j+1]->uv)) { + return true; + } + } + } + + return false; +} + +static void p_convex_hull_compute_horizontal_angles(PConvexHull *hull) +{ + int j; + //printf("*p_convex_hull_compute_horizontal_angles\n"); + for (j = 0; j < hull->nverts; j++) { + + /* Compute horizontal angle for each edge of hull (Needed for NFP) */ + /* ToDo SaphireS: Get rid of h_verts */ + if (j == (hull->nverts - 1)) { + //hull->h_verts[j]->edge->u.horizontal_angle = p_edge_horizontal_angle(hull->h_verts[j], hull->h_verts[0]); + hull->verts[j]->angle = p_edge_horizontal_angle_ppointuv(hull->verts[j], hull->verts[0]); + } + else { + //hull->h_verts[j]->edge->u.horizontal_angle = p_edge_horizontal_angle(hull->h_verts[j], hull->h_verts[j + 1]); + hull->verts[j]->angle = p_edge_horizontal_angle_ppointuv(hull->verts[j], hull->verts[j + 1]); + } + + /* Note: FLT_EPSILON is to exact and produces wrong results in this context, use custom max_diff instead */ + /* Since we're counting edges in CW winding we have to set 0.0 to 2*pi */ + /*if (compare_ff(hull->h_verts[j]->edge->u.horizontal_angle, 0.0f, 0.00001f)) { + hull->h_verts[j]->edge->u.horizontal_angle += 2 * M_PI; + }*/ + if (compare_ff(hull->verts[j]->angle, 0.0f, 0.00001f)) { + hull->verts[j]->angle += 2 * M_PI; + } + + /*printf("---horizontal angle of edge [%i]: %f\n", j, hull->h_verts[j]->edge->u.horizontal_angle);*/ + } +} + +static void p_convex_hull_compute_edge_components(PConvexHull *hull) +{ + int j; + //printf("*p_convex_hull_compute_edge_lengths\n"); + for (j = 0; j < hull->nverts; j++) { + + /* Compute edge components for each edge of hull (Needed for NFP) */ + /* ToDo SaphireS: Get rid of h_verts */ + if (j == (hull->nverts - 1)) { + hull->h_verts[j]->u.delta_edge[0] = hull->h_verts[0]->uv[0] - hull->h_verts[j]->uv[0]; + hull->h_verts[j]->u.delta_edge[1] = hull->h_verts[0]->uv[1] - hull->h_verts[j]->uv[1]; + } + else { + hull->h_verts[j]->u.delta_edge[0] = hull->h_verts[j + 1]->uv[0] - hull->h_verts[j]->uv[0]; + hull->h_verts[j]->u.delta_edge[1] = hull->h_verts[j + 1]->uv[1] - hull->h_verts[j]->uv[1]; + } + /* verts */ + if (j == (hull->nverts - 1)) { + hull->verts[j]->delta_edge[0] = hull->verts[0]->x - hull->verts[j]->x; + hull->verts[j]->delta_edge[1] = hull->verts[0]->y - hull->verts[j]->y; + } + else { + hull->verts[j]->delta_edge[0] = hull->verts[j + 1]->x - hull->verts[j]->x; + hull->verts[j]->delta_edge[1] = hull->verts[j + 1]->y - hull->verts[j]->y; + } + + //printf("--- edge [%i]: x: %f, y: %f\n", j, hull->h_verts[j]->u.delta_edge[0], hull->h_verts[j]->u.delta_edge[1]); + } +} + +static PConvexHull *p_convex_hull_reverse_vert_order(PConvexHull *hull) +{ + PConvexHull *conv_hull_inv = (PConvexHull *)MEM_callocN(sizeof(*conv_hull_inv), "PConvexHullInverse"); + conv_hull_inv->nverts = hull->nverts; + conv_hull_inv->h_verts = (PVert **)MEM_mallocN(sizeof(PVert *) * conv_hull_inv->nverts, "PConvexHullInversePoints"); + conv_hull_inv->verts = (PPointUV **)MEM_callocN(sizeof(*conv_hull_inv->verts) * conv_hull_inv->nverts, "PConvexHullVerts"); + conv_hull_inv->right = hull->right; + conv_hull_inv->placed = false; + int i, j; + float miny = 1.0e30f, b[2]; + + /* reverse vert order */ + for (j = 0; j < hull->nverts; j++) { + conv_hull_inv->h_verts[j] = hull->h_verts[hull->nverts - (j + 1)]; + PPointUV *p = (PPointUV *)MEM_callocN(sizeof(*p), "PPointUV"); + p->x = hull->verts[hull->nverts - (j + 1)]->x; + p->y = hull->verts[hull->nverts - (j + 1)]->y; + conv_hull_inv->verts[j] = p; + } + + INIT_MINMAX2(conv_hull_inv->min_v, conv_hull_inv->max_v); + + /* reference vertex, for inverse winding direction that's the one with lowest y value */ + for (i = 0; i < conv_hull_inv->nverts; i++) { + + /* Note: FLT_EPSILON is to exact and produces wrong results in this context, use custom max_diff instead */ + if (compare_ff(miny, conv_hull_inv->verts[i]->y, 0.00001f)) { + /* same y value, only take if x value is higher than for current ref vert */ + if (conv_hull_inv->verts[i]->x > conv_hull_inv->verts[conv_hull_inv->ref_vert_index]->x) { + miny = conv_hull_inv->verts[i]->y; + conv_hull_inv->ref_vert_index = i; + //printf("--p_convex_hull_reverse_vert_order: EQUAL min_y : x = %f, y = %f\n", conv_hull_inv->verts[i]->x, conv_hull_inv->verts[i]->y); + } + } + else if (conv_hull_inv->verts[i]->y < miny) { + /* lower y value */ + miny = conv_hull_inv->verts[i]->y; + conv_hull_inv->ref_vert_index = i; + //printf("--p_convex_hull_reverse_vert_order: SMALLER min_y : x = %f, y = %f\n", conv_hull_inv->verts[i]->x, conv_hull_inv->verts[i]->y); + } + + /* compute bounds */ + b[0] = conv_hull_inv->verts[i]->x; + b[1] = conv_hull_inv->verts[i]->y; + minmax_v2v2_v2(conv_hull_inv->min_v, conv_hull_inv->max_v, b); + } + + //printf("--p_convex_hull_reverse_vert_order: FINAL min_y: x: %f, y: %f\n", conv_hull_inv->verts[conv_hull_inv->ref_vert_index]->x, conv_hull_inv->verts[conv_hull_inv->ref_vert_index]->y); + + return conv_hull_inv; +} + +/* Grow hull by margin amount */ +static void p_convex_hull_grow(PConvexHull *chull, float margin) +{ + /* ToDo SaphireS */ + PVert *v1, *v2, *v3; + //PPointUV *p1, *p2, *p3; + float vec1[2], vec2[2], vec3[2]; + float dist_fac; + float a[2], b[2], dir[2], end_pos[2], a_n[2], b_n[2]; + int i; + + for (i = 0; i < chull->nverts; i++) { + v1 = chull->h_verts[(i ? i : chull->nverts) - 1]; + v2 = chull->h_verts[i]; + v3 = chull->h_verts[(i + 1) < chull->nverts ? (i + 1) : 0]; + + //p1 = chull->verts[(i ? i : chull->nverts) - 1]; + //p2 = chull->verts[i]; + //p3 = chull->verts[(i + 1) < chull->nverts ? (i + 1) : 0]; + + vec1[0] = v1->uv[0]; + vec1[1] = v1->uv[1]; + + vec2[0] = v2->uv[0]; + vec2[1] = v2->uv[1]; + + vec3[0] = v3->uv[0]; + vec3[1] = v3->uv[1]; + + sub_v2_v2v2(a, vec1, vec2); + sub_v2_v2v2(b, vec2, vec3); + + //printf("--a x: %f, y: %f\n", a[0], a[1]); + //printf("--b x: %f, y: %f\n", b[0], b[1]); + + normalize_v2(a); + normalize_v2(b); + + /* distance to offset */ + dist_fac = shell_v2v2_mid_normalized_to_dist(a, b); + + /* direction to offset */ + edge_normal_v2_v2v2(a_n, vec1, vec2, true); + edge_normal_v2_v2v2(b_n, vec2, vec3, true); + + normalize_v2(a_n); + normalize_v2(b_n); + + //printf("--a_n x: %f, y: %f\n", a_n[0], a_n[1]); + //printf("--b_n x: %f, y: %f\n", b_n[0], b_n[1]); + + add_v2_v2v2(dir, a_n, b_n); + + normalize_v2(dir); + + //printf("--dirx: %f, diry: %f, dist_fac: %f, margin: %f\n", dir[0], dir[1], dist_fac, margin); + + /* offset point */ + madd_v2_v2v2fl(end_pos, v2->uv, dir, dist_fac * margin); + + /*ToDo: apply end_pos */ + chull->verts[i]->x = end_pos[0]; + chull->verts[i]->y = end_pos[1]; + + //printf("-growing end_pos[%i]: x: %f, y: %f\n", i, end_pos[0], end_pos[1]); + } +} + +static PConvexHull *p_convex_hull_reverse_direction(PConvexHull *item) +{ + /* Invert direction of one convex hull -> CCW */ + //printf("inversing direction start\n"); + /* ref_vert_index now contains the vert with the lowest y value */ + PConvexHull *item_inv = p_convex_hull_reverse_vert_order(item); + //printf("angles start\n"); + p_convex_hull_compute_horizontal_angles(item_inv); + //printf("components start\n"); + p_convex_hull_compute_edge_components(item_inv); + //printf("inversing direction done!\n"); + + return item_inv; +} + +/* ToDo SaphireS: store edge angle/edge components in different way so this isn't necessary */ +static void p_convex_hull_restore_direction(PConvexHull *item, float margin) +{ + p_convex_hull_update(item, true); + p_convex_hull_grow(item, margin); + p_convex_hull_update(item, false); + p_convex_hull_compute_horizontal_angles(item); + p_convex_hull_compute_edge_components(item); +} + +static void p_convex_hull_update_all(PConvexHull *hull, float margin) +{ + p_convex_hull_update(hull, true); + p_convex_hull_grow(hull, margin); + p_convex_hull_update(hull, false); + p_convex_hull_compute_horizontal_angles(hull); /* ToDo: Shouldn't be necessary! */ + p_convex_hull_compute_edge_components(hull); +} + +static PNoFitPolygon *p_inner_fit_polygon_create(PHandle *phandle, PConvexHull *item) +{ + PNoFitPolygon *nfp = (PNoFitPolygon *)MEM_callocN(sizeof(*nfp), "PNoFitPolygon"); + /* Simplification, since we're not dealing with arbitrary shaped outer bounds */ + /* we can assume the inner fit polygon to always be a rectangle */ + nfp->nverts = 4; + PVert **points = (PVert **)MEM_mallocN(sizeof(PVert *) * nfp->nverts, "PNFPPoints"); + nfp->final_pos = (PPointUV **)MEM_callocN(sizeof(*nfp->final_pos) * nfp->nverts, "PNFPFinalPos"); + + PPointUV *p1 = (PPointUV *)MEM_callocN(sizeof(*p1), "PPointUV"); + PPointUV *p2 = (PPointUV *)MEM_callocN(sizeof(*p2), "PPointUV"); + PPointUV *p3 = (PPointUV *)MEM_callocN(sizeof(*p3), "PPointUV"); + PPointUV *p4 = (PPointUV *)MEM_callocN(sizeof(*p4), "PPointUV"); + + /* reference point for item hull is the one with the lowest y value*/ + + /* Aspect Ratio compensation */ + float bounds_height = phandle->aspy; + float bounds_width = phandle->aspx; + + p1->x = item->verts[item->ref_vert_index]->x - item->min_v[0]; + //printf("item_ref_x: %f, %f, min_v[0]: %f\n", item->verts[item->ref_vert_index]->x, item->verts[item->ref_vert_index]->y, item->min_v[0]); + p1->y = 0.0f; + nfp->final_pos[0] = p1; + + p2->x = p1->x; + p2->y = bounds_height - (item->max_v[1] - item->min_v[1]); /* ToDo Saphires: ConvexHull should store relative bounds (width/height) ?*/ + nfp->final_pos[1] = p2; + + p3->x = bounds_width - (item->max_v[0] - item->min_v[0] - (item->verts[item->ref_vert_index]->x - item->min_v[0])); + p3->y = p2->y; + nfp->final_pos[2] = p3; + + p4->x = p3->x; + p4->y = 0.0f; + nfp->final_pos[3] = p4; + + /* CleanUp */ + MEM_freeN(points); + + return nfp; +} + +static PConvexHull** p_decompose_triangulate_chart(PChart *chart, const float(*hull_points)[2], const int nbounds) +{ + int i, ntris = nbounds - 2; + unsigned int(*r_tris)[3] = BLI_array_alloca(r_tris, ntris); + float cur_tri[3][2]; + /* ToDo Saphires: Could make this function void if we have the chart as parameter anyway */ + PConvexHull **chull_tris = (PConvexHull **)MEM_mallocN(sizeof(PConvexHull *) * ntris, "PNFPs"); + chart->u.ipack.ntris = ntris; + + //printf("p_decompose_triangulate_chart!\n"); + + /* triangulate */ + BLI_polyfill_calc((const float(*)[2])hull_points, nbounds, -1, r_tris); + + /* write r_tris to convex hulls */ + for (i = 0; i < ntris; i++){ + cur_tri[0][0] = hull_points[r_tris[i][0]][0]; + cur_tri[0][1] = hull_points[r_tris[i][0]][1]; + //printf("p_decompose_triangulate_chart: tri[0]x: %f, y: %f\n", hull_points[r_tris[i][0]][0], hull_points[r_tris[i][0]][1]); + + cur_tri[1][0] = hull_points[r_tris[i][1]][0]; + cur_tri[1][1] = hull_points[r_tris[i][1]][1]; + //printf("p_decompose_triangulate_chart: tri[0]x: %f, y: %f\n", hull_points[r_tris[i][1]][0], hull_points[r_tris[i][1]][1]); + + cur_tri[2][0] = hull_points[r_tris[i][2]][0]; + cur_tri[2][1] = hull_points[r_tris[i][2]][1]; + //printf("p_decompose_triangulate_chart: tri[0]x: %f, y: %f\n", hull_points[r_tris[i][2]][0], hull_points[r_tris[i][2]][1]); + + chull_tris[i] = p_convex_hull_new_tri(chart, cur_tri); + } + + return chull_tris; +} + +static bool p_point_inside_nfp(PNoFitPolygon *nfp, float p[2]) +{ + /* raycast to the right of vert, odd number of intersections means inside */ + int i, j, c = 0; + + for (i = 0, j = nfp->nverts - 1; i < nfp->nverts; j = i++) { + if (((nfp->final_pos[i]->y > p[1]) != (nfp->final_pos[j]->y > p[1])) && + (p[0] < (nfp->final_pos[j]->x - nfp->final_pos[i]->x) * (p[1] - nfp->final_pos[i]->y) / (nfp->final_pos[j]->y - nfp->final_pos[i]->y) + nfp->final_pos[i]->x)) + c = !c; + } + + return c; +} + +static bool p_is_concave(PChart *chart, const float(*hull_points)[2], int nboundaries) +{ + if (nboundaries <= 4) { + //printf("<= 4 boundary edges, convex\n"); + return false; + } + + /* ToDo SaphireS */ + if (is_poly_convex_v2((const float(*)[2])hull_points, nboundaries)){ + //printf("is_poly_convex true, convex\n"); + return false; + } + else { + //printf("is_poly_convex false, concave\n"); + return true; + } +} + +static bool p_temp_cfr_check(PNoFitPolygon **nfps, PNoFitPolygon *ifp, PPointUV *pp, int nfp_count, int index) +{ + float v1[2], v2[2], v3[2], p[2]; + int i; + + p[0] = pp->x; + p[1] = pp->y; + + /* Make sure point is inside IFP */ + if (p[0] < ifp->final_pos[0]->x || + p[1] < ifp->final_pos[0]->y || + p[0] > ifp->final_pos[2]->x || + p[1] > ifp->final_pos[2]->y) { + /*printf("--end_pos outside IFP!\n");*/ + return false; + } + + /* Make sure point is outside other NFPs */ + for (i = 0; i < nfp_count; i++) { + if (nfps[i] && (i != index)) { + if (nfps[i]->nverts == 3) { + v1[0] = nfps[i]->final_pos[0]->x; + v1[1] = nfps[i]->final_pos[0]->y; + + v2[0] = nfps[i]->final_pos[1]->x; + v2[1] = nfps[i]->final_pos[1]->y; + + v3[0] = nfps[i]->final_pos[2]->x; + v3[1] = nfps[i]->final_pos[2]->y; + + /*if (p_triangle_inside_v3_v2(nfps[i]->final_pos[0], + nfps[i]->final_pos[1], + nfps[i]->final_pos[2], p)) {*/ + if (p_triangle_inside_v3_v2(v1, v2, v3, p)) { + /*printf("--end_pos x: %f y: %f is inside nfps[%i]!\n", p[0], p[1], i);*/ + return false; + } + } + else { + if (p_point_inside_nfp(nfps[i], p)) { + /*printf("--end_pos x: %f y: %f is inside nfps[%i]!\n", p[0], p[1], i);*/ + return false; + } + } + } + } + + printf("--end_pos inside IFP and outside of other NFPs!\n"); + return true; +} + +#ifndef RAND_NFP_POS +static PNoFitPolygon *p_collision_free_region_create(PNoFitPolygon **nfps, PNoFitPolygon *ifp, int n_nfps) +{ + PNoFitPolygon *cfr = (PNoFitPolygon *)MEM_callocN(sizeof(*cfr), "PCollisionFreeRegion"); + cfr->nverts = 100; + cfr->final_pos = (PVert **)MEM_mallocN(sizeof(PPointUV *) * cfr->nverts, "PNFPPoints");; + PPointUV *p; + int i, j, k = 0; + float pos[2]; + + for (i = 0; i <= n_nfps; i++) { + if (nfps[i]){ + for (j = 0; j < nfps[i]->nverts; j++) { + p = nfps[i]->final_pos[j]; + pos[0] = p->x; + pos[1] = p->y; + if (p_temp_cfr_check(nfps, ifp, pos, n_nfps, i)) { + /* Add point to CFR */ + cfr->final_pos[k] = p; + k++; + if (k == (cfr->nverts - 1)) { + /* realloc */ + cfr->nverts += 100; + cfr->final_pos = MEM_recallocN_id(cfr->final_pos, sizeof(PPointUV *) * cfr->nverts, "PNFPPoints"); + } + } + } + } + } + + /*for (i = k; i < cfr->nverts; i++) { + MEM_freeN(cfr->final_pos[i]); + } */ + + cfr->nverts = k; + + /* ToDo SaphireS: Add intersection points of nfps */ + + return cfr; +} +#endif + +/* Make sure vert order for item is CCW and for fixed is CW! */ +static PNoFitPolygon *p_no_fit_polygon_create(PConvexHull *item, PConvexHull *fixed) +{ + PNoFitPolygon *nfp = (PNoFitPolygon *)MEM_callocN(sizeof(*nfp), "PNoFitPolygon"); + nfp->nverts = item->nverts + fixed->nverts; + PVert **points = (PVert **)MEM_mallocN(sizeof(PVert *) * nfp->nverts, "PNFPPoints"); + PPointUV **fpoints = (PPointUV **)MEM_mallocN(sizeof(PPointUV *) * nfp->nverts, "PNFPFPoints"); + nfp->final_pos = (PPointUV **)MEM_callocN(sizeof(*nfp->final_pos) * nfp->nverts, "PNFPFinalPos"); + int i, j; + + /* Assign verts of hulls to NFP */ + for (i = 0; i < nfp->nverts; i++) { + if (i < item->nverts) { + points[i] = item->h_verts[i]; + fpoints[i] = item->verts[i]; + } + else { + points[i] = fixed->h_verts[i - item->nverts]; + fpoints[i] = fixed->verts[i - item->nverts]; + } + } + //printf("-Assignment to points done\n"); + + /* sort edges according to horizontal angle, biggest to smallest */ + /*qsort(points, (size_t)nfp->nverts, sizeof(PVert *), vert_anglesort);*/ + qsort(fpoints, (size_t)nfp->nverts, sizeof(PPointUV *), point_anglesort); + //printf("-Sorting done\n"); + + for (i = 0; i < nfp->nverts; i++) { + /*printf("-- horizontal angle of points[%i]: %f\n", i, points[i]->edge->u.horizontal_angle);*/ + //printf("-- horizontal angle of fpoints[%i]: %f\n", i, fpoints[i]->angle); + } + + //printf("***item ref h_vertex: x: %f, y: %f\n", item->h_verts[item->ref_vert_index]->uv[0], item->h_verts[item->ref_vert_index]->uv[1]); + //printf("***fixed ref h_vertex: x: %f, y: %f\n", fixed->h_verts[fixed->ref_vert_index]->uv[0], fixed->h_verts[fixed->ref_vert_index]->uv[1]); + //printf("***item ref vertex: x: %f, y: %f\n", item->verts[item->ref_vert_index]->x, item->verts[item->ref_vert_index]->y); + //printf("***fixed ref vertex: x: %f, y: %f\n", fixed->verts[fixed->ref_vert_index]->x, fixed->verts[fixed->ref_vert_index]->y); + + + /* Minkowski sum computation */ + //printf("-PPointUV creation started!\n"); + PPointUV *p = (PPointUV *)MEM_callocN(sizeof(*p), "PPointUV"); + p->x = fixed->verts[fixed->ref_vert_index]->x; + p->y = fixed->verts[fixed->ref_vert_index]->y; + //p = fixed->verts[fixed->ref_vert_index]; /* ToDo SaphireS: Good way? */ + nfp->final_pos[0] = p; + + for (j = 1; j < nfp->nverts; j++) { + PPointUV *p1 = (PPointUV *)MEM_callocN(sizeof(*p1), "PPointUV1"); + p1->x = nfp->final_pos[j - 1]->x + fpoints[j - 1]->delta_edge[0]; + p1->y = nfp->final_pos[j - 1]->y + fpoints[j - 1]->delta_edge[1]; + nfp->final_pos[j] = p1; + } + //printf("-PPointUV creation done!\n"); + + for (i = 0; i < nfp->nverts; i++) { + //printf("--NFP Vert (offset == 0): x: %f, y: %f\n", nfp->final_pos[i]->x, nfp->final_pos[i]->y); + } + + /* free memory */ + MEM_freeN(points); + MEM_freeN(fpoints); + + return nfp; +} + +static void p_no_fit_polygon_delete(PNoFitPolygon *nfp) +{ + int i; + for (i = 0; i < nfp->nverts; i++) { + if (nfp->final_pos[i]) + MEM_freeN(nfp->final_pos[i]); + } + MEM_freeN(nfp->final_pos); + MEM_freeN(nfp); +} + +static void p_place_chart(PChart* item, PConvexHull *ch_item, PPointUV *pos) +{ + float cur_pos[2], trans[2]; + + cur_pos[0] = ch_item->verts[ch_item->ref_vert_index]->x; + cur_pos[1] = ch_item->verts[ch_item->ref_vert_index]->y; + + //printf("-ref_vert x: %f\n", cur_pos[0]); + //printf("-ref_vert y: %f\n", cur_pos[1]); + //printf("-end_pos x: %f\n", pos->x); + //printf("-end_pos y: %f\n", pos->y); + + trans[0] = cur_pos[0] - pos->x; + trans[1] = cur_pos[1] - pos->y; + + trans[0] = -trans[0]; + trans[1] = -trans[1]; + + p_chart_uv_translate(item, trans); + //printf("-translation done!\n"); +} + +static bool p_chart_pack_individual(PHandle *phandle, PChart *item, float margin) +{ + /* compute number of NFPs */ + int num_nfp = 0, k; + for(k = 0; k < phandle->ncharts; k++) { + PChart *chart = phandle->charts[k]; + if (chart->u.ipack.decomposed) { + num_nfp += chart->u.ipack.ntris; + } + else { + num_nfp++; + } + } + PNoFitPolygon **nfps = (PNoFitPolygon **)MEM_callocN(sizeof(PNoFitPolygon *) * (num_nfp + 1), "PNoFitPolygons"); +#ifndef RAND_NFP_POS + PNoFitPolygon *cfr; +#endif + PConvexHull *ch_item = item->u.ipack.convex_hull; + PChart *fixed; + printf("end_pos create\n"); + PPointUV *end_pos = (PPointUV *)MEM_callocN(sizeof(PPointUV *), "PPointUV end_pos"); + float delta_edge[2], randf1, randf2, r = 0.0f; + int i, j, cur_nfp = 0, cur_iter = 0, max_iter = 100; + unsigned int rand1, rand2; + bool found = false, init = true; + + /* Reverse winding direction of item convex hull */ + /* ref_vert_index now contains the vert with the lowest y value */ + PConvexHull *item_inv = p_convex_hull_reverse_direction(ch_item); + + /* compute no fit polygons (NFPs) of item with already placed charts */ + //printf("NFPs construction start!\n"); + for (i = 0; i < phandle->ncharts; i++) { + fixed = phandle->charts[i]; + /* ToDo SaphireS: Do not create NFP with self */ + /*Since placed=false it won't happen, but may be better to make sure? */ + if (fixed->u.ipack.convex_hull->placed) { + if (fixed->u.ipack.decomposed) { + for (j = 0; j < fixed->u.ipack.ntris; j++) { + PConvexHull *ch_fixed = fixed->u.ipack.tris[j]; + PNoFitPolygon *nfp = p_no_fit_polygon_create(item_inv, ch_fixed); + nfps[cur_nfp] = nfp; + cur_nfp++; + } + init = false; + } + else { + PConvexHull *ch_fixed = fixed->u.ipack.convex_hull; + PNoFitPolygon *nfp = p_no_fit_polygon_create(item_inv, ch_fixed); + nfps[cur_nfp] = nfp; + cur_nfp++; + init = false; + } + } + else { + nfps[cur_nfp] = NULL; + cur_nfp++; + } + } + printf("NFPs construction done!\n"); + + /* compute inner fit polygon (IFP) */ + //printf("IFP construction start!\n"); + PNoFitPolygon *ifp = p_inner_fit_polygon_create(phandle, item_inv); + /*nfps[phandle->ncharts] = ifp;*/ + nfps[cur_nfp] = ifp; + //printf("IFP construction done!\n"); + +#ifndef RAND_NFP_POS + /* compute collsion free region (CFR) */ + cfr = p_collision_free_region_create(nfps, ifp, phandle->ncharts); +#endif + + /* Choose placement point */ + if (init) { + /* First item, place in bottom left corner */ + end_pos->x = ifp->final_pos[0]->x; + end_pos->y = ifp->final_pos[0]->y; + //printf("Placing initial chart according to IFP!\n"); + found = true; + } + else { +#ifdef RAND_NFP_POS + while (!found) { + /* Old method, randomly choosing a point */ + cur_iter++; + randf1 = BLI_rng_get_float(phandle->rng); + //rand1 = p_float_to_int_range(item->u.ipack.sa_params[1], phandle->ncharts + 1); + /*rand1 = p_float_to_int_range(randf1, phandle->ncharts + 1);*/ + rand1 = p_float_to_int_range(randf1, cur_nfp + 1); + /*printf("-rand1 choosen as: %i\n", rand1);*/ + if (nfps[rand1]) { + + randf2 = BLI_rng_get_float(phandle->rng); + //rand2 = p_float_to_int_range(item->u.ipack.sa_params[2], nfps[rand1]->nverts); /* ToDo: Actual point amount in cfr */ + //rand2 = p_float_to_int_range(randf2, nfps[rand1]->nverts); + r = 0.0f; + rand2 = p_float_to_int_range_remainder(randf2, nfps[rand1]->nverts, &r); + /*printf("--rand2 choosen as: %i, r as %f\n", rand2, r);*/ + + if (nfps[rand1]->final_pos[rand2]) { + /* Account for sliding along edges here to cover all possible placements */ + if(rand2 == (nfps[rand1]->nverts - 1)){ + delta_edge[0] = nfps[rand1]->final_pos[0]->x - nfps[rand1]->final_pos[rand2]->x; + delta_edge[1] = nfps[rand1]->final_pos[0]->y - nfps[rand1]->final_pos[rand2]->y; + } + else { + delta_edge[0] = nfps[rand1]->final_pos[rand2 + 1]->x - nfps[rand1]->final_pos[rand2]->x; + delta_edge[1] = nfps[rand1]->final_pos[rand2 + 1]->y - nfps[rand1]->final_pos[rand2]->y; + } + + end_pos->x = nfps[rand1]->final_pos[rand2]->x + delta_edge[0] * r; + end_pos->y = nfps[rand1]->final_pos[rand2]->y + delta_edge[1] * r; + + found = p_temp_cfr_check(nfps, ifp, end_pos, cur_nfp, rand1); + } + } + + if (cur_iter >= max_iter) { + found = false; + break; + } + } +#endif +#ifndef RAND_NFP_POS + /* New method, cfr */ + rand1 = p_float_to_int_range(item->u.ipack.sa_params[2], cfr->nverts); + + if (cfr->final_pos[rand1]) { + end_pos[0] = cfr->final_pos[rand1]->x; + end_pos[1] = cfr->final_pos[rand1]->y; + found = true; + } + else { + printf("Error: cfr->final_pos[%i] doesn't exist!\n", rand1); + break; + } +#endif + } + + if (found) { + if (!init) { + printf("--found placement for rand1: %i, rand2: %i, r as %f\n", rand1, rand2, r); + } + + /* Place chart */ + p_place_chart(item, item_inv, end_pos); + + /* ToDo SaphireS: Verify placement? */ + ch_item->placed = true; + if (item->u.ipack.decomposed){ + for (i = 0; i < item->u.ipack.ntris; i++) { + item->u.ipack.tris[i]->placed = true; + } + } + } + + /* delete temporary inversed convex hull */ + p_convex_hull_delete(item_inv); + p_convex_hull_restore_direction(ch_item, margin); + if (item->u.ipack.decomposed){ + for (i = 0; i < item->u.ipack.ntris; i++){ + PConvexHull *tri = item->u.ipack.tris[i]; + p_convex_hull_restore_direction(tri, margin); + } + } + printf("-restoring angles/edges done!\n"); + + /* CleanUp, pay attention to also delete IFP which is part of nfps */ +#ifndef RAND_NFP_POS + MEM_freeN(cfr); +#endif + MEM_freeN(end_pos); + + for (j = 0; j <= num_nfp; j++) { + if (nfps[j]) { + p_no_fit_polygon_delete(nfps[j]); + } + } + MEM_freeN(nfps); + printf("-freeing stuff done!\n"); + p_flush_uvs(phandle, item); /* ToDo SaphireS: Needs update to work ... */ + + return found; +} + +static float p_scale_binary_search(PHandle *phandle, PChart *chart, float val, float range, int depth, float abs_scale, float found, float margin) +{ + float range1, val1; + + if (depth--) { + p_chart_uv_scale_origin(chart, val); + p_convex_hull_update_all(chart->u.ipack.convex_hull, margin); + + abs_scale *= val; + + if (!(p_chart_pack_individual(phandle, chart, margin))) { + /*scale down */ + //printf("-no placement found for scale: %f, trying scaling down\n", val); + range1 = range * 0.5f; + val1 = 1.0f - range1; + + return p_scale_binary_search(phandle, chart, val1, range1, depth, abs_scale, found, margin); + } + else { + /* scale up */ + //printf("-placement found for scale: %f, trying scaling up\n", val); + range1 = range * 0.5f; + val1 = 1.0f + range; + found = abs_scale; + + return p_scale_binary_search(phandle, chart, val1, range1, depth, abs_scale, found, margin); + } + } + else { + printf("-scale factor found: %f\n", (found / abs_scale)); + p_chart_uv_scale_origin(chart, 1.0f / (found / abs_scale)); // = abs_scale / found + p_convex_hull_update_all(chart->u.ipack.convex_hull, margin); + + return (found / abs_scale); + } +} + +static float p_binary_depth_search(PHandle *phandle, PChart *chart, int depth, float margin) +{ + float value = 0.5f, abs_scale = chart->u.ipack.last_scale/*1.0f*/, found = 1.0f; + + return p_scale_binary_search(phandle, chart, value, value, depth, abs_scale, found, margin); +} + +static bool p_compute_packing_solution(PHandle *phandle, float margin /* ToDo SaphireS: Simulated Annealing parameters */) +{ + PChart *chart; + int i, j; + int depth = 4; + + /* Place UV islands one by one */ + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + float scale = 1.0f; + + //printf("p_compute_packing_solution: chart[%i] with area %f:\n", i, chart->u.ipack.area); + + if (chart->u.ipack.decomposed) { + if (!(chart->u.ipack.tris[0]->placed)) { + if (!p_chart_pack_individual(phandle, chart, margin)) { + /* binary depth search for scaling down the current chart */ + scale = p_binary_depth_search(phandle, chart, depth, margin); + chart->u.ipack.last_scale *= scale; + printf("p_binary_depth_search() done, scale = %f! \n", scale); + + /* ToDo SaphireS: Avoid recomputation, store placement/scale of best solution from binary depth search! */ + /* scale chart */ + p_chart_uv_scale_origin(chart, scale); + for (j = 0; j < chart->u.ipack.ntris; j++) { + PConvexHull *hull = chart->u.ipack.tris[j]; + + p_convex_hull_update_all(hull, margin); + + p_chart_pack_individual(phandle, chart, margin); + hull->placed = true; + } + } + } + } + else { + if (!(chart->u.ipack.convex_hull->placed)) { + if (!p_chart_pack_individual(phandle, chart, margin)) { + /* binary depth search for scaling down the current chart */ + scale = p_binary_depth_search(phandle, chart, depth, margin); + chart->u.ipack.last_scale *= scale; + printf("p_binary_depth_search() done, scale = %f! \n", scale); + + /* ToDo SaphireS: Avoid recomputation, store placement/scale of best solution from binary depth search! */ + /* scale chart */ + p_chart_uv_scale_origin(chart, scale); + + p_convex_hull_update(chart->u.ipack.convex_hull, true); + + p_chart_pack_individual(phandle, chart, margin); + chart->u.ipack.convex_hull->placed = true; + } + } + } + } + + /* Un-set placed property of charts so next iteration works as expected */ + /*for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + chart->u.ipack.convex_hull->placed = false; + }*/ + + return true; +} + +void param_irregular_pack_begin(ParamHandle *handle, float *w_area, float margin, int rot_step, bool concave) +{ + PHandle *phandle = (PHandle *)handle; + PChart *chart; + PEdge *outer, *e; + PFace *f; + int i, j, nboundaries = 0; + unsigned int seed = 31415925; + float used_area, init_scale, init_value = 0.6f, randf1, rot; + + param_assert(phandle->state == PHANDLE_STATE_CONSTRUCTED); + phandle->state = PHANDLE_STATE_PACK; + + /* Initializations */ + phandle->rng = BLI_rng_new(seed); + used_area = p_face_uv_area_combined(handle); + init_scale = init_value / used_area; + printf("init_scale: %f\n", init_scale); + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + /* Back up UVs */ + for (f = chart->faces; f; f = f->nextlink) { + p_face_backup_uvs(f); + } + + /* Set initial scale of charts */ + /* ToDo: Do this in p_compute_packing_solution */ + /*p_chart_uv_scale_origin(chart, init_scale); */ + + /* Initial random Simulated Annealing parameters*/ + randf1 = BLI_rng_get_float(phandle->rng); + chart->u.ipack.sa_params[0] = randf1; /* theta */ + //printf("-init theta for chart[%i]: %f\n", i, chart->u.ipack.sa_params[0]); + randf1 = BLI_rng_get_float(phandle->rng); + chart->u.ipack.sa_params[1] = randf1; /* m */ + //printf("-init m for chart[%i]: %f\n", i, chart->u.ipack.sa_params[1]); + randf1 = BLI_rng_get_float(phandle->rng); + chart->u.ipack.sa_params[2] = randf1; /* f */ + //printf("-init f for chart[%i]: %f\n", i, chart->u.ipack.sa_params[2]); + + /* Initial rotation */ + rot = (int)(chart->u.ipack.sa_params[0] * (float)rot_step) * (2 * M_PI / (float)rot_step); + //printf("init rot for chart[%i]: %f\n", i, rot); + p_chart_uv_rotate_origin(chart, rot); + + /* Get boundaries of chart*/ + /* Find initial boundary edge */ + + nboundaries = 0; + p_chart_boundaries(chart, NULL, &outer); + if (!outer) { + /* ToDo Saphires: BKE_report(..) */ + printf("Warning: p_chart_boundaries: No boundary edge found for chart %i!\n", i); + chart->u.ipack.convex_hull = p_convex_hull_new(chart); + chart->u.ipack.convex_hull->placed = true; + chart->u.ipack.decomposed = false; + break; + } + + /* Get number of boundary edges */ + e = outer; + do { + nboundaries++; + e = p_boundary_edge_next(e); + } while (e != outer); + + //printf("number of boundary edges: %i\n", nboundaries); + PVert **bounds = (PVert **)MEM_mallocN(sizeof(PVert *) * nboundaries, "PCHullpoints"); + float(*hullverts)[2] = BLI_array_alloca(hullverts, nboundaries); + + /* Get boundary verts */ + e = outer; + j = 0; + do { + bounds[j] = e->vert; + hullverts[j][0] = e->vert->uv[0]; + hullverts[j][1] = e->vert->uv[1]; + /*printf(">>bounds: vert x: %f, y: %f\n", bounds[j]->uv[0], bounds[j]->uv[1]); + printf(">-hullverts x: %f, y: %f\n", hullverts[j][0], hullverts[j][1]);*/ + j++; + e = p_boundary_edge_next(e); + } while (e != outer); + + /* Start computation of hull(s) for chart ---------------------------*/ + /* Determine if chart is concave or convex */ + if (concave && p_is_concave(chart, (const float(*)[2])hullverts, nboundaries)) { + //printf("Entered concave execution path\n"); + /* Decompose concave hull into convex hulls and store within chart */ + chart->u.ipack.tris = p_decompose_triangulate_chart(chart, (const float(*)[2])hullverts, nboundaries); + chart->u.ipack.decomposed = true; + chart->u.ipack.convex_hull = p_convex_hull_new(chart); + chart->u.ipack.best_pos = MEM_callocN(sizeof(PPointUV), "PPointUV"); + //printf("--chart decomposed into %i tris\n", chart->u.ipack.ntris); + + /* For each convex hull: */ + for (j = 0; j < chart->u.ipack.ntris; j++) { + PConvexHull *hull = chart->u.ipack.tris[j]; + + /* Apply margin */ + if (!(compare_ff(margin, 0.0f, 0.0001f))) { + p_convex_hull_grow(hull, margin); + p_convex_hull_update(hull, false); + } + + /* Compute horizontal angle for edges of hull (Needed for NFP) */ + p_convex_hull_compute_horizontal_angles(hull); + /* Compute edge lengths */ + p_convex_hull_compute_edge_components(hull); + + /* ToDo SaphireS: turn last few steps into a reusable function for cleaner code */ + } + + //printf("Bounds of chart [%i]: minx: %f, maxx: %f, miny: %f,maxy: %f\n", i, chart->u.ipack.convex_hull->min_v[0], chart->u.ipack.convex_hull->max_v[0], chart->u.ipack.convex_hull->min_v[1], chart->u.ipack.convex_hull->max_v[1]); + /*chart->u.ipack.convex_hull = NULL;*/ /* ToDo SaphireS: Unifiy tris and convex_hull */ + } + else { + //printf("Entered convex execution path\n"); + /* Compute convex hull for each chart -> CW */ + chart->u.ipack.convex_hull = p_convex_hull_new(chart); + chart->u.ipack.decomposed = false; + chart->u.ipack.best_pos = MEM_callocN(sizeof(PPointUV), "PPointUV"); + + /* Apply margin here */ + if (!(compare_ff(margin, 0.0f, 0.0001f))) { + p_convex_hull_grow(chart->u.ipack.convex_hull, margin); + p_convex_hull_update(chart->u.ipack.convex_hull, false); + } + + /* Compute horizontal angle for edges of hull (Needed for NFP) */ + p_convex_hull_compute_horizontal_angles(chart->u.ipack.convex_hull); + /* Compute edge lengths */ + p_convex_hull_compute_edge_components(chart->u.ipack.convex_hull); + + /* DEBUG */ + //printf("Bounds of chart [%i]: minx: %f, maxx: %f, miny: %f,maxy: %f\n", i, chart->u.ipack.convex_hull->min_v[0], chart->u.ipack.convex_hull->max_v[0], chart->u.ipack.convex_hull->min_v[1], chart->u.ipack.convex_hull->max_v[1]); + + chart->u.ipack.tris = NULL; /* ToDo SaphireS: Unifiy tris and convex_hull */ + } + + chart->u.ipack.area = p_chart_uv_area_signed(chart); /* used for sorting */ + + MEM_freeN(bounds); + } + + /* Sort UV islands by area */ + qsort(phandle->charts, (size_t)phandle->ncharts, sizeof(PChart *), chart_areasort); + + if (p_compute_packing_solution(phandle, margin)) { + printf("Initial packing solution found---------------------------------------------\n"); + } + + used_area = p_face_uv_area_combined(handle); + + /* ToDo(SaphireS): Account for aspect ratio != 1 */ + *w_area = 1.0f - used_area; +} + +void param_irregular_pack_iter(ParamHandle *handle, float *w_area, unsigned int seed, int rot_step, float margin) +{ + PHandle *phandle = (PHandle *)handle; + PChart* chart; + float randf, rot, rand_value, rot_rand, place_rand, upscale_fac = 1.5f; + int rand_chart, rand_param, i; + + BLI_rng_seed(phandle->rng, seed); + + printf("starting param_irregular_pack_iter-----------------------------\n"); + + param_assert(phandle->state == PHANDLE_STATE_PACK); + + /* packing solution for random part, based on last solution */ + randf = BLI_rng_get_float(phandle->rng); + rand_chart = p_float_to_int_range(randf, phandle->ncharts); + chart = phandle->charts[rand_chart]; + chart->u.ipack.convex_hull->placed = false; + if (chart->u.ipack.decomposed) { + for (i = 0; i < chart->u.ipack.ntris; i++){ + chart->u.ipack.tris[i]->placed = false; + } + } + + /* Set initial scale of charts so finding a better solution is possible */ + p_chart_uv_scale_origin(chart, upscale_fac); /* ToDo SaphireS: Find best upscale_fac value */ + chart->u.ipack.last_scale = upscale_fac; + p_convex_hull_update_all(chart->u.ipack.convex_hull, margin); + + /* Set random SA parameter of chosen chart to new value */ + randf = BLI_rng_get_float(phandle->rng); + rand_param = p_float_to_int_range(randf, 3); /* 0 = theta, 1 = m, 2 = f */ + rand_value = BLI_rng_get_float(phandle->rng); + rand_value -= 0.5f; + + if (rand_param == 0) { + rot_rand = fabsf(remainderf(chart->u.ipack.sa_params[0] + rand_value, 1.0f)); + printf("SA param rot_rand for chart[%i]: %f\n", rand_chart, rot_rand); + rot = (int)(rot_rand * (float)rot_step) * (2 * M_PI / (float)rot_step); + printf("SA param rot for chart[%i]: %f\n", rand_chart, rot); + //p_chart_uv_rotate_origin(chart, rot); + p_convex_hull_update_all(chart->u.ipack.convex_hull, margin); + + if (chart->u.ipack.decomposed) { + for (i = 0; i < chart->u.ipack.ntris; i++){ + PConvexHull *tri = chart->u.ipack.tris[i]; + p_convex_hull_update_all(tri, margin); + } + } + } + else { + place_rand = fabsf(remainderf(chart->u.ipack.sa_params[2] + rand_value, 1.0f)); + chart->u.ipack.sa_params[2] = place_rand; + printf("SA param place_rand for chart[%i]: %f\n", rand_chart, place_rand); + } + + /* Find placement for part */ + if (p_compute_packing_solution(phandle, margin)) { + printf("packing solution found---------------------------------------------\n"); + } + + float used_area = p_face_uv_area_combined(handle); + + /* ToDo(SaphireS): Account for aspect ratio != 1 */ + *w_area = 1.0f - used_area; + +} + +void param_irregular_pack_end(ParamHandle *handle) +{ + PHandle *phandle = (PHandle *)handle; + PChart *chart; + int i, j; + + param_assert(phandle->state == PHANDLE_STATE_PACK); + phandle->state = PHANDLE_STATE_CONSTRUCTED; + + /* ToDo (SaphireS) */ + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + MEM_freeN(chart->u.ipack.best_pos); + + if (chart->u.ipack.decomposed){ + for (j = 0; j < chart->u.ipack.ntris; j++) { + p_convex_hull_delete(chart->u.ipack.tris[j]); + } + MEM_freeN(chart->u.ipack.tris); + } + + p_convex_hull_delete(chart->u.ipack.convex_hull); + } + + BLI_rng_free(phandle->rng); + phandle->rng = NULL; +} + void param_average(ParamHandle *handle) { PChart *chart; @@ -4669,6 +6295,248 @@ void param_scale(ParamHandle *handle, float x, float y) } } +void param_scale_bounds(ParamHandle *handle) +{ + PHandle *phandle = (PHandle *)handle; + PChart *chart; + int i; + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + float tot_width, tot_height, scale; + float minv[2], maxv[2], trans[2]; + + /* Compute bounds of chart */ + p_chart_uv_bbox(chart, minv, maxv); + + /* Move center to 0,0 */ + trans[0] = (minv[0] + maxv[0]) / -2.0f; + trans[1] = (minv[1] + maxv[1]) / -2.0f; + p_chart_uv_translate(chart, trans); + + /* Compute width/height of bounds */ + if (signf(minv[0]) != signf(maxv[0])) + tot_width = fabsf(minv[0]) + fabsf(maxv[0]); + else + tot_width = maxv[0] - minv[0]; + + if (signf(minv[1]) != signf(maxv[1])) + tot_height = fabsf(minv[1]) + fabsf(maxv[1]); + else + tot_height = maxv[1] - minv[1]; + + if (tot_height >= tot_width) + scale = phandle->aspy / tot_height; + else + scale = phandle->aspx / tot_width; + + /* Scale to fit UV area */ + p_chart_uv_scale(chart, scale); + + /* Move chart to center of UV Space*/ + trans[0] = phandle->aspx / 2.0f; + trans[1] = phandle->aspy / 2.0f; + p_chart_uv_translate(chart, trans); + } +} + +static void p_verttag_add_adjacent(Heap *heap, PVert *v_a, PVert **v_prev, + float *cost, bool use_topology_distance) +{ + const int v_a_index = v_a->u.id; + PEdge *e, *we, *lastwe = NULL; + e = v_a->edge; + + /* rewind to start */ + lastwe = e; + for (we = p_wheel_edge_prev(e); we && (we != e); we = p_wheel_edge_prev(we)) { + lastwe = we; + } + + we = lastwe; + do { + PVert *v_b = we->next->vert; + if (!(v_b->flag & PVERT_MARKED) && !(we->flag & PEDGE_DIAG )) { + /* v_b not visited yet, check it out! */ + const int v_b_index = v_b->u.id; + const float cost_cut = use_topology_distance ? 1.0f : len_v2v2(v_a->uv, v_b->uv); + const float cost_new = cost[v_a_index] + cost_cut; + + if (cost[v_b_index] > cost_new) { + cost[v_b_index] = cost_new; + v_prev[v_b_index] = v_a; + BLI_heap_insert(heap, cost_new, v_b); + } + } + + we = p_wheel_edge_next(we); + } while (we && (we != lastwe)); +} + +static LinkNode *p_calc_path_vert(PChart *chart, PVert *src, PVert *dst, bool topological_distance) +{ + LinkNode *path = NULL; + Heap *heap; + PVert *vert; + PVert **verts_prev; + float *cost; + int totvert, index; + + /* Clear flags, assign index */ + for (vert = chart->verts, index = 0; vert; vert = vert->nextlink, index++) { + vert->flag &= ~PVERT_MARKED; + vert->u.id = index; /* Re-use lscm id field */ + } + + /* alloc */ + totvert = chart->nverts; + verts_prev = MEM_callocN(sizeof(*verts_prev) * totvert, __func__); + cost = MEM_mallocN(sizeof(*cost) * totvert, __func__); + + copy_vn_fl(cost, totvert, 1e20f); + + /* regular dijkstra shortest path */ + heap = BLI_heap_new(); + BLI_heap_insert(heap, 0.0f, src); + cost[src->u.id] = 0.0f; + + while (!BLI_heap_is_empty(heap)) { + vert = BLI_heap_popmin(heap); + + if (vert == dst) { + break; + } + + if (!(vert->flag & PVERT_MARKED)) { + vert->flag |= PVERT_MARKED; + p_verttag_add_adjacent(heap, vert, verts_prev, cost, topological_distance); + } + } + + if (vert == dst) { + do { + BLI_linklist_prepend(&path, vert); + } while (vert = verts_prev[vert->u.id]); + } + + MEM_freeN(verts_prev); + MEM_freeN(cost); + BLI_heap_free(heap, NULL); + + return path; +} + +static void p_vert_select_edges(PVert *v) +{ + v->flag |= PVERT_SELECT; + PEdge *e = v->edge; + e->flag |= PEDGE_SELECT; + + /* rewind to start */ + PEdge *we, *lastwe = e; + for (we = p_wheel_edge_prev(e); we && (we != e); we = p_wheel_edge_prev(we)) { + lastwe = we; + } + + we = lastwe; + do { + + we->flag |= PEDGE_SELECT; + PVert *v_e = we->vert; + v_e->flag |= PVERT_SELECT; + we = p_wheel_edge_next(we); + + } while (we && (we != lastwe)); +} + +void param_shortest_path(ParamHandle *handle, bool *p_found, bool topological_distance) +{ + PHandle *phandle = (PHandle *)handle; + PChart *chart, *current_chart = 0; + PVert *v, *vert_src = NULL, *vert_dst = NULL; + int i; + bool success = false; + + if (phandle->ncharts == 0) { + *p_found = success; + return; + } + + /* Get src and dst */ + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + for (v = chart->verts; v; v = v->nextlink) { + + if (v->flag & PVERT_SELECT) { + + if (vert_src == NULL) { + vert_src = v; + current_chart = chart; + } + else if (vert_dst == NULL) { + if (current_chart == chart) { + vert_dst = v; + } + } + } + } + } + + /* Connect src and dst */ + LinkNode *path = NULL; + if (vert_src && vert_dst) { + + path = p_calc_path_vert(current_chart, vert_src, vert_dst, topological_distance); + + if (path) { + LinkNode *node = NULL; + node = path; + + do { + PVert *v1 = node->link; + p_vert_select_edges(v1); + } while (node = node->next); + + success = true; + BLI_linklist_free(path, NULL); + } + } + + *p_found = success; +} + +void param_select_overlapping(ParamHandle *handle, const bool extend) +{ + PHandle *phandle = (PHandle *)handle; + PChart *chart1, *chart2; + int i, j; + + /* deselect charts */ + if (!extend) + { + for (i = 0; i < phandle->ncharts; i++) { + chart1 = phandle->charts[i]; + p_chart_select(chart1, false); + } + } + + /* Check charts for intersections/overlaps */ + for (i = 0; i < phandle->ncharts; i++) { + chart1 = phandle->charts[i]; + + for (j = 0; j < phandle->ncharts; j++) { + chart2 = phandle->charts[j]; + if ((i != j) && p_charts_intersect(chart1, chart2)) { + /* select charts */ + p_chart_select(chart1, true); + p_chart_select(chart2, true); + } + } + } +} + void param_flush(ParamHandle *handle) { PHandle *phandle = (PHandle *)handle; @@ -4688,6 +6556,26 @@ void param_flush(ParamHandle *handle) } } +/* Use this function if you need to flush position + selection tags */ +void param_flush_sel(ParamHandle *handle) +{ + PHandle *phandle = (PHandle *)handle; + PChart *chart; + int i; + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + if ((phandle->state == PHANDLE_STATE_LSCM) && !chart->u.lscm.context) + continue; + + if (phandle->blend == 0.0f) + p_flush_uvs_selection(phandle, chart); + else + p_flush_uvs_blend(phandle, chart, phandle->blend); + } +} + void param_flush_restore(ParamHandle *handle) { PHandle *phandle = (PHandle *)handle; @@ -4703,3 +6591,110 @@ void param_flush_restore(ParamHandle *handle) } } +void param_accept_placement_all(ParamHandle *handle) +{ + PHandle *phandle = (PHandle *)handle; + PConvexHull *chull; + int i; + printf("param_store_packing_solution\n"); + + for (i = 0; i < phandle->ncharts; i++) { + PChart *chart = phandle->charts[i]; + chull = chart->u.ipack.convex_hull; + /*chart->u.ipack.best_pos->x = chull->h_verts[chull->ref_vert_index]->uv[0]; + chart->u.ipack.best_pos->y = chull->h_verts[chull->ref_vert_index]->uv[1];*/ + chart->u.ipack.best_pos->x = chull->verts[chull->ref_vert_index]->x; + chart->u.ipack.best_pos->y = chull->verts[chull->ref_vert_index]->y; + chart->u.ipack.last_scale = 1.f; + } +} + +void param_accept_placement(PChart *chart) +{ + PConvexHull *chull = chart->u.ipack.convex_hull; + int i; + printf("param_store_packing_solution\n"); + + /*chart->u.ipack.best_pos->x = chull->h_verts[chull->ref_vert_index]->uv[0]; + chart->u.ipack.best_pos->y = chull->h_verts[chull->ref_vert_index]->uv[1];*/ + chart->u.ipack.best_pos->x = chull->verts[chull->ref_vert_index]->x; + chart->u.ipack.best_pos->y = chull->verts[chull->ref_vert_index]->y; + chart->u.ipack.last_scale = 1.f; +} + + +void param_restore_placement(ParamHandle *handle, float margin) +{ + PHandle *phandle = (PHandle *)handle; + PConvexHull *chull; + float trans[2], cur_pos[2], scale_fac; + int i; + printf("param_restore_packing_solution\n"); + + + for (i = 0; i < phandle->ncharts; i++) { + PChart *chart = phandle->charts[i]; + chull = chart->u.ipack.convex_hull; + + if (chart->u.ipack.last_scale != 1.f) { + scale_fac = 1.f / chart->u.ipack.last_scale; + printf("scale_fac for chart[%i]: %f\n", i, scale_fac); + + p_chart_uv_scale_origin(chart, scale_fac); + p_convex_hull_update(chart->u.ipack.convex_hull, true); + p_convex_hull_grow(chart->u.ipack.convex_hull, margin); + p_convex_hull_update(chart->u.ipack.convex_hull, false); + p_convex_hull_compute_horizontal_angles(chart->u.ipack.convex_hull); /* ToDo: Shouldn't be necessary! */ + p_convex_hull_compute_edge_components(chart->u.ipack.convex_hull); + chart->u.ipack.last_scale = 1.f; + + /*cur_pos[0] = chull->h_verts[chull->ref_vert_index]->uv[0]; + cur_pos[1] = chull->h_verts[chull->ref_vert_index]->uv[1];*/ + cur_pos[0] = chull->verts[chull->ref_vert_index]->x; + cur_pos[1] = chull->verts[chull->ref_vert_index]->y; + + trans[0] = chart->u.ipack.best_pos->x - cur_pos[0]; + trans[1] = chart->u.ipack.best_pos->y - cur_pos[1]; + + p_chart_uv_translate(chart, trans); + } + } +} + +/* XXX (SaphireS): Remove */ +void param_test(ParamHandle *handle) +{ + /* TEST STUFF */ + printf("param_test() reached!\n"); + + float area = p_face_uv_area_combined(handle); + + PHandle *phandle = (PHandle *)handle; + PChart *chart; + PConvexHull *hull; + int i, j; + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + chart->u.ipack.convex_hull = p_convex_hull_new(chart); + hull = chart->u.ipack.convex_hull; + printf("convex hull for chart %i done!\n", i); + + for (j = 0; j < hull->nverts; j++) { + printf("--PVert of convex hull - x: %f, y: %f\n", hull->h_verts[j]->uv[0], hull->h_verts[j]->uv[1]); + printf("-+PPointUV of convex hull - x: %f, y: %f\n", hull->verts[j]->x, hull->verts[j]->y); + } + + printf("p_convex_hull_grow()\n"); + p_convex_hull_grow(hull, 0.1f); + + for (j = 0; j < hull->nverts; j++) { + printf("--PVert of convex hull - x: %f, y: %f\n", hull->h_verts[j]->uv[0], hull->h_verts[j]->uv[1]); + printf("-+PPointUV of convex hull - x: %f, y: %f\n", hull->verts[j]->x, hull->verts[j]->y); + } + + p_convex_hull_delete(hull); + } + + printf("used uv charts area: %f\n", area); +} diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.h b/source/blender/editors/uvedit/uvedit_parametrizer.h index eaea781971d..9764d3551f7 100644 --- a/source/blender/editors/uvedit/uvedit_parametrizer.h +++ b/source/blender/editors/uvedit/uvedit_parametrizer.h @@ -1,3 +1,4 @@ + /* * ***** BEGIN GPL LICENSE BLOCK ***** * @@ -63,7 +64,8 @@ void param_face_add(ParamHandle *handle, float *uv[4], ParamBool *pin, ParamBool *select, - float face_normal[3]); + float face_normal[3], + int **flag); void param_edge_set_seam(ParamHandle *handle, ParamKey *vkeys); @@ -101,6 +103,15 @@ void param_smooth_area(ParamHandle *handle); void param_pack(ParamHandle *handle, float margin, bool do_rotate); +/* Packing 2.0 */ + +void param_irregular_pack_begin(ParamHandle *handle, float *w_area, float margin, int rot_step, bool concave); +void param_irregular_pack_iter(ParamHandle *handle, float *w_area, unsigned int seed, int rot_step, float margin); +void param_irregular_pack_end(ParamHandle *handle); +void param_accept_placement_all(ParamHandle *handle); +//void param_accept_placement(PChart *chart); +void param_restore_placement(ParamHandle *handle, float margin); + /* Average area for all charts */ void param_average(ParamHandle *handle); @@ -109,11 +120,26 @@ void param_average(ParamHandle *handle); void param_scale(ParamHandle *handle, float x, float y); +/* Scale to bounds */ + +void param_scale_bounds(ParamHandle *handle); + +/* Select shortest Path */ + +void param_shortest_path(ParamHandle *handle, bool *p_found, bool topological_distance); + +/* Select Overlapping */ + +void param_select_overlapping(ParamHandle *handle, const bool extend); + /* Flushing */ void param_flush(ParamHandle *handle); +void param_flush_sel(ParamHandle *handle); void param_flush_restore(ParamHandle *handle); +/* XXX (SaphireS): Remove */ +void param_test(ParamHandle *handle); #ifdef __cplusplus } diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c index d8080002818..719b5c78926 100644 --- a/source/blender/editors/uvedit/uvedit_unwrap_ops.c +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -46,6 +46,7 @@ #include "BLI_utildefines.h" #include "BLI_alloca.h" #include "BLI_math.h" +#include "BLI_rand.h" #include "BLI_uvproject.h" #include "BLI_string.h" @@ -82,6 +83,20 @@ #include "uvedit_intern.h" #include "uvedit_parametrizer.h" +static int set_handle_params(bool implicit, bool fill_holes, bool selected, bool correct_aspect, bool all_faces) +{ + int hparams = 0; + + /* Set flags fpr handle_params */ + if (implicit) hparams |= HANDLE_IMPLICIT; + if (fill_holes) hparams |= HANDLE_FILL; + if (selected) hparams |= HANDLE_SELECTED; + if (correct_aspect) hparams |= HANDLE_CORRECT_ASPECT; + if (all_faces) hparams |= HANDLE_ALL_FACES; + + return hparams; +} + static void modifier_unwrap_state(Object *obedit, Scene *scene, bool *r_use_subsurf) { ModifierData *md; @@ -235,6 +250,7 @@ static void construct_param_handle_face_add(ParamHandle *handle, Scene *scene, ParamBool *select = BLI_array_alloca(select, efa->len); float **co = BLI_array_alloca(co, efa->len); float **uv = BLI_array_alloca(uv, efa->len); + int **flag = BLI_array_alloca(flag, efa->len); int i; BMIter liter; @@ -251,15 +267,14 @@ static void construct_param_handle_face_add(ParamHandle *handle, Scene *scene, co[i] = l->v->co; uv[i] = luv->uv; pin[i] = (luv->flag & MLOOPUV_PINNED) != 0; + flag[i] = &(luv->flag); select[i] = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); } - param_face_add(handle, key, i, vkeys, co, uv, pin, select, efa->no); + param_face_add(handle, key, i, vkeys, co, uv, pin, select, efa->no, flag); } -static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm, - const bool implicit, const bool fill, const bool sel, - const bool correct_aspect) +static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm, const int handle_params) { ParamHandle *handle; BMFace *efa; @@ -268,6 +283,12 @@ static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm, BMIter iter, liter; int i; + const bool implicit = handle_params & HANDLE_IMPLICIT; + const bool fill = handle_params & HANDLE_FILL; + const bool sel = handle_params & HANDLE_SELECTED; + const bool correct_aspect = handle_params & HANDLE_CORRECT_ASPECT; + const bool all_faces = handle_params & HANDLE_ALL_FACES; + const int cd_loop_uv_offset = CustomData_get_offset(&bm->ldata, CD_MLOOPUV); handle = param_construct_begin(); @@ -290,7 +311,7 @@ static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm, continue; } - if (implicit) { + if (implicit && !all_faces) { bool is_loopsel = false; BM_ITER_ELEM (l, &liter, efa, BM_LOOPS_OF_FACE) { @@ -325,7 +346,7 @@ static ParamHandle *construct_param_handle(Scene *scene, Object *ob, BMesh *bm, static void texface_from_original_index(BMFace *efa, int index, float **uv, ParamBool *pin, ParamBool *select, - Scene *scene, const int cd_loop_uv_offset) + int *flag, Scene *scene, const int cd_loop_uv_offset) { BMLoop *l; BMIter liter; @@ -334,6 +355,7 @@ static void texface_from_original_index(BMFace *efa, int index, float **uv, Para *uv = NULL; *pin = 0; *select = 1; + *flag = 0; if (index == ORIGINDEX_NONE) return; @@ -344,6 +366,7 @@ static void texface_from_original_index(BMFace *efa, int index, float **uv, Para *uv = luv->uv; *pin = (luv->flag & MLOOPUV_PINNED) ? 1 : 0; *select = uvedit_uv_select_test(scene, l, cd_loop_uv_offset); + *flag = luv->flag; break; } } @@ -445,6 +468,7 @@ static ParamHandle *construct_param_handle_subsurfed(Scene *scene, Object *ob, B ParamBool pin[4], select[4]; float *co[4]; float *uv[4]; + int flag[4]; BMFace *origFace = faceMap[i]; if (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { @@ -473,12 +497,12 @@ static ParamHandle *construct_param_handle_subsurfed(Scene *scene, Object *ob, B /* This is where all the magic is done. If the vertex exists in the, we pass the original uv pointer to the solver, thus * flushing the solution to the edit mesh. */ - texface_from_original_index(origFace, origVertIndices[mloop[0].v], &uv[0], &pin[0], &select[0], scene, cd_loop_uv_offset); - texface_from_original_index(origFace, origVertIndices[mloop[1].v], &uv[1], &pin[1], &select[1], scene, cd_loop_uv_offset); - texface_from_original_index(origFace, origVertIndices[mloop[2].v], &uv[2], &pin[2], &select[2], scene, cd_loop_uv_offset); - texface_from_original_index(origFace, origVertIndices[mloop[3].v], &uv[3], &pin[3], &select[3], scene, cd_loop_uv_offset); + texface_from_original_index(origFace, origVertIndices[mloop[0].v], &uv[0], &pin[0], &select[0], &flag[0], scene, cd_loop_uv_offset); + texface_from_original_index(origFace, origVertIndices[mloop[1].v], &uv[1], &pin[1], &select[1], &flag[1], scene, cd_loop_uv_offset); + texface_from_original_index(origFace, origVertIndices[mloop[2].v], &uv[2], &pin[2], &select[2], &flag[2], scene, cd_loop_uv_offset); + texface_from_original_index(origFace, origVertIndices[mloop[3].v], &uv[3], &pin[3], &select[3], &flag[3], scene, cd_loop_uv_offset); - param_face_add(handle, key, 4, vkeys, co, uv, pin, select, NULL); + param_face_add(handle, key, 4, vkeys, co, uv, pin, select, NULL, flag); } /* these are calculated from original mesh too */ @@ -527,6 +551,8 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op) return false; } + int hparams = set_handle_params(implicit, fill_holes, true, true, false); + ms = MEM_callocN(sizeof(MinStretch), "MinStretch"); ms->scene = scene; ms->obedit = obedit; @@ -534,7 +560,7 @@ static bool minimize_stretch_init(bContext *C, wmOperator *op) ms->blend = RNA_float_get(op->ptr, "blend"); ms->iterations = RNA_int_get(op->ptr, "iterations"); ms->i = 0; - ms->handle = construct_param_handle(scene, obedit, em->bm, implicit, fill_holes, 1, 1); + ms->handle = construct_param_handle(scene, obedit, em->bm, hparams); ms->lasttime = PIL_check_seconds_timer(); param_stretch_begin(ms->handle); @@ -712,12 +738,48 @@ void UV_OT_minimize_stretch(wmOperatorType *ot) RNA_def_int(ot->srna, "iterations", 0, 0, INT_MAX, "Iterations", "Number of iterations to run, 0 is unlimited when run interactively", 0, 100); } +/* ******************** Select Shortest Path operator **************** */ +bool ED_uvedit_shortest_path_select(Scene *scene, Object *ob, BMesh *bm, bool topo_dist) +{ + ParamHandle *handle; + bool path_found = false; + int hparams = set_handle_params(true, false, false, true, true); + handle = construct_param_handle(scene, ob, bm, hparams); + param_shortest_path(handle, &path_found, topo_dist); + param_flush_sel(handle); + param_delete(handle); + return path_found; +} + +/* ******************** Select Overlapping UVs operator **************** */ +void ED_uvedit_overlapping_select(Scene *scene, Object *ob, BMesh *bm, const bool extend) +{ + ParamHandle *handle; + int hparams = set_handle_params(true, false, false, true, true); + handle = construct_param_handle(scene, ob, bm, hparams); + param_select_overlapping(handle, extend); + param_flush_sel(handle); + param_delete(handle); +} + +/* ******************** Scale To Bounds operator **************** */ +void ED_uvedit_scale_to_bounds(Scene *scene, Object *ob, BMesh *bm) +{ + ParamHandle *handle; + int hparams = set_handle_params(true, false, true, true, false); + handle = construct_param_handle(scene, ob, bm, hparams); + param_scale_bounds(handle); + param_flush(handle); + param_delete(handle); +} + /* ******************** Pack Islands operator **************** */ void ED_uvedit_pack_islands(Scene *scene, Object *ob, BMesh *bm, bool selected, bool correct_aspect, bool do_rotate) { ParamHandle *handle; - handle = construct_param_handle(scene, ob, bm, true, false, selected, correct_aspect); + int hparams = set_handle_params(true, false, selected, correct_aspect, false); + handle = construct_param_handle(scene, ob, bm, hparams); param_pack(handle, scene->toolsettings->uvcalc_margin, do_rotate); param_flush(handle); param_delete(handle); @@ -765,6 +827,344 @@ void UV_OT_pack_islands(wmOperatorType *ot) RNA_def_float_factor(ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f); } +/* ******************** Pack Islands operator 2.0 **************** */ + +typedef struct SimulatedAnnealing { + RNG *rng; + int seed; + float theta; + float f; + float r; + float temperature; + int rot_steps; +} SimulatedAnnealing; + +typedef struct PackIslands { + Scene *scene; + Object *obedit; + BMEditMesh *em; + ParamHandle *handle; + double lasttime; + int iter_global, iter_local, iter_max; + wmTimer *timer; + float wasted_area_last, margin; + bool use_concave; + SimulatedAnnealing *sa; +} PackIslands; + +static bool irregular_pack_islands_init(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + PackIslands *pi; + SimulatedAnnealing *simann; + unsigned int seed = 31415926; + float wasted_area; + bool average_scale = RNA_boolean_get(op->ptr, "average_islands_scale"); + + /* Keep for now, needed when making packing work with current selection */ + /*if (!uvedit_have_selection(scene, em, implicit)) { + return false; + }*/ + + int hparams = set_handle_params(true, false, false, false, true); + + pi = MEM_callocN(sizeof(PackIslands), "PackIslands"); + pi->scene = scene; + pi->obedit = obedit; + pi->em = em; + pi->iter_max = RNA_int_get(op->ptr, "iterations"); + pi->iter_global = 0; + pi->iter_local = 0; + pi->margin = RNA_float_get(op->ptr, "margin") / 2.0f; /* Only apply half the margin per chart */ + pi->use_concave = RNA_boolean_get(op->ptr, "concave"); + pi->handle = construct_param_handle(scene, obedit, em->bm, hparams); + pi->lasttime = PIL_check_seconds_timer(); + + simann = MEM_callocN(sizeof(SimulatedAnnealing), "SimulatedAnnealing"); + simann->rng = BLI_rng_new(seed); + simann->seed = seed; + simann->theta = 0.0f; + simann->f = 0.0f; + simann->r = 0.0f; + simann->temperature = 1.0f; + simann->rot_steps = RNA_int_get(op->ptr, "rotation_steps"); + pi->sa = simann; + + if (average_scale) + param_average(pi->handle); + + param_irregular_pack_begin(pi->handle, + &wasted_area, + pi->margin, + pi->sa->rot_steps, + pi->use_concave /* SA */); + + param_accept_placement_all(pi->handle); + + pi->wasted_area_last = wasted_area; + printf("wasted area currently: %f\n", wasted_area); + + op->customdata = pi; + + DAG_id_tag_update(pi->obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, pi->obedit->data); + + return true; +} + +static void irregular_pack_islands_iteration(bContext *C, wmOperator *op, bool interactive) +{ + PackIslands *pi = op->customdata; + ScrArea *sa = CTX_wm_area(C); + float wasted_area = 0.0f, dE, r1, r2; + float a = 0.95f; + /* ToDo Saphires: Find optimal parameter */ + float k = 0.5f; /* Stefan-Boltzman constant-like parameter */ + int local_iter_max = 50; + + pi->iter_global++; + + /* Cooling Schedule */ + if (pi->iter_local >= local_iter_max) { + pi->sa->temperature = pi->sa->temperature * a; + pi->iter_local = 0; + } + + + /* Find neighboring solution */ + /*ToDo Saphires: Pass SA parameters */ + param_irregular_pack_iter(pi->handle, + &wasted_area, + pi->iter_global, + pi->sa->rot_steps, + pi->margin /* SA */); + + /* delta Energy */ + dE = wasted_area - pi->wasted_area_last; + + printf("wasted area currently: %f, wasted area last: %f\n", wasted_area, pi->wasted_area_last); + + if (dE < 0) { + /* Current solution is new best solution, keep placement */ + param_accept_placement_all(pi->handle); + pi->wasted_area_last = wasted_area; + printf("dE < 0\n"); + } + else { + r1 = BLI_rng_get_float(pi->sa->rng); + + r2 = (float)exp(-dE/(k * pi->sa->temperature)); + + if (0 /*r1 < r2*/) { + /* Current solution is new best solution, keep placement */ + param_accept_placement_all(pi->handle); + pi->wasted_area_last = wasted_area; + printf("r1 < r2\n"); + } + else { + /* no better solution found, "frozen state solution" */ + printf("frozen state, revert\n"); + param_restore_placement(pi->handle, pi->margin); + pi->iter_local++; + } + } + + /* RNA_int_set(op->ptr, "iterations", pi->iter_global); */ /* ToDo SaphireS */ + + if (interactive /*&& (PIL_check_seconds_timer() - pi->lasttime > 0.5)*/) { + char str[UI_MAX_DRAW_STR]; + + param_flush(pi->handle); + + if (sa) { + BLI_snprintf(str, sizeof(str), + IFACE_("Pack Islands (irregular). Iteration: %i, Wasted UV Area (Best Solution): %f"), pi->iter_global, pi->wasted_area_last); + ED_area_headerprint(sa, str); + } + + pi->lasttime = PIL_check_seconds_timer(); + + DAG_id_tag_update(pi->obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, pi->obedit->data); + + printf("done iteration\n"); + } +} + +static void irregular_pack_islands_exit(bContext *C, wmOperator *op, bool cancel) +{ + PackIslands *pi = op->customdata; + ScrArea *sa = CTX_wm_area(C); + + if (sa) + ED_area_headerprint(sa, NULL); + if (pi->timer) + WM_event_remove_timer(CTX_wm_manager(C), CTX_wm_window(C), pi->timer); + + if (cancel) { + param_flush_restore(pi->handle); /* Restore original UVs */ + } + else { + //param_restore_packing_solution(pi->handle); /* Restore best solution*/ + param_flush(pi->handle); /* Keep new UVs */ + } + + param_irregular_pack_end(pi->handle); + param_delete(pi->handle); + + BLI_rng_free(pi->sa->rng); + MEM_freeN(pi->sa); + + DAG_id_tag_update(pi->obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, pi->obedit->data); + + MEM_freeN(pi); + op->customdata = NULL; +} + +static int irregular_pack_islands_exec(bContext *C, wmOperator *op) +{ + int i, iterations; + + if (!irregular_pack_islands_init(C, op)) + return OPERATOR_CANCELLED; + + iterations = RNA_int_get(op->ptr, "iterations"); + for (i = 0; i < iterations; i++) + irregular_pack_islands_iteration(C, op, false); + irregular_pack_islands_exit(C, op, false); + + return OPERATOR_FINISHED; +} + +static int irregular_pack_islands_invoke(bContext *C, wmOperator *op, const wmEvent *UNUSED(event)) +{ + PackIslands *pi; + + if (!irregular_pack_islands_init(C, op)) + return OPERATOR_CANCELLED; + + irregular_pack_islands_iteration(C, op, true); + + pi = op->customdata; + WM_event_add_modal_handler(C, op); + pi->timer = WM_event_add_timer(CTX_wm_manager(C), CTX_wm_window(C), TIMER, 0.01f); + + return OPERATOR_RUNNING_MODAL; +} + +static int irregular_pack_islands_modal(bContext *C, wmOperator *op, const wmEvent *event) +{ + PackIslands *pi = op->customdata; + + switch (event->type) { + case ESCKEY: + case RIGHTMOUSE: + irregular_pack_islands_exit(C, op, true); + return OPERATOR_CANCELLED; + case RETKEY: + case PADENTER: + case LEFTMOUSE: + irregular_pack_islands_exit(C, op, false); + return OPERATOR_FINISHED; + case TIMER: + if (pi->timer == event->customdata) { + double start = PIL_check_seconds_timer(); + + do { + irregular_pack_islands_iteration(C, op, true); + } while (PIL_check_seconds_timer() - start < 0.01); + } + break; + } + + if (pi->iter_max && pi->iter_global >= pi->iter_max) { + irregular_pack_islands_exit(C, op, false); + return OPERATOR_FINISHED; + } + + return OPERATOR_RUNNING_MODAL; +} + +static void irregular_pack_islands_cancel(bContext *C, wmOperator *op) +{ + irregular_pack_islands_exit(C, op, true); +} + +void UV_OT_irregular_pack_islands(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "Pack Islands (irregular)"; + ot->idname = "UV_OT_irregular_pack_islands"; + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO | OPTYPE_GRAB_CURSOR | OPTYPE_BLOCKING; + ot->description = "New and improved packing taking into account irregular uv shapes"; + + /* api callbacks */ + ot->exec = irregular_pack_islands_exec; + ot->invoke = irregular_pack_islands_invoke; + ot->modal = irregular_pack_islands_modal; + ot->cancel = irregular_pack_islands_cancel; + ot->poll = ED_operator_uvedit; + + /* properties */ + RNA_def_boolean(ot->srna, "concave", false, "Use concave boundaries", "Use concave boundaries (slower but better results)"); + RNA_def_float(ot->srna, "margin", 0.0f, 0.0f, 1.0f, "Margin", "Border Margin/Padding to apply per UV island", 0.0f, 1.0f); + RNA_def_int(ot->srna, "rotation_steps", 4, 0, 360, "Rotation Steps", "Allowed rotations to try during packing. (2=180°, 4=90°, etc.)", 0, 360); + RNA_def_int(ot->srna, "iterations", 0, 0, INT_MAX, "Iterations", "Number of iterations to run, 0 is unlimited when run interactively", 0, 10000); + RNA_def_boolean(ot->srna, "average_islands_scale", true, "Average Islands Scale", "Average Islands Scale before starting packing"); +} + +/* ******************** XXX (SaphireS): DEBUG-TEST operator **************** */ + +/* XXX (SaphireS): Remove */ +static void ED_uvedit_test_debug(Scene *scene, Object *ob, BMesh *bm, bool selected, bool correct_aspect) +{ + ParamHandle *handle; + int hparams = set_handle_params(true, false, selected, correct_aspect, true); + handle = construct_param_handle(scene, ob, bm, hparams); + + param_test(handle); + + param_flush(handle); + param_delete(handle); +} + +/* XXX (SaphireS): Remove */ +static int test_exec(bContext *C, wmOperator *op) +{ + Scene *scene = CTX_data_scene(C); + Object *obedit = CTX_data_edit_object(C); + BMEditMesh *em = BKE_editmesh_from_object(obedit); + + /*if (!uvedit_have_selection(scene, em, true)) { + return OPERATOR_CANCELLED; + }*/ + + ED_uvedit_test_debug(scene, obedit, em->bm, false, true); + + DAG_id_tag_update(obedit->data, 0); + WM_event_add_notifier(C, NC_GEOM | ND_DATA, obedit->data); + + return OPERATOR_FINISHED; +} + +/* XXX (SaphireS): Remove */ +void UV_OT_test(wmOperatorType *ot) +{ + /* identifiers */ + ot->name = "DEBUG - TEST"; + ot->idname = "UV_OT_test"; + ot->description = "Debug operator to test stuff"; + + ot->flag = OPTYPE_REGISTER | OPTYPE_UNDO; + + /* api callbacks */ + ot->exec = test_exec; + ot->poll = ED_operator_uvedit; +} + /* ******************** Average Islands Scale operator **************** */ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) @@ -774,12 +1174,14 @@ static int average_islands_scale_exec(bContext *C, wmOperator *UNUSED(op)) BMEditMesh *em = BKE_editmesh_from_object(obedit); ParamHandle *handle; bool implicit = true; + + int hparams = set_handle_params(implicit, false, true, true, false); if (!uvedit_have_selection(scene, em, implicit)) { return OPERATOR_CANCELLED; } - handle = construct_param_handle(scene, obedit, em->bm, implicit, 0, 1, 1); + handle = construct_param_handle(scene, obedit, em->bm, hparams); param_average(handle); param_flush(handle); param_delete(handle); @@ -817,6 +1219,8 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit) modifier_unwrap_state(obedit, scene, &use_subsurf); + int hparams = set_handle_params(false, fillholes, false, true, false); + if (!ED_uvedit_test(obedit)) { return; } @@ -824,7 +1228,7 @@ void ED_uvedit_live_unwrap_begin(Scene *scene, Object *obedit) if (use_subsurf) liveHandle = construct_param_handle_subsurfed(scene, obedit, em, fillholes, false, true); else - liveHandle = construct_param_handle(scene, obedit, em->bm, false, fillholes, false, true); + liveHandle = construct_param_handle(scene, obedit, em->bm, hparams); param_lscm_begin(liveHandle, PARAM_TRUE, abf); } @@ -1146,21 +1550,27 @@ void ED_unwrap_lscm(Scene *scene, Object *obedit, const short sel) const bool fill_holes = (scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES) != 0; const bool correct_aspect = (scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT) == 0; + const bool pack_islands = (scene->toolsettings->uvcalc_flag & UVCALC_PACKISLANDS) != 0; bool use_subsurf; + int hparams = set_handle_params(false, fill_holes, sel, correct_aspect, false); + modifier_unwrap_state(obedit, scene, &use_subsurf); if (use_subsurf) handle = construct_param_handle_subsurfed(scene, obedit, em, fill_holes, sel, correct_aspect); else - handle = construct_param_handle(scene, obedit, em->bm, false, fill_holes, sel, correct_aspect); + handle = construct_param_handle(scene, obedit, em->bm, hparams); param_lscm_begin(handle, PARAM_FALSE, scene->toolsettings->unwrapper == 0); param_lscm_solve(handle); param_lscm_end(handle); param_average(handle); - param_pack(handle, scene->toolsettings->uvcalc_margin, false); + if (pack_islands) + param_pack(handle, scene->toolsettings->uvcalc_margin, false); + else + param_scale_bounds(handle); param_flush(handle); @@ -1175,6 +1585,7 @@ static int unwrap_exec(bContext *C, wmOperator *op) int method = RNA_enum_get(op->ptr, "method"); const bool fill_holes = RNA_boolean_get(op->ptr, "fill_holes"); const bool correct_aspect = RNA_boolean_get(op->ptr, "correct_aspect"); + const bool pack_islands = RNA_boolean_get(op->ptr, "pack_islands"); const bool use_subsurf = RNA_boolean_get(op->ptr, "use_subsurf_data"); bool use_subsurf_final; float obsize[3]; @@ -1189,6 +1600,9 @@ static int unwrap_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* Reveal hidden UVs since they're taken into account*/ + ED_uvedit_reveal(em); + mat4_to_size(obsize, obedit->obmat); if (!(fabsf(obsize[0] - obsize[1]) < 1e-4f && fabsf(obsize[1] - obsize[2]) < 1e-4f)) BKE_report(op->reports, RPT_INFO, @@ -1215,6 +1629,9 @@ static int unwrap_exec(bContext *C, wmOperator *op) if (correct_aspect) scene->toolsettings->uvcalc_flag &= ~UVCALC_NO_ASPECT_CORRECT; else scene->toolsettings->uvcalc_flag |= UVCALC_NO_ASPECT_CORRECT; + if (pack_islands) scene->toolsettings->uvcalc_flag |= UVCALC_PACKISLANDS; + else scene->toolsettings->uvcalc_flag &= ~UVCALC_PACKISLANDS; + if (use_subsurf) scene->toolsettings->uvcalc_flag |= UVCALC_USESUBSURF; else scene->toolsettings->uvcalc_flag &= ~UVCALC_USESUBSURF; @@ -1258,6 +1675,8 @@ void UV_OT_unwrap(wmOperatorType *ot) "Virtual fill holes in mesh before unwrapping, to better avoid overlaps and preserve symmetry"); RNA_def_boolean(ot->srna, "correct_aspect", 1, "Correct Aspect", "Map UVs taking image aspect ratio into account"); + RNA_def_boolean(ot->srna, "pack_islands", 1, "Pack Islands", + "Pack UV islands after unwrapping"); RNA_def_boolean(ot->srna, "use_subsurf_data", 0, "Use Subsurf Modifier", "Map UVs taking vertex position after Subdivision Surface modifier has been applied"); RNA_def_float_factor(ot->srna, "margin", 0.001f, 0.0f, 1.0f, "Margin", "Space between islands", 0.0f, 1.0f); @@ -1303,6 +1722,9 @@ static int uv_from_view_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* Reveal hidden UVs since they're taken into account*/ + ED_uvedit_reveal(em); + cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); if (RNA_boolean_get(op->ptr, "orthographic")) { @@ -1405,6 +1827,9 @@ static int reset_exec(bContext *C, wmOperator *UNUSED(op)) return OPERATOR_CANCELLED; } + /* Reveal hidden UVs since they're taken into account*/ + ED_uvedit_reveal(me->edit_btmesh); + ED_mesh_uv_loop_reset(C, me); DAG_id_tag_update(obedit->data, 0); @@ -1491,6 +1916,9 @@ static int sphere_project_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* Reveal hidden UVs since they're taken into account*/ + ED_uvedit_reveal(em); + cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); uv_map_transform(C, op, center, rotmat); @@ -1570,6 +1998,9 @@ static int cylinder_project_exec(bContext *C, wmOperator *op) return OPERATOR_CANCELLED; } + /* Reveal hidden UVs since they're taken into account*/ + ED_uvedit_reveal(em); + cd_loop_uv_offset = CustomData_get_offset(&em->bm->ldata, CD_MLOOPUV); uv_map_transform(C, op, center, rotmat); @@ -1676,6 +2107,9 @@ static int cube_project_exec(bContext *C, wmOperator *op) if (!ED_uvedit_ensure_uvs(C, scene, obedit)) { return OPERATOR_CANCELLED; } + + /* Reveal hidden UVs since they're taken into account*/ + ED_uvedit_reveal(em); ED_uvedit_unwrap_cube_project(obedit, em->bm, cube_size, true); uv_map_clip_correct(scene, obedit, em, op); diff --git a/source/blender/makesdna/DNA_meshdata_types.h b/source/blender/makesdna/DNA_meshdata_types.h index 3676066a399..3e878fab66b 100644 --- a/source/blender/makesdna/DNA_meshdata_types.h +++ b/source/blender/makesdna/DNA_meshdata_types.h @@ -213,6 +213,7 @@ enum { MLOOPUV_EDGESEL = (1 << 0), MLOOPUV_VERTSEL = (1 << 1), MLOOPUV_PINNED = (1 << 2), + MLOOPUV_HIDDEN = (1 << 3) }; /** diff --git a/source/blender/makesdna/DNA_scene_types.h b/source/blender/makesdna/DNA_scene_types.h index 3201b75ee1e..76cac158b5f 100644 --- a/source/blender/makesdna/DNA_scene_types.h +++ b/source/blender/makesdna/DNA_scene_types.h @@ -2152,6 +2152,7 @@ typedef enum ImagePaintMode { #define UVCALC_NO_ASPECT_CORRECT 2 /* would call this UVCALC_ASPECT_CORRECT, except it should be default with old file */ #define UVCALC_TRANSFORM_CORRECT 4 /* adjust UV's while transforming to avoid distortion */ #define UVCALC_USESUBSURF 8 /* Use mesh data after subsurf to compute UVs*/ +#define UVCALC_PACKISLANDS 16 /* Pack Islands after unwrapping*/ /* toolsettings->uv_flag */ #define UV_SYNC_SELECTION 1 |