diff options
31 files changed, 12897 insertions, 67 deletions
diff --git a/source/blender/blenkernel/BKE_context.h b/source/blender/blenkernel/BKE_context.h index ec2024089bd..d6efc1288ec 100644 --- a/source/blender/blenkernel/BKE_context.h +++ b/source/blender/blenkernel/BKE_context.h @@ -47,6 +47,8 @@ struct ScrArea; struct SpaceLink; struct StructRNA; struct ToolSettings; +struct Image; +struct ImBuf; struct wmWindow; struct wmWindowManager; @@ -149,6 +151,9 @@ struct Object *CTX_data_active_object(const bContext *C); struct Base *CTX_data_active_base(const bContext *C); struct Object *CTX_data_edit_object(const bContext *C); +struct Image *CTX_data_edit_image(const bContext *C); +struct ImBuf *CTX_data_edit_image_buffer(const bContext *C); + int CTX_data_selected_nodes(const bContext *C, ListBase *list); /* Data Evaluation Context */ diff --git a/source/blender/blenkernel/intern/context.c b/source/blender/blenkernel/intern/context.c index d5e277a8d1f..ef8b922c0b3 100644 --- a/source/blender/blenkernel/intern/context.c +++ b/source/blender/blenkernel/intern/context.c @@ -392,6 +392,16 @@ struct Object *CTX_data_edit_object(const bContext *C) return ctx_data_pointer_get(C, CTX_data_edit_object); } +struct Image *CTX_data_edit_image(const bContext *C) +{ + return ctx_data_pointer_get(C, CTX_data_edit_image); +} + +struct ImBuf *CTX_data_edit_image_buffer(const bContext *C) +{ + return ctx_data_pointer_get(C, CTX_data_edit_image_buffer); +} + /* data evaluation */ float CTX_eval_frame(const bContext *C) diff --git a/source/blender/editors/Makefile b/source/blender/editors/Makefile index b7b2096fea9..af446811d6f 100644 --- a/source/blender/editors/Makefile +++ b/source/blender/editors/Makefile @@ -29,6 +29,6 @@ # Bounces make to subdirectories. SOURCEDIR = source/blender/editors -DIRS = armature mesh animation object sculpt datafiles transform screen curve gpencil physics preview space_outliner space_time space_view3d interface util space_api space_ipo space_image space_node space_buttons space_info space_file space_sound space_action space_nla space_script space_text space_sequencer +DIRS = armature mesh animation object sculpt datafiles transform screen curve gpencil physics preview uvedit space_outliner space_time space_view3d interface util space_api space_ipo space_image space_node space_buttons space_info space_file space_sound space_action space_nla space_script space_text space_sequencer include nan_subdirs.mk diff --git a/source/blender/editors/SConscript b/source/blender/editors/SConscript index 9c77d44945a..d6b7619ba9f 100644 --- a/source/blender/editors/SConscript +++ b/source/blender/editors/SConscript @@ -31,4 +31,5 @@ SConscript(['datafiles/SConscript', 'space_sequencer/SConscript', 'transform/SConscript', 'screen/SConscript', - 'sculpt/SConscript']) + 'sculpt/SConscript', + 'uvedit/SConscript']) diff --git a/source/blender/editors/include/ED_armature.h b/source/blender/editors/include/ED_armature.h index c731c9ed740..c3c757b1a30 100644 --- a/source/blender/editors/include/ED_armature.h +++ b/source/blender/editors/include/ED_armature.h @@ -33,7 +33,9 @@ struct Object; struct Base; struct Bone; struct bArmature; +struct bPoseChannel; struct ListBase; +struct View3D; typedef struct EditBone { diff --git a/source/blender/editors/include/ED_mesh.h b/source/blender/editors/include/ED_mesh.h index e1b7e283565..5a72ab201f4 100644 --- a/source/blender/editors/include/ED_mesh.h +++ b/source/blender/editors/include/ED_mesh.h @@ -42,6 +42,11 @@ struct ViewContext; struct bDeformGroup; struct MDeformWeight; struct MDeformVert; +struct Scene; +struct MCol; +struct UvVertMap; +struct UvMapVert; +struct CustomData; // edge and face flag both #define EM_FGON 2 @@ -79,9 +84,9 @@ void ED_keymap_mesh(struct wmWindowManager *wm); void ED_spacetypes_init(void); void ED_keymap_mesh(struct wmWindowManager *wm); -void make_editMesh(Scene *scene, Object *ob); -void load_editMesh(Scene *scene, Object *ob); -void remake_editMesh(Scene *scene, Object *ob); +void make_editMesh(struct Scene *scene, Object *ob); +void load_editMesh(struct Scene *scene, Object *ob); +void remake_editMesh(struct Scene *scene, Object *ob); void free_editMesh(struct EditMesh *em); void recalc_editnormals(struct EditMesh *em); @@ -100,8 +105,12 @@ void undo_push_mesh(struct bContext *C, char *name); /* editmesh_lib.c */ struct EditFace *EM_get_actFace(struct EditMesh *em, int sloppy); +void EM_set_actFace(struct EditMesh *em, struct EditFace *efa); +float EM_face_area(struct EditFace *efa); +void EM_add_data_layer(struct EditMesh *em, struct CustomData *data, int type); void EM_select_edge(struct EditEdge *eed, int sel); +void EM_select_face(struct EditFace *efa, int sel); void EM_select_face_fgon(struct EditMesh *em, struct EditFace *efa, int val); void EM_selectmode_flush(struct EditMesh *em); void EM_deselect_flush(struct EditMesh *em); @@ -114,6 +123,9 @@ int EM_get_actSelection(struct EditMesh *em, struct EditSelection *ese); void EM_editselection_normal(float *normal, struct EditSelection *ese); void EM_editselection_plane(float *plane, struct EditSelection *ese); +struct UvVertMap *EM_make_uv_vert_map(struct EditMesh *em, int selected, int do_face_idx_array, float *limit); +struct UvMapVert *EM_get_uv_map_vert(struct UvVertMap *vmap, unsigned int v); +void EM_free_uv_vert_map(struct UvVertMap *vmap); /* editmesh_mods.c */ extern unsigned int em_vertoffs, em_solidoffs, em_wireoffs; @@ -125,6 +137,9 @@ void EM_free_backbuf(void); int EM_init_backbuf_border(struct ViewContext *vc, short xmin, short ymin, short xmax, short ymax); int EM_init_backbuf_circle(struct ViewContext *vc, short xs, short ys, short rads); +/* editface.c */ +struct MTFace *EM_get_active_mtface(struct EditMesh *em, struct EditFace **act_efa, struct MCol **mcol, int sloppy); + /* editdeform.c XXX rename functions? */ #define WEIGHT_REPLACE 1 diff --git a/source/blender/editors/include/ED_object.h b/source/blender/editors/include/ED_object.h index 7e786cf11db..ad7d54d7062 100644 --- a/source/blender/editors/include/ED_object.h +++ b/source/blender/editors/include/ED_object.h @@ -35,6 +35,10 @@ struct bContext; struct Base; struct View3D; struct bConstraint; +struct KeyBlock; +struct Lattice; +struct Mesh; +struct Curve; void ED_operatortypes_object(void); void ED_keymap_object(struct wmWindowManager *wm); diff --git a/source/blender/editors/include/ED_screen.h b/source/blender/editors/include/ED_screen.h index a9ad6c545cb..7c38d167b0b 100644 --- a/source/blender/editors/include/ED_screen.h +++ b/source/blender/editors/include/ED_screen.h @@ -107,6 +107,7 @@ int ED_operator_sequencer_active(struct bContext *C); int ED_operator_object_active(struct bContext *C); int ED_operator_editmesh(struct bContext *C); int ED_operator_editcurve(struct bContext *C); +int ED_operator_uvedit(struct bContext *C); /* default keymaps, bitflags */ #define ED_KEYMAP_UI 1 diff --git a/source/blender/editors/include/ED_uvedit.h b/source/blender/editors/include/ED_uvedit.h new file mode 100644 index 00000000000..f027dbc8915 --- /dev/null +++ b/source/blender/editors/include/ED_uvedit.h @@ -0,0 +1,45 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef ED_UVEDIT_H +#define ED_UVEDIT_H + +struct Scene; +struct Object; +struct Image; +struct wmWindowManager; + +/* uvedit_ops.c */ +void ED_operatortypes_uvedit(void); +void ED_keymap_uvedit(struct wmWindowManager *wm); + +void ED_uvedit_assign_image(struct Scene *scene, struct Object *obedit, struct Image *ima, struct Image *previma); +void ED_uvedit_set_tile(struct Scene *scene, struct Object *obedit, struct Image *ima, int curtile, int dotile); +int ED_uvedit_minmax(struct Scene *scene, struct Image *ima, struct Object *obedit, float *min, float *max); + +#endif /* ED_UVEDIT_H */ + diff --git a/source/blender/editors/mesh/editface.c b/source/blender/editors/mesh/editface.c index 49c8fe648d7..f5c14a34140 100644 --- a/source/blender/editors/mesh/editface.c +++ b/source/blender/editors/mesh/editface.c @@ -598,7 +598,7 @@ static void calculate_uv_map(Scene *scene, ARegion *ar, View3D *v3d, EditMesh *e /* last_sel, use em->act_face otherwise get the last selected face in the editselections * at the moment, last_sel is mainly useful for gaking sure the space image dosnt flicker */ -MTFace *get_active_mtface(EditMesh *em, EditFace **act_efa, MCol **mcol, int sloppy) +MTFace *EM_get_active_mtface(EditMesh *em, EditFace **act_efa, MCol **mcol, int sloppy) { EditFace *efa = NULL; diff --git a/source/blender/editors/mesh/editmesh_lib.c b/source/blender/editors/mesh/editmesh_lib.c index 93de720d4f6..0bd3295d1d7 100644 --- a/source/blender/editors/mesh/editmesh_lib.c +++ b/source/blender/editors/mesh/editmesh_lib.c @@ -2133,7 +2133,7 @@ void EM_fgon_flags(EditMesh *em) * if do_face_idx_array is 0 it means we need to run it as well as freeing * */ -UvVertMap *make_uv_vert_map_EM(EditMesh *em, int selected, int do_face_idx_array, float *limit) +UvVertMap *EM_make_uv_vert_map(EditMesh *em, int selected, int do_face_idx_array, float *limit) { EditVert *ev; EditFace *efa; @@ -2251,12 +2251,12 @@ UvVertMap *make_uv_vert_map_EM(EditMesh *em, int selected, int do_face_idx_array return vmap; } -UvMapVert *get_uv_map_vert_EM(UvVertMap *vmap, unsigned int v) +UvMapVert *EM_get_uv_map_vert(UvVertMap *vmap, unsigned int v) { return vmap->vert[v]; } -void free_uv_vert_map_EM(UvVertMap *vmap) +void EM_free_uv_vert_map(UvVertMap *vmap) { if (vmap) { if (vmap->vert) MEM_freeN(vmap->vert); diff --git a/source/blender/editors/mesh/mesh_intern.h b/source/blender/editors/mesh/mesh_intern.h index 6d5bd16c23d..95a6bdbfe29 100644 --- a/source/blender/editors/mesh/mesh_intern.h +++ b/source/blender/editors/mesh/mesh_intern.h @@ -112,19 +112,14 @@ extern int faceselectedOR(EditFace *efa, int flag); extern int faceselectedAND(EditFace *efa, int flag); void EM_remove_selection(EditMesh *em, void *data, int type); -void EM_set_actFace(EditMesh *em, EditFace *efa); -void EM_select_face(EditFace *efa, int sel); void EM_clear_flag_all(EditMesh *em, int flag); void EM_set_flag_all(EditMesh *em, int flag); -void EM_add_data_layer(EditMesh *em, CustomData *data, int type); - void EM_data_interp_from_verts(EditMesh *em, EditVert *v1, EditVert *v2, EditVert *eve, float fac); void EM_data_interp_from_faces(EditMesh *em, EditFace *efa1, EditFace *efa2, EditFace *efan, int i1, int i2, int i3, int i4); int EM_nvertices_selected(EditMesh *em); int EM_nfaces_selected(EditMesh *em); -float EM_face_area(EditFace *efa); float EM_face_perimeter(EditFace *efa); void EM_store_selection(EditMesh *em, void *data, int type); diff --git a/source/blender/editors/screen/screen_ops.c b/source/blender/editors/screen/screen_ops.c index 761ba384c82..3e6604943c4 100644 --- a/source/blender/editors/screen/screen_ops.c +++ b/source/blender/editors/screen/screen_ops.c @@ -26,10 +26,12 @@ #include "MEM_guardedalloc.h" -#include "BLI_blenlib.h" #include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" #include "BKE_context.h" +#include "BKE_customdata.h" #include "BKE_library.h" #include "BKE_main.h" #include "BKE_screen.h" @@ -158,6 +160,20 @@ int ED_operator_editmesh(bContext *C) return 0; } +int ED_operator_uvedit(bContext *C) +{ + Object *obedit= CTX_data_edit_object(C); + EditMesh *em= NULL; + + if(obedit && obedit->type==OB_MESH) + em= ((Mesh *)obedit->data)->edit_mesh; + + if(em && (em->faces.first) && (CustomData_has_layer(&em->fdata, CD_MTFACE))) + return 1; + + return 0; +} + int ED_operator_editcurve(bContext *C) { Object *obedit= CTX_data_edit_object(C); diff --git a/source/blender/editors/space_api/spacetypes.c b/source/blender/editors/space_api/spacetypes.c index e54dc3817da..89693b6cc2c 100644 --- a/source/blender/editors/space_api/spacetypes.c +++ b/source/blender/editors/space_api/spacetypes.c @@ -42,10 +42,10 @@ #include "ED_anim_api.h" #include "ED_mesh.h" #include "ED_object.h" -#include "ED_space_api.h" -#include "ED_screen.h" #include "ED_sculpt.h" - +#include "ED_screen.h" +#include "ED_space_api.h" +#include "ED_uvedit.h" ARegionType *ED_regiontype_from_id(SpaceType *st, int regionid) { @@ -91,6 +91,7 @@ void ED_spacetypes_init(void) ED_operatortypes_object(); ED_operatortypes_mesh(); ED_operatortypes_sculpt(); + ED_operatortypes_uvedit(); ui_view2d_operatortypes(); spacetypes = BKE_spacetypes_list(); @@ -112,6 +113,7 @@ void ED_spacetypes_keymap(wmWindowManager *wm) ED_keymap_animchannels(wm); ED_keymap_object(wm); ED_keymap_mesh(wm); + ED_keymap_uvedit(wm); UI_view2d_keymap(wm); spacetypes = BKE_spacetypes_list(); diff --git a/source/blender/editors/space_image/Makefile b/source/blender/editors/space_image/Makefile index 8110bd1c2a3..44d841a0606 100644 --- a/source/blender/editors/space_image/Makefile +++ b/source/blender/editors/space_image/Makefile @@ -40,14 +40,16 @@ CPPFLAGS += -I$(OPENGL_HEADERS) # not very neat.... CPPFLAGS += -I../../windowmanager -CPPFLAGS += -I../../blenloader CPPFLAGS += -I../../blenkernel CPPFLAGS += -I../../blenlib CPPFLAGS += -I../../makesdna +CPPFLAGS += -I../../makesrna CPPFLAGS += -I../../imbuf CPPFLAGS += -I../../python +CPPFLAGS += -I../../render/extern/include CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include # own include CPPFLAGS += -I../include + diff --git a/source/blender/editors/space_image/SConscript b/source/blender/editors/space_image/SConscript index bc449effb5b..a5aee00facc 100644 --- a/source/blender/editors/space_image/SConscript +++ b/source/blender/editors/space_image/SConscript @@ -5,5 +5,6 @@ sources = env.Glob('*.c') incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' +incs += ' ../../render/extern/include ../../makesrna' env.BlenderLib ( 'bf_editors_space_image', sources, Split(incs), [], libtype=['core'], priority=[45] ) diff --git a/source/blender/editors/space_image/image_draw.c b/source/blender/editors/space_image/image_draw.c new file mode 100644 index 00000000000..99869bad348 --- /dev/null +++ b/source/blender/editors/space_image/image_draw.c @@ -0,0 +1,722 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <math.h> +#include <stdlib.h> +#include <string.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_brush_types.h" +#include "DNA_camera_types.h" +#include "DNA_image_types.h" +#include "DNA_object_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" + +#include "PIL_time.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "BKE_colortools.h" +#include "BKE_global.h" +#include "BKE_image.h" +#include "BKE_utildefines.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "ED_screen.h" + +#include "UI_resources.h" +#include "UI_text.h" +#include "UI_view2d.h" + +#include "WM_api.h" + +#include "image_intern.h" + +#define HEADER_HEIGHT 18 + +#if 0 +static int image_preview_active(SpaceImage *sima, Scene *scene, float *xim, float *yim) +{ + /* only when compositor shows, and image handler set */ + if(sima->image && sima->image->type==IMA_TYPE_COMPOSITE) { + /* XXX panels .. */ +#if 0 + short a; + + for(a=0; a<SPACE_MAXHANDLER; a+=2) { + if(sima->blockhandler[a] == IMAGE_HANDLER_PREVIEW) { + if(xim) *xim= (scene->r.size*scene->r.xsch)/100; + if(yim) *yim= (scene->r.size*scene->r.ysch)/100; + return 1; + } + } +#endif + } + return 0; +} +#endif + +/* are there curves? curves visible? and curves do something? */ +static int image_curves_active(SpaceImage *sima) +{ + if(sima->cumap) { + if(curvemapping_RGBA_does_something(sima->cumap)) { + /* XXX panels .. */ +#if 0 + short a; + for(a=0; a<SPACE_MAXHANDLER; a+=2) { + if(sima->blockhandler[a] == IMAGE_HANDLER_CURVES) + return 1; + } +#endif + } + } + + return 0; +} + +static void image_verify_buffer_float(SpaceImage *sima, ImBuf *ibuf) +{ + /* detect if we need to redo the curve map. + ibuf->rect is zero for compositor and render results after change + convert to 32 bits always... drawing float rects isnt supported well (atis) + + NOTE: if float buffer changes, we have to manually remove the rect + */ + + if(ibuf->rect_float) { + if(ibuf->rect==NULL) { + if(image_curves_active(sima)) + curvemapping_do_ibuf(sima->cumap, ibuf); + else + IMB_rect_from_float(ibuf); + } + } +} + +static void sima_draw_render_info(SpaceImage *sima, ARegion *ar) +{ + rcti rect; + float colf[3]; + int showspare= 0; // XXX BIF_show_render_spare(); + char *str= "render text"; // XXX BIF_render_text(); + + if(str==NULL) + return; + + rect= ar->winrct; + rect.ymin= rect.ymax - HEADER_HEIGHT; + + glaDefine2DArea(&rect); + + /* clear header rect */ + UI_GetThemeColor3fv(TH_BACK, colf); + glClearColor(colf[0]+0.1f, colf[1]+0.1f, colf[2]+0.1f, 1.0); + glClear(GL_COLOR_BUFFER_BIT); + + UI_ThemeColor(TH_TEXT_HI); + glRasterPos2i(12, 5); + UI_RasterPos(12, 5); + + if(showspare) { + UI_DrawString(G.fonts, "(Previous)", 0); + glRasterPos2i(72, 5); + UI_RasterPos(72, 5); + } + + UI_DrawString(G.fonts, str, 0); +} + +/*static void sima_draw_image_info(ARegion *ar, int channels, int x, int y, char *cp, float *fp, int *zp, float *zpf) +{ + char str[256]; + int ofs; + + ofs= sprintf(str, "X: %d Y: %d ", x, y); + if(cp) + ofs+= sprintf(str+ofs, "| R: %d G: %d B: %d A: %d ", cp[0], cp[1], cp[2], cp[3]); + + if(fp) { + if(channels==4) + ofs+= sprintf(str+ofs, "| R: %.3f G: %.3f B: %.3f A: %.3f ", fp[0], fp[1], fp[2], fp[3]); + else if(channels==1) + ofs+= sprintf(str+ofs, "| Val: %.3f ", fp[0]); + else if(channels==3) + ofs+= sprintf(str+ofs, "| R: %.3f G: %.3f B: %.3f ", fp[0], fp[1], fp[2]); + } + + if(zp) + ofs+= sprintf(str+ofs, "| Z: %.4f ", 0.5+0.5*(((float)*zp)/(float)0x7fffffff)); + if(zpf) + ofs+= sprintf(str+ofs, "| Z: %.3f ", *zpf); + + glBlendFunc(GL_SRC_ALPHA,GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + glColor4f(.0,.0,.0,.25); + glRectf(0.0, 0.0, ar->winrct.xmax - ar->winrct.xmin + 1, 30.0); + glDisable(GL_BLEND); + + glColor3ub(255, 255, 255); + glRasterPos2i(10, 10); + UI_RasterPos(10, 10); + + UI_DrawString(G.fonts, str, 0); +}*/ + +/* image drawing */ + +static void draw_image_grid(ARegion *ar, float zoomx, float zoomy) +{ + float gridsize, gridstep= 1.0f/32.0f; + float fac, blendfac; + int x1, y1, x2, y2; + + /* the image is located inside (0,0),(1, 1) as set by view2d */ + UI_ThemeColorShade(TH_BACK, 20); + + UI_view2d_to_region_no_clip(&ar->v2d, 0.0f, 0.0f, &x1, &y1); + UI_view2d_to_region_no_clip(&ar->v2d, 1.0f, 1.0f, &x2, &y2); + glRectf(x1, y1, x2, y2); + + /* gridsize adapted to zoom level */ + gridsize= 0.5f*(zoomx+zoomy); + if(gridsize<=0.0f) return; + + if(gridsize<1.0f) { + while(gridsize<1.0f) { + gridsize*= 4.0; + gridstep*= 4.0; + } + } + else { + while(gridsize>=4.0f) { + gridsize/= 4.0; + gridstep/= 4.0; + } + } + + /* the fine resolution level */ + blendfac= 0.25*gridsize - floor(0.25*gridsize); + CLAMP(blendfac, 0.0, 1.0); + UI_ThemeColorShade(TH_BACK, (int)(20.0*(1.0-blendfac))); + + fac= 0.0f; + glBegin(GL_LINES); + while(fac<1.0f) { + glVertex2f(x1, y1*(1.0f-fac) + y2*fac); + glVertex2f(x2, y1*(1.0f-fac) + y2*fac); + glVertex2f(x1*(1.0f-fac) + x2*fac, y1); + glVertex2f(x1*(1.0f-fac) + x2*fac, y2); + fac+= gridstep; + } + + /* the large resolution level */ + UI_ThemeColor(TH_BACK); + + fac= 0.0f; + while(fac<1.0f) { + glVertex2f(x1, y1*(1.0f-fac) + y2*fac); + glVertex2f(x2, y1*(1.0f-fac) + y2*fac); + glVertex2f(x1*(1.0f-fac) + x2*fac, y1); + glVertex2f(x1*(1.0f-fac) + x2*fac, y2); + fac+= 4.0f*gridstep; + } + glEnd(); +} + +static void sima_draw_alpha_backdrop(float x1, float y1, float xsize, float ysize, float zoomx, float zoomy) +{ + GLubyte checker_stipple[32*32/8] = + { + 255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0, \ + 255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0, \ + 255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0, \ + 255,255,0,0,255,255,0,0,255,255,0,0,255,255,0,0, \ + 0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255, \ + 0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255, \ + 0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255, \ + 0,0,255,255,0,0,255,255,0,0,255,255,0,0,255,255, \ + }; + + glColor3ub(100, 100, 100); + glRectf(x1, y1, x1 + zoomx*xsize, y1 + zoomy*ysize); + glColor3ub(160, 160, 160); + + glEnable(GL_POLYGON_STIPPLE); + glPolygonStipple(checker_stipple); + glRectf(x1, y1, x1 + zoomx*xsize, y1 + zoomy*ysize); + glDisable(GL_POLYGON_STIPPLE); +} + +static void sima_draw_alpha_pixels(float x1, float y1, int rectx, int recty, unsigned int *recti) +{ + + /* swap bytes, so alpha is most significant one, then just draw it as luminance int */ + if(ENDIAN_ORDER == B_ENDIAN) + glPixelStorei(GL_UNPACK_SWAP_BYTES, 1); + + glaDrawPixelsSafe(x1, y1, rectx, recty, rectx, GL_LUMINANCE, GL_UNSIGNED_INT, recti); + glPixelStorei(GL_UNPACK_SWAP_BYTES, 0); +} + +static void sima_draw_alpha_pixelsf(float x1, float y1, int rectx, int recty, float *rectf) +{ + float *trectf= MEM_mallocN(rectx*recty*4, "temp"); + int a, b; + + for(a= rectx*recty -1, b= 4*a+3; a>=0; a--, b-=4) + trectf[a]= rectf[b]; + + glaDrawPixelsSafe(x1, y1, rectx, recty, rectx, GL_LUMINANCE, GL_FLOAT, trectf); + MEM_freeN(trectf); + /* ogl trick below is slower... (on ATI 9600) */ +// glColorMask(1, 0, 0, 0); +// glaDrawPixelsSafe(x1, y1, rectx, recty, rectx, GL_RGBA, GL_FLOAT, rectf+3); +// glColorMask(0, 1, 0, 0); +// glaDrawPixelsSafe(x1, y1, rectx, recty, rectx, GL_RGBA, GL_FLOAT, rectf+2); +// glColorMask(0, 0, 1, 0); +// glaDrawPixelsSafe(x1, y1, rectx, recty, rectx, GL_RGBA, GL_FLOAT, rectf+1); +// glColorMask(1, 1, 1, 1); +} + +static void sima_draw_zbuf_pixels(float x1, float y1, int rectx, int recty, int *recti) +{ + /* zbuffer values are signed, so we need to shift color range */ + glPixelTransferf(GL_RED_SCALE, 0.5f); + glPixelTransferf(GL_GREEN_SCALE, 0.5f); + glPixelTransferf(GL_BLUE_SCALE, 0.5f); + glPixelTransferf(GL_RED_BIAS, 0.5f); + glPixelTransferf(GL_GREEN_BIAS, 0.5f); + glPixelTransferf(GL_BLUE_BIAS, 0.5f); + + glaDrawPixelsSafe(x1, y1, rectx, recty, rectx, GL_LUMINANCE, GL_INT, recti); + + glPixelTransferf(GL_RED_SCALE, 1.0f); + glPixelTransferf(GL_GREEN_SCALE, 1.0f); + glPixelTransferf(GL_BLUE_SCALE, 1.0f); + glPixelTransferf(GL_RED_BIAS, 0.0f); + glPixelTransferf(GL_GREEN_BIAS, 0.0f); + glPixelTransferf(GL_BLUE_BIAS, 0.0f); +} + +static void sima_draw_zbuffloat_pixels(Scene *scene, float x1, float y1, int rectx, int recty, float *rect_float) +{ + float bias, scale, *rectf, clipend; + int a; + + if(scene->camera && scene->camera->type==OB_CAMERA) { + bias= ((Camera *)scene->camera->data)->clipsta; + clipend= ((Camera *)scene->camera->data)->clipend; + scale= 1.0f/(clipend-bias); + } + else { + bias= 0.1f; + scale= 0.01f; + clipend= 100.0f; + } + + rectf= MEM_mallocN(rectx*recty*4, "temp"); + for(a= rectx*recty -1; a>=0; a--) { + if(rect_float[a]>clipend) + rectf[a]= 0.0f; + else if(rect_float[a]<bias) + rectf[a]= 1.0f; + else { + rectf[a]= 1.0f - (rect_float[a]-bias)*scale; + rectf[a]*= rectf[a]; + } + } + glaDrawPixelsSafe(x1, y1, rectx, recty, rectx, GL_LUMINANCE, GL_FLOAT, rectf); + + MEM_freeN(rectf); +} + +static void draw_image_buffer(SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf *ibuf, float fx, float fy, float zoomx, float zoomy) +{ + int x, y; + + /* set zoom */ + glPixelZoom(zoomx, zoomy); + + /* find window pixel coordinates of origin */ + UI_view2d_to_region_no_clip(&ar->v2d, fx, fy, &x, &y); + + /* this part is generic image display */ + if(sima->flag & SI_SHOW_ALPHA) { + if(ibuf->rect) + sima_draw_alpha_pixels(x, y, ibuf->x, ibuf->y, ibuf->rect); + else if(ibuf->rect_float && ibuf->channels==4) + sima_draw_alpha_pixelsf(x, y, ibuf->x, ibuf->y, ibuf->rect_float); + } + else if(sima->flag & SI_SHOW_ZBUF && (ibuf->zbuf || ibuf->zbuf_float || (ibuf->channels==1))) { + if(ibuf->zbuf) + sima_draw_zbuf_pixels(x, y, ibuf->x, ibuf->y, ibuf->zbuf); + else if(ibuf->zbuf_float) + sima_draw_zbuffloat_pixels(scene, x, y, ibuf->x, ibuf->y, ibuf->zbuf_float); + else if(ibuf->channels==1) + sima_draw_zbuffloat_pixels(scene, x, y, ibuf->x, ibuf->y, ibuf->rect_float); + } + else { + if(sima->flag & SI_USE_ALPHA) { + sima_draw_alpha_backdrop(x, y, ibuf->x, ibuf->y, zoomx, zoomy); + + glEnable(GL_BLEND); + glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA); + } + + /* we don't draw floats buffers directly but + * convert them, and optionally apply curves */ + image_verify_buffer_float(sima, ibuf); + + if(ibuf->rect) + glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + /*else + glaDrawPixelsSafe(x, y, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_FLOAT, ibuf->rect_float);*/ + + if(sima->flag & SI_USE_ALPHA) + glDisable(GL_BLEND); + } + + /* reset zoom */ + glPixelZoom(1.0f, 1.0f); +} + +static unsigned int *get_part_from_ibuf(ImBuf *ibuf, short startx, short starty, short endx, short endy) +{ + unsigned int *rt, *rp, *rectmain; + short y, heigth, len; + + /* the right offset in rectot */ + + rt= ibuf->rect+ (starty*ibuf->x+ startx); + + len= (endx-startx); + heigth= (endy-starty); + + rp=rectmain= MEM_mallocN(heigth*len*sizeof(int), "rect"); + + for(y=0; y<heigth; y++) { + memcpy(rp, rt, len*4); + rt+= ibuf->x; + rp+= len; + } + return rectmain; +} + +static void draw_image_buffer_tiled(SpaceImage *sima, ARegion *ar, Image *ima, ImBuf *ibuf, float zoomx, float zoomy) +{ + unsigned int *rect; + int dx, dy, sx, sy, x, y; + + /* verify valid values, just leave this a while */ + if(ima->xrep<1) return; + if(ima->yrep<1) return; + + glPixelZoom(zoomx, zoomy); + + if(sima->curtile >= ima->xrep*ima->yrep) + sima->curtile = ima->xrep*ima->yrep - 1; + + /* create char buffer from float if needed */ + image_verify_buffer_float(sima, ibuf); + + /* retrieve part of image buffer */ + dx= ibuf->x/ima->xrep; + dy= ibuf->y/ima->yrep; + sx= (sima->curtile % ima->xrep)*dx; + sy= (sima->curtile / ima->xrep)*dy; + rect= get_part_from_ibuf(ibuf, sx, sy, sx+dx, sy+dy); + + /* draw repeated */ + for(sy=0; sy+dy<=ibuf->y; sy+= dy) { + for(sx=0; sx+dx<=ibuf->x; sx+= dx) { + UI_view2d_view_to_region(&ar->v2d, (float)sx/(float)ibuf->x, (float)sy/(float)ibuf->y, &x, &y); + + glaDrawPixelsSafe(x, y, dx, dy, dx, GL_RGBA, GL_UNSIGNED_BYTE, rect); + } + } + + glPixelZoom(1.0f, 1.0f); + + MEM_freeN(rect); +} + +static void draw_image_buffer_repeated(SpaceImage *sima, ARegion *ar, Scene *scene, ImBuf *ibuf, float zoomx, float zoomy) +{ + float x, y; + double time_current; + + time_current = PIL_check_seconds_timer(); + + for(x=ar->v2d.cur.xmin; x<ar->v2d.cur.xmax; x += zoomx) { + for(y=ar->v2d.cur.ymin; y<ar->v2d.cur.ymax; y += zoomy) { + draw_image_buffer(sima, ar, scene, ibuf, x, y, zoomx, zoomy); + + /* only draw until running out of time */ + if((PIL_check_seconds_timer() - time_current) > 0.25) + return; + } + } +} + +/* draw uv edit */ + +/* XXX this becomes draw extra? */ +#if 0 + glPixelZoom(zoomx, zoomy); + + if(sima->flag & SI_EDITTILE) { + /* create char buffer from float if needed */ + image_verify_buffer_float(sima, ibuf); + + glaDrawPixelsSafe(x1, y1, ibuf->x, ibuf->y, ibuf->x, GL_RGBA, GL_UNSIGNED_BYTE, ibuf->rect); + + glPixelZoom(1.0, 1.0); + + dx= ibuf->x/sima->image->xrep; + dy= ibuf->y/sima->image->yrep; + sy= (sima->curtile / sima->image->xrep); + sx= sima->curtile - sy*sima->image->xrep; + + sx*= dx; + sy*= dy; + + calc_image_view(sima, 'p'); /* pixel */ + myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax); + + cpack(0x0); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glRects(sx, sy, sx+dx-1, sy+dy-1); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + cpack(0xFFFFFF); + glPolygonMode(GL_FRONT_AND_BACK, GL_LINE); glRects(sx+1, sy+1, sx+dx, sy+dy); glPolygonMode(GL_FRONT_AND_BACK, GL_FILL); + } +#endif + +/* draw grease pencil */ + +static void draw_image_grease_pencil(SpaceImage *sima, ImBuf *ibuf) +{ + /* XXX bring back */ + /* draw grease-pencil ('image' strokes) */ + if (sima->flag & SI_DISPGP) + ; // XXX draw_gpencil_2dimage(sa, ibuf); + +#if 0 + mywinset(sa->win); /* restore scissor after gla call... */ + wmOrtho2(-0.375, sa->winx-0.375, -0.375, sa->winy-0.375); +#endif + + /* draw grease-pencil (screen strokes) */ + if (sima->flag & SI_DISPGP) + ; // XXX draw_gpencil_2dview(sa, NULL); +} + +/* XXX becomes WM paint cursor */ +#if 0 +static void draw_image_view_tool(Scene *scene) +{ + ToolSettings *settings= scene->toolsettings; + Brush *brush= settings->imapaint.brush; + short mval[2]; + float radius; + int draw= 0; + + if(brush) { + if(settings->imapaint.flag & IMAGEPAINT_DRAWING) { + if(settings->imapaint.flag & IMAGEPAINT_DRAW_TOOL_DRAWING) + draw= 1; + } + else if(settings->imapaint.flag & IMAGEPAINT_DRAW_TOOL) + draw= 1; + + if(draw) { + getmouseco_areawin(mval); + + radius= brush->size*G.sima->zoom/2; + fdrawXORcirc(mval[0], mval[1], radius); + + if (brush->innerradius != 1.0) { + radius *= brush->innerradius; + fdrawXORcirc(mval[0], mval[1], radius); + } + } + } +} +#endif + +static unsigned char *get_alpha_clone_image(Scene *scene, int *width, int *height) +{ + Brush *brush = scene->toolsettings->imapaint.brush; + ImBuf *ibuf; + unsigned int size, alpha; + unsigned char *rect, *cp; + + if(!brush || !brush->clone.image) + return NULL; + + ibuf= BKE_image_get_ibuf(brush->clone.image, NULL); + + if(!ibuf || !ibuf->rect) + return NULL; + + rect= MEM_dupallocN(ibuf->rect); + if(!rect) + return NULL; + + *width= ibuf->x; + *height= ibuf->y; + + size= (*width)*(*height); + alpha= (unsigned char)255*brush->clone.alpha; + cp= rect; + + while(size-- > 0) { + cp[3]= alpha; + cp += 4; + } + + return rect; +} + +static void draw_image_paint_helpers(SpaceImage *sima, ARegion *ar, Scene *scene, float zoomx, float zoomy) +{ + Brush *brush; + int x, y, w, h; + unsigned char *clonerect; + + brush= scene->toolsettings->imapaint.brush; + + if(brush && (scene->toolsettings->imapaint.tool == PAINT_TOOL_CLONE)) { + /* this is not very efficient, but glDrawPixels doesn't allow + drawing with alpha */ + clonerect= get_alpha_clone_image(scene, &w, &h); + + if(clonerect) { + UI_view2d_to_region_no_clip(&ar->v2d, brush->clone.offset[0], brush->clone.offset[1], &x, &y); + + glPixelZoom(zoomx, zoomy); + + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glaDrawPixelsSafe(x, y, w, h, w, GL_RGBA, GL_UNSIGNED_BYTE, clonerect); + glDisable(GL_BLEND); + + glPixelZoom(1.0, 1.0); + + MEM_freeN(clonerect); + } + } +} + +/* draw main image area */ + +void draw_image_main(SpaceImage *sima, ARegion *ar, Scene *scene) +{ + Image *ima; + ImBuf *ibuf; + float zoomx, zoomy; + int show_viewer, show_render; + + /* XXX can we do this in refresh? */ +#if 0 + what_image(sima); + + if(sima->image) { + image_pixel_aspect(sima->image, &xuser_asp, &yuser_asp); + + /* UGLY hack? until now iusers worked fine... but for flipbook viewer we need this */ + if(sima->image->type==IMA_TYPE_COMPOSITE) { + ImageUser *iuser= ntree_get_active_iuser(scene->nodetree); + if(iuser) { + BKE_image_user_calc_imanr(iuser, scene->r.cfra, 0); + sima->iuser= *iuser; + } + } + /* and we check for spare */ + ibuf= get_space_image_buffer(sima); + } +#endif + + /* retrieve the image and information about it */ + ima= get_space_image(sima); + ibuf= get_space_image_buffer(sima); + get_space_image_zoom(sima, ar, &zoomx, &zoomy); + + show_viewer= (ima && ima->source == IMA_SRC_VIEWER); + show_render= (show_viewer && ima->type == IMA_TYPE_R_RESULT); + + /* draw the image or grid */ + if(ibuf==NULL) + draw_image_grid(ar, zoomx, zoomy); + else if(sima->flag & SI_DRAW_TILE) + draw_image_buffer_repeated(sima, ar, scene, ibuf, zoomx, zoomy); + else if(ima && (ima->tpageflag & IMA_TILES)) + draw_image_buffer_tiled(sima, ar, ima, ibuf, zoomx, zoomy); + else + draw_image_buffer(sima, ar, scene, ibuf, 0.0f, 0.0f, zoomx, zoomy); + + /* grease pencil */ + draw_image_grease_pencil(sima, ibuf); + + /* paint helpers */ + draw_image_paint_helpers(sima, ar, scene, zoomx, zoomy); + + /* render info */ + if(ibuf && show_render) + sima_draw_render_info(sima, ar); + + /* XXX integrate this code */ +#if 0 + if(ibuf) { + float xoffs=0.0f, yoffs= 0.0f; + + if(image_preview_active(sa, &xim, &yim)) { + xoffs= scene->r.disprect.xmin; + yoffs= scene->r.disprect.ymin; + glColor3ub(0,0,0); + calc_image_view(sima, 'f'); + myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax); + glRectf(0.0f, 0.0f, 1.0f, 1.0f); + glLoadIdentity(); + } + } +#endif + +#if 0 + /* it is important to end a view in a transform compatible with buttons */ + bwin_scalematrix(sa->win, sima->blockscale, sima->blockscale, sima->blockscale); + if(!(G.rendering && show_render)) + image_blockhandlers(sa); +#endif +} + diff --git a/source/blender/editors/space_image/image_header.c b/source/blender/editors/space_image/image_header.c index 6363718779a..6991d7f4206 100644 --- a/source/blender/editors/space_image/image_header.c +++ b/source/blender/editors/space_image/image_header.c @@ -29,6 +29,9 @@ #include <string.h> #include <stdio.h> +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" +#include "DNA_customdata_types.h" #include "DNA_space_types.h" #include "DNA_scene_types.h" #include "DNA_screen_types.h" @@ -37,10 +40,18 @@ #include "MEM_guardedalloc.h" #include "BLI_blenlib.h" +#include "BLI_editVert.h" #include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_image.h" #include "BKE_screen.h" +#include "BKE_utildefines.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "ED_mesh.h" #include "ED_screen.h" #include "ED_types.h" #include "ED_util.h" @@ -55,29 +66,714 @@ #include "UI_resources.h" #include "UI_view2d.h" -#include "image_intern.h" +#include "RNA_access.h" +#include "RE_pipeline.h" + +#include "image_intern.h" /* ************************ header area region *********************** */ +#define B_NOP -1 +#define B_REDR 1 +#define B_SIMAGEPAINTTOOL 4 +#define B_SIMA_USE_ALPHA 5 +#define B_SIMA_SHOW_ALPHA 6 +#define B_SIMA_SHOW_ZBUF 7 +#define B_SIMA_RECORD 8 +#define B_SIMA_PLAY 9 + +static uiBlock *image_view_viewnavmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + uiBlock *block; + uiBut *but; + int a; + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_view_viewnavmenu", UI_EMBOSSP, UI_HELV); + + uiDefMenuButO(block, "IMAGE_OT_view_zoom_in", NULL); + uiDefMenuButO(block, "IMAGE_OT_view_zoom_out", NULL); + + uiDefMenuSep(block); + + for(a=0; a<7; a++) { + const int ratios[7][2] = {{1, 8}, {1, 4}, {1, 2}, {1, 1}, {2, 1}, {4, 1}, {8, 1}}; + char namestr[128]; + + sprintf(namestr, "Zoom %d:%d", ratios[a][0], ratios[a][1]); + + but= uiDefMenuButO(block, "IMAGE_OT_view_zoom_ratio", namestr); + RNA_float_set(uiButGetOperatorPtrRNA(but), "ratio", (float)ratios[a][0]/(float)ratios[a][1]); + } + + /* XXX find key shortcut! */ + + /* position menu */ + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 50); + + return block; +} + +#if 0 static void do_viewmenu(bContext *C, void *arg, int event) { + switch(event) { + case 1: /* View All */ + do_image_buttons(B_SIMAGEHOME); + break; + case 4: /* Realtime Panel... */ + add_blockhandler(curarea, IMAGE_HANDLER_VIEW_PROPERTIES, UI_PNL_UNSTOW); + break; + case 7: /* Properties Panel */ + add_blockhandler(curarea, IMAGE_HANDLER_PROPERTIES, UI_PNL_UNSTOW); + break; + case 8: /* Paint Panel... */ + add_blockhandler(curarea, IMAGE_HANDLER_PAINT, UI_PNL_UNSTOW); + break; + case 9: + image_viewcenter(); + break; + case 11: /* Curves Panel... */ + add_blockhandler(curarea, IMAGE_HANDLER_CURVES, UI_PNL_UNSTOW); + break; + case 12: /* composite preview */ + toggle_blockhandler(curarea, IMAGE_HANDLER_PREVIEW, 0); + scrarea_queue_winredraw(curarea); + break; + case 13: /* Realtime Panel... */ + add_blockhandler(curarea, IMAGE_HANDLER_GAME_PROPERTIES, UI_PNL_UNSTOW); + break; + case 15: /* Grease Pencil... */ + add_blockhandler(curarea, IMAGE_HANDLER_GREASEPENCIL, UI_PNL_UNSTOW); + break; + } + + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWVIEW3D, 0); +} +#endif + +static uiBlock *image_viewmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa= CTX_wm_area(C); + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + PointerRNA spaceptr, uvptr; + uiBlock *block; + int show_paint, show_render, show_uvedit; + + /* retrrieve state */ + RNA_pointer_create(&sc->id, &RNA_SpaceImageEditor, sima, &spaceptr); + RNA_pointer_create(&sc->id, &RNA_SpaceUVEditor, sima, &uvptr); + + show_render= get_space_image_show_render(sima); + show_paint= get_space_image_show_paint(sima); + show_uvedit= get_space_image_show_uvedit(sima, CTX_data_edit_object(C)); + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_viewmenu", UI_EMBOSSP, UI_HELV); + + uiDefMenuButO(block, "IMAGE_OT_toggle_view_properties_panel", NULL); // View Properties... + uiDefMenuButO(block, "IMAGE_OT_toggle_image_properties_panel", NULL); // Image Properties...|N + uiDefMenuButO(block, "IMAGE_OT_toggle_realtime_properties_panel", NULL); // Real-time properties... + if(show_paint) uiDefMenuButO(block, "IMAGE_OT_toggle_paint_panel", NULL); // Paint Tool...|C + uiDefMenuButO(block, "IMAGE_OT_toggle_curves_panel", NULL); // Curves Tool... + if(show_render) uiDefMenuButO(block, "IMAGE_OT_toggle_compositing_preview_panel", NULL); // Compositing Preview...|Shift P + uiDefMenuButO(block, "IMAGE_OT_toggle_grease_pencil_panel", NULL); // Grease Pencil... + + uiDefMenuSep(block); + + uiDefMenuTogR(block, &spaceptr, "update_automatically", NULL, NULL); + // XXX if(show_uvedit) uiDefMenuTogR(block, &uvptr, "local_view", NULL, "UV Local View"); // Numpad / + + uiDefMenuSep(block); + + uiDefMenuSub(block, image_view_viewnavmenu, "View Navigation"); + if(show_uvedit) uiDefMenuButO(block, "IMAGE_OT_view_selected", NULL); + uiDefMenuButO(block, "IMAGE_OT_view_all", NULL); + + if(sa->full) uiDefMenuButO(block, "SCREEN_OT_screen_full_area", "Tile Window"); // Ctrl UpArrow + else uiDefMenuButO(block, "SCREEN_OT_screen_full_area", "Maximize Window"); // Ctr DownArrow + + /* position menu */ + if(sa->headertype==HEADERTOP) { + uiBlockSetDirection(block, UI_DOWN); + } + else { + uiBlockSetDirection(block, UI_TOP); + uiBlockFlipOrder(block); + } + + uiTextBoundsBlock(block, 50); + uiEndBlock(C, block); + + return block; +} + +#if 0 +static void do_selectmenu(bContext *C, void *arg, int event) +{ + switch(event) + { + case 0: /* Border Select */ + borderselect_sima(UV_SELECT_ALL); + break; + case 8: /* Border Select Pinned */ + borderselect_sima(UV_SELECT_PINNED); + break; + case 2: /* Unlink Selection */ + unlink_selection(); + break; + case 3: /* Linked UVs */ + select_linked_tface_uv(2); + break; + case 7: /* Pinned UVs */ + select_pinned_tface_uv(); + break; + } +} +#endif + +static uiBlock *image_selectmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + ScrArea *sa= CTX_wm_area(C); + uiBlock *block; + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_selectmenu", UI_EMBOSSP, UI_HELV); + + uiDefMenuButO(block, "UV_OT_border_select", NULL); // Border Select|B + uiDefMenuButO(block, "UV_OT_border_select_pinned", NULL); // Border Select Pinned|Shift B + + uiDefMenuSep(block); + + uiDefMenuButO(block, "UV_OT_de_select_all", NULL); + uiDefMenuButO(block, "UV_OT_select_inverse", NULL); + uiDefMenuButO(block, "UV_OT_unlink_selection", NULL); // Unlink Selection|Alt L + + uiDefMenuSep(block); + + uiDefMenuButO(block, "UV_OT_select_pinned", NULL); // Select Pinned|Shift P + uiDefMenuButO(block, "UV_OT_select_linked", NULL); // Select Linked|Ctrl L + + /* position menu */ + if(sa->headertype==HEADERTOP) { + uiBlockSetDirection(block, UI_DOWN); + } + else { + uiBlockSetDirection(block, UI_TOP); + uiBlockFlipOrder(block); + } + + uiTextBoundsBlock(block, 50); + uiEndBlock(C, block); + + return block; +} + +#if 0 +static void do_image_imagemenu(void *arg, int event) +{ + /* events >=20 are registered bpython scripts */ +#ifndef DISABLE_PYTHON + if (event >= 20) BPY_menu_do_python(PYMENU_IMAGE, event - 20); +#endif + switch(event) + { + case 0: + open_image_sima((G.qual==LR_CTRLKEY)); + break; + case 1: + replace_image_sima((G.qual==LR_CTRLKEY)); + break; + case 2: + pack_image_sima(); + break; + case 4: /* Texture Painting */ + brush_check_exists(&G.scene->toolsettings->imapaint.brush); + if(sima->flag & SI_DRAWTOOL) sima->flag &= ~SI_DRAWTOOL; + else sima->flag |= SI_DRAWTOOL; + allqueue(REDRAWBUTSSHADING, 0); + break; + case 5: + save_as_image_sima(); + break; + case 6: + reload_image_sima(); + break; + case 7: + new_image_sima(); + break; + case 8: + save_image_sima(); + break; + case 9: + save_image_sequence_sima(); + break; + case 10: + BKE_image_memorypack(sima->image); + allqueue(REDRAWIMAGE, 0); + break; + } +} +#endif + +/* move to realtime properties panel */ +#if 0 +static void do_image_image_rtmappingmenu(void *arg, int event) +{ + switch(event) { + case 0: /* UV Co-ordinates */ + sima->image->flag &= ~IMA_REFLECT; + break; + case 1: /* Reflection */ + sima->image->flag |= IMA_REFLECT; + break; + } + + allqueue(REDRAWVIEW3D, 0); +} +#endif + +static uiBlock *image_imagemenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa= CTX_wm_area(C); + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + uiBlock *block; + PointerRNA spaceptr; + Image *ima; + ImBuf *ibuf; + int show_render; + + /* retrieve state */ + ima= get_space_image(sima); + ibuf= get_space_image_buffer(sima); + + show_render= get_space_image_show_render(sima); + + RNA_pointer_create(&sc->id, &RNA_SpaceImageEditor, sima, &spaceptr); + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_imagemenu", UI_EMBOSSP, UI_HELV); + + uiDefMenuButO(block, "IMAGE_OT_new", NULL); // New...|Alt N + uiDefMenuButO(block, "IMAGE_OT_open", NULL); // Open...|Alt O + + if(ima) { + uiDefMenuButO(block, "IMAGE_OT_replace", NULL); // Replace... + uiDefMenuButO(block, "IMAGE_OT_reload", NULL); // Reload...|Alt R + uiDefMenuButO(block, "IMAGE_OT_save", NULL); // Save|Alt S + uiDefMenuButO(block, "IMAGE_OT_save_as", NULL); // Save As... + if(ima->source == IMA_SRC_SEQUENCE) + uiDefMenuButO(block, "IMAGE_OT_save_changed", NULL); // Save Changed Images + + if(!show_render) { + uiDefMenuSep(block); + + if(ima->packedfile) uiDefMenuButO(block, "IMAGE_OT_unpack", NULL); // Unpack Image... + else uiDefMenuButO(block, "IMAGE_OT_pack", NULL); // Pack Image + + /* only for dirty && specific image types : XXX poll? */ + if(ibuf && (ibuf->userflags & IB_BITMAPDIRTY)) + if(ELEM(ima->source, IMA_SRC_FILE, IMA_SRC_GENERATED) && ima->type != IMA_TYPE_MULTILAYER) + uiDefMenuButO(block, "IMAGE_OT_pack_as_png", NULL); // Pack Image As PNG + + uiDefMenuSep(block); + + /* XXX check state better */ + uiDefMenuTogR(block, &spaceptr, "image_painting", NULL, NULL); + } + } + +#if 0 +#ifndef DISABLE_PYTHON + { + BPyMenu *pym; + int i = 0; + + /* note that we acount for the N previous entries with i+20: */ + for (pym = BPyMenuTable[PYMENU_IMAGE]; pym; pym = pym->next, i++) { + + uiDefIconTextBut(block, BUTM, 1, ICON_PYTHON, pym->name, 0, yco-=20, menuwidth, 19, + NULL, 0.0, 0.0, 1, i+20, + pym->tooltip?pym->tooltip:pym->filename); + } + } +#endif +#endif + + /* position menu */ + if(sa->headertype==HEADERTOP) { + uiBlockSetDirection(block, UI_DOWN); + } + else { + uiBlockSetDirection(block, UI_TOP); + uiBlockFlipOrder(block); + } + + uiTextBoundsBlock(block, 80); + uiEndBlock(C, block); + + return block; +} + +#if 0 +static void do_image_uvs_showhidemenu(void *arg, int event) +{ + switch(event) { + case 4: /* show hidden faces */ + reveal_tface_uv(); + break; + case 5: /* hide selected faces */ + hide_tface_uv(0); + break; + case 6: /* hide deselected faces */ + hide_tface_uv(1); + break; + } + allqueue(REDRAWVIEW3D, 0); +} +#endif + +static uiBlock *image_uvs_showhidemenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + uiBlock *block; + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_uvs_showhidemenu", UI_EMBOSSP, UI_HELV); + + uiDefMenuButO(block, "UV_OT_show_hidden_faces", NULL); // Show Hidden Faces|Alt H + uiDefMenuButO(block, "UV_OT_hide_selected_faces", NULL); // Hide Selected Faces|H + uiDefMenuButO(block, "UV_OT_hide_deselected_faces", NULL); // Hide Deselected Faces|Shift H + + /* position menu */ + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 60); + + uiEndBlock(C, block); + + return block; +} + +#if 0 +static void do_image_uvs_propfalloffmenu(void *arg, int event) +{ + G.scene->prop_mode= event; + allqueue(REDRAWVIEW3D, 1); +} +#endif + +static uiBlock *image_uvs_propfalloffmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + Scene *scene= CTX_data_scene(C); + PointerRNA sceneptr; + uiBlock *block; + + /* retrieve state */ + RNA_id_pointer_create(&scene->id, &sceneptr); + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_uvs_propfalloffmenu", UI_EMBOSSP, UI_HELV); + + uiDefMenuTogR(block, &sceneptr, "proportional_editing_falloff", "SMOOTH", NULL); // Smooth|Shift O + uiDefMenuTogR(block, &sceneptr, "proportional_editing_falloff", "SPHERE", NULL); // Sphere|Shift O + uiDefMenuTogR(block, &sceneptr, "proportional_editing_falloff", "ROOT", NULL); // Root|Shift O + uiDefMenuTogR(block, &sceneptr, "proportional_editing_falloff", "SHARP", NULL); // Sharp|Shift O + uiDefMenuTogR(block, &sceneptr, "proportional_editing_falloff", "LINEAR", NULL); // Linear|Shift O + uiDefMenuTogR(block, &sceneptr, "proportional_editing_falloff", "RANDOM", NULL); // Random|Shift O + uiDefMenuTogR(block, &sceneptr, "proportional_editing_falloff", "CONSTANT", NULL); // Constant|Shift O + + /* position menu */ + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 60); + + uiEndBlock(C, block); + + return block; +} + +#if 0 +static void do_image_uvs_transformmenu(void *arg, int event) +{ + switch(event) { + case 0: /* Grab */ + initTransform(TFM_TRANSLATION, CTX_NONE); + Transform(); + break; + case 1: /* Rotate */ + initTransform(TFM_ROTATION, CTX_NONE); + Transform(); + break; + case 2: /* Scale */ + initTransform(TFM_RESIZE, CTX_NONE); + Transform(); + break; + } +} +#endif + +static uiBlock *image_uvs_transformmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + uiBlock *block; + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_uvs_transformmenu", UI_EMBOSSP, UI_HELV); + + uiDefMenuButO(block, "UV_OT_grab", NULL); // Grab/Move|G + uiDefMenuButO(block, "UV_OT_rotate", NULL); // Rotate|R + uiDefMenuButO(block, "UV_OT_scale", NULL); // Scale|S + + /* position menu */ + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 60); + + uiEndBlock(C, block); + + return block; +} + +#if 0 +static void do_image_uvs_mirrormenu(void *arg, int event) +{ + float mat[3][3]; + + Mat3One(mat); + + switch(event) { + case 0: /* X axis */ + initTransform(TFM_MIRROR, CTX_NO_PET|CTX_AUTOCONFIRM); + BIF_setSingleAxisConstraint(mat[0], " on global X axis"); + Transform(); + break; + case 1: /* Y axis */ + initTransform(TFM_MIRROR, CTX_NO_PET|CTX_AUTOCONFIRM); + BIF_setSingleAxisConstraint(mat[1], " on global Y axis"); + Transform(); + break; + } + + BIF_undo_push("Mirror UV"); +} +#endif + +static uiBlock *image_uvs_mirrormenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + uiBlock *block; + uiBut *but; + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_uvs_mirrormenu", UI_EMBOSSP, UI_HELV); + + but= uiDefMenuButO(block, "UV_OT_mirror", "X Axis"); // M, 1 + RNA_enum_set(uiButGetOperatorPtrRNA(but), "axis", 'x'); + but= uiDefMenuButO(block, "UV_OT_mirror", "Y Axis"); // M, 2 + RNA_enum_set(uiButGetOperatorPtrRNA(but), "axis", 'y'); + + /* position menu */ + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 60); + + uiEndBlock(C, block); + + return block; +} + +#if 0 +static void do_image_uvs_weldalignmenu(void *arg, int event) +{ + switch(event) { + case 0: /* Weld */ + weld_align_tface_uv('w'); + break; + case 1: /* Align Auto */ + weld_align_tface_uv('a'); + break; + case 2: /* Align X */ + weld_align_tface_uv('x'); + break; + case 3: /* Align Y */ + weld_align_tface_uv('y'); + break; + } + + if(event==0) BIF_undo_push("Weld UV"); + else if(ELEM3(event, 1, 2, 3)) BIF_undo_push("Align UV"); +} +#endif + +static uiBlock *image_uvs_weldalignmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + uiBlock *block; + uiBut *but; + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_uvs_weldalignmenu", UI_EMBOSSP, UI_HELV); + + but= uiDefMenuButO(block, "UV_OT_weld", NULL); // W, 1 + but= uiDefMenuButO(block, "UV_OT_align", "Align Auto"); // W, 2 + RNA_enum_set(uiButGetOperatorPtrRNA(but), "axis", 'a'); + but= uiDefMenuButO(block, "UV_OT_align", "Align X"); // W, 3 + RNA_enum_set(uiButGetOperatorPtrRNA(but), "axis", 'x'); + but= uiDefMenuButO(block, "UV_OT_align", "Align Y"); // W, 4 + RNA_enum_set(uiButGetOperatorPtrRNA(but), "axis", 'y'); + + /* position menu */ + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 60); + + uiEndBlock(C, block); + + return block; +} + +#if 0 +#ifndef DISABLE_PYTHON +static void do_image_uvs_scriptsmenu(void *arg, int event) +{ + BPY_menu_do_python(PYMENU_UV, event); + + allqueue(REDRAWIMAGE, 0); } -static uiBlock *dummy_viewmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +static uiBlock *image_uvs_scriptsmenu (void *args_unused) { - ScrArea *curarea= CTX_wm_area(C); uiBlock *block; - short yco= 0, menuwidth=120; + BPyMenu *pym; + int i= 0; + short yco = 20, menuwidth = 120; - block= uiBeginBlock(C, handle->region, "dummy_viewmenu", UI_EMBOSSP, UI_HELV); - uiBlockSetButmFunc(block, do_viewmenu, NULL); + block= uiNewBlock(&curarea->uiblocks, "image_uvs_scriptsmenu", UI_EMBOSSP, UI_HELV, G.curscreen->mainwin); + uiBlockSetButmFunc(block, do_image_uvs_scriptsmenu, NULL); - uiDefIconTextBut(block, BUTM, 1, ICON_BLANK1, "Nothing yet", 0, yco-=20, - menuwidth, 19, NULL, 0.0, 0.0, 1, 3, ""); + /* note that we acount for the N previous entries with i+20: */ + for (pym = BPyMenuTable[PYMENU_UV]; pym; pym = pym->next, i++) { + + uiDefIconTextBut(block, BUTM, 1, ICON_PYTHON, pym->name, 0, yco-=20, menuwidth, 19, + NULL, 0.0, 0.0, 1, i, + pym->tooltip?pym->tooltip:pym->filename); + } + + uiBlockSetDirection(block, UI_RIGHT); + uiTextBoundsBlock(block, 60); - if(curarea->headertype==HEADERTOP) { + return block; +} +#endif /* DISABLE_PYTHON */ +#endif + +#if 0 +static void do_uvsmenu(bContext *C, void *arg, int event) +{ + switch(event) { + case 1: /* UVs Constrained Rectangular */ + if(sima->flag & SI_BE_SQUARE) sima->flag &= ~SI_BE_SQUARE; + else sima->flag |= SI_BE_SQUARE; + break; + case 2: /* UVs Clipped to Image Size */ + if(sima->flag & SI_CLIP_UV) sima->flag &= ~SI_CLIP_UV; + else sima->flag |= SI_CLIP_UV; + break; + case 5: /* Proportional Edit (toggle) */ + if(G.scene->proportional) + G.scene->proportional= 0; + else + G.scene->proportional= 1; + break; + case 7: /* UVs Snap to Pixel */ + sima->flag ^= SI_PIXELSNAP; + break; + case 8: + pin_tface_uv(1); + break; + case 9: + pin_tface_uv(0); + break; + case 10: + unwrap_lscm(0); + break; + case 11: + if(sima->flag & SI_LIVE_UNWRAP) sima->flag &= ~SI_LIVE_UNWRAP; + else sima->flag |= SI_LIVE_UNWRAP; + break; + case 12: + minimize_stretch_tface_uv(); + break; + case 13: + pack_charts_tface_uv(); + break; + case 14: + average_charts_tface_uv(); + break; + } +} +#endif + +static uiBlock *image_uvsmenu(bContext *C, uiMenuBlockHandle *handle, void *arg_unused) +{ + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa= CTX_wm_area(C); + Scene *scene= CTX_data_scene(C); + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + uiBlock *block; + PointerRNA uvptr, sceneptr; + Image *ima; + ImBuf *ibuf; + + /* retrieve state */ + ima= get_space_image(sima); + ibuf= get_space_image_buffer(sima); + + RNA_pointer_create(&sc->id, &RNA_SpaceUVEditor, sima, &uvptr); + RNA_id_pointer_create(&scene->id, &sceneptr); + + /* create menu */ + block= uiBeginBlock(C, handle->region, "image_imagemenu", UI_EMBOSSP, UI_HELV); + + uiDefMenuTogR(block, &uvptr, "snap_to_pixels", 0, NULL); + uiDefMenuTogR(block, &uvptr, "constrain_quads_rectangular", 0, NULL); + uiDefMenuTogR(block, &uvptr, "constrain_to_image_bounds", 0, NULL); + + uiDefMenuSep(block); + + uiDefMenuTogR(block, &uvptr, "live_unwrap", 0, NULL); + uiDefMenuButO(block, "UV_OT_unwrap", NULL); // Unwrap|E + uiDefMenuButO(block, "UV_OT_unpin", NULL); // Unpin|Alt P + uiDefMenuButO(block, "UV_OT_pin", NULL); // Pin|P + + uiDefMenuSep(block); + + uiDefMenuButO(block, "UV_OT_pack_islands", NULL); // Pack Islands|Ctr P + uiDefMenuButO(block, "UV_OT_average_islands", NULL); // Average Islands Scale|Ctrl A + uiDefMenuButO(block, "UV_OT_minimize_stretch", NULL); // Minimize Stretch...|Ctrl V + uiDefMenuButO(block, "UV_OT_stitch", NULL); + + uiDefMenuSep(block); + + uiDefMenuSub(block, image_uvs_transformmenu, "Transform"); + uiDefMenuSub(block, image_uvs_mirrormenu, "Mirror"); + uiDefMenuSub(block, image_uvs_weldalignmenu, "Weld/Align"); + + uiDefMenuSep(block); + + uiDefMenuTogR(block, &sceneptr, "proportional_editing", 0, NULL); + uiDefMenuSub(block, image_uvs_propfalloffmenu, "Proportional Falloff"); + + uiDefMenuSep(block); + + uiDefMenuSub(block, image_uvs_showhidemenu, "Show/Hide Faces"); + +#if 0 +#ifndef DISABLE_PYTHON + uiDefMenuSep(block); + + uiDefMenuSub(block, image_uvs_scriptsmenu, "Scripts"); +#endif +#endif + + if(sa->headertype==HEADERTOP) { uiBlockSetDirection(block, UI_DOWN); } else { @@ -91,38 +787,541 @@ static uiBlock *dummy_viewmenu(bContext *C, uiMenuBlockHandle *handle, void *arg return block; } +static void image_menu_uvlayers(Object *obedit, char *menustr, int *active) +{ + Mesh *me= (Mesh*)obedit->data; + EditMesh *em= me->edit_mesh; + CustomDataLayer *layer; + int i, count = 0; + + menustr[0]= '\0'; + + for(i=0; i<em->fdata.totlayer; i++) { + layer = &em->fdata.layers[i]; + + if(layer->type == CD_MTFACE) { + menustr += sprintf(menustr, "%s%%x%d|", layer->name, count); + count++; + } + } + + *active= CustomData_get_active_layer(&em->fdata, CD_MTFACE); +} + static void do_image_buttons(bContext *C, void *arg, int event) { switch(event) { + case B_REDR: + ED_area_tag_redraw(CTX_wm_area(C)); + break; } + +#if 0 + ToolSettings *settings= G.scene->toolsettings; + ID *id, *idtest; + int nr; + + if(curarea->win==0) return; + + if(event<=100) { + if(event<=50) do_global_buttons2(event); + else do_global_buttons(event); + return; + } + + switch(event) { + case B_SIMAPIN: + allqueue (REDRAWIMAGE, 0); + break; + case B_SIMAGEHOME: + image_home(); + break; + + case B_SIMABROWSE: + if(sima->imanr== -2) { + if(G.qual & LR_CTRLKEY) { + activate_databrowse_imasel((ID *)sima->image, ID_IM, 0, B_SIMABROWSE, + &sima->imanr, do_image_buttons); + } else { + activate_databrowse((ID *)sima->image, ID_IM, 0, B_SIMABROWSE, + &sima->imanr, do_image_buttons); + } + return; + } + if(sima->imanr < 0) break; + + nr= 1; + id= (ID *)sima->image; + + idtest= BLI_findlink(&G.main->image, sima->imanr-1); + if(idtest==NULL) { /* no new */ + return; + } + + if(idtest!=id) { + sima->image= (Image *)idtest; + if(idtest->us==0) idtest->us= 1; + BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_USER_NEW_IMAGE); + allqueue(REDRAWIMAGE, 0); + } + /* also when image is the same: assign! 0==no tileflag: */ + image_changed(sima, (Image *)idtest); + BIF_undo_push("Assign image UV"); + + break; + case B_SIMAGETILE: + image_set_tile(sima, 1); /* 1: only tileflag */ + allqueue(REDRAWVIEW3D, 0); + allqueue(REDRAWIMAGE, 0); + break; + case B_SIMA3DVIEWDRAW: + allqueue(REDRAWVIEW3D, 0); + break; + case B_SIMA_REDR_IMA_3D: + allqueue(REDRAWVIEW3D, 0); + allqueue(REDRAWIMAGE, 0); + break; + case B_SIMAGEPAINTTOOL: + if(sima->flag & SI_DRAWTOOL) + /* add new brush if none exists */ + brush_check_exists(&G.scene->toolsettings->imapaint.brush); + allqueue(REDRAWBUTSSHADING, 0); + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWVIEW3D, 0); + break; + + case B_SIMAPACKIMA: + pack_image_sima(); + break; + + case B_SIMA_REPACK: + BKE_image_memorypack(sima->image); + allqueue(REDRAWIMAGE, 0); + break; + + case B_SIMA_USE_ALPHA: + sima->flag &= ~(SI_SHOW_ALPHA|SI_SHOW_ZBUF); + scrarea_queue_winredraw(curarea); + scrarea_queue_headredraw(curarea); + break; + case B_SIMA_SHOW_ALPHA: + sima->flag &= ~(SI_USE_ALPHA|SI_SHOW_ZBUF); + scrarea_queue_winredraw(curarea); + scrarea_queue_headredraw(curarea); + break; + case B_SIMA_SHOW_ZBUF: + sima->flag &= ~(SI_SHOW_ALPHA|SI_USE_ALPHA); + scrarea_queue_winredraw(curarea); + scrarea_queue_headredraw(curarea); + break; + case B_SIMARELOAD: + reload_image_sima(); + break; + case B_SIMAGELOAD: + open_image_sima(0); + break; + case B_SIMANAME: + if(sima->image) { + Image *ima; + char str[FILE_MAXDIR+FILE_MAXFILE]; + + /* name in ima has been changed by button! */ + BLI_strncpy(str, sima->image->name, sizeof(str)); + ima= BKE_add_image_file(str); + if(ima) { + BKE_image_signal(ima, &sima->iuser, IMA_SIGNAL_RELOAD); + image_changed(sima, ima); + } + BIF_undo_push("Load image"); + allqueue(REDRAWIMAGE, 0); + } + break; + case B_SIMAMULTI: + if(sima && sima->image) { + BKE_image_multilayer_index(sima->image->rr, &sima->iuser); + allqueue(REDRAWIMAGE, 0); + } + break; + case B_TRANS_IMAGE: + image_editvertex_buts(NULL); + break; + case B_CURSOR_IMAGE: + image_editcursor_buts(NULL); + break; + + case B_TWINANIM: + { + Image *ima; + int nr; + + ima = sima->image; + if (ima) { + if(ima->flag & IMA_TWINANIM) { + nr= ima->xrep*ima->yrep; + if(ima->twsta>=nr) ima->twsta= 1; + if(ima->twend>=nr) ima->twend= nr-1; + if(ima->twsta>ima->twend) ima->twsta= 1; + } + + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWVIEW3D, 0); + } + break; + } + case B_SIMACLONEBROWSE: + if(settings->imapaint.brush) { + Brush *brush= settings->imapaint.brush; + + if(sima->menunr== -2) { + if(G.qual & LR_CTRLKEY) { + activate_databrowse_imasel((ID *)brush->clone.image, ID_IM, 0, B_SIMACLONEBROWSE, + &sima->menunr, do_image_buttons); + } else { + activate_databrowse((ID *)brush->clone.image, ID_IM, 0, B_SIMACLONEBROWSE, + &sima->menunr, do_image_buttons); + } + break; + } + if(sima->menunr < 0) break; + + if(brush_clone_image_set_nr(brush, sima->menunr)) + allqueue(REDRAWIMAGE, 0); + } + break; + + case B_SIMACLONEDELETE: + if (settings->imapaint.brush) + if (brush_clone_image_delete(settings->imapaint.brush)) + allqueue(REDRAWIMAGE, 0); + break; + + case B_SIMABRUSHCHANGE: + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); + break; + + case B_SIMACURVES: + curvemapping_do_ibuf(sima->cumap, imagewindow_get_ibuf(sima)); + allqueue(REDRAWIMAGE, 0); + break; + + case B_SIMARANGE: + curvemapping_set_black_white(sima->cumap, NULL, NULL); + curvemapping_do_ibuf(sima->cumap, imagewindow_get_ibuf(sima)); + allqueue(REDRAWIMAGE, 0); + break; + + case B_SIMABRUSHBROWSE: + if(sima->menunr==-2) { + activate_databrowse((ID*)settings->imapaint.brush, ID_BR, 0, B_SIMABRUSHBROWSE, &sima->menunr, do_global_buttons); + break; + } + else if(sima->menunr < 0) break; + + if(brush_set_nr(&settings->imapaint.brush, sima->menunr)) { + BIF_undo_push("Browse Brush"); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + } + break; + case B_SIMABRUSHDELETE: + if(brush_delete(&settings->imapaint.brush)) { + BIF_undo_push("Unlink Brush"); + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); + } + break; + case B_KEEPDATA: + brush_toggled_fake_user(settings->imapaint.brush); + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); + break; + case B_SIMABRUSHLOCAL: + if(settings->imapaint.brush && settings->imapaint.brush->id.lib) { + if(okee("Make local")) { + make_local_brush(settings->imapaint.brush); + allqueue(REDRAWIMAGE, 0); + allqueue(REDRAWBUTSEDIT, 0); + } + } + break; + case B_SIMABTEXBROWSE: + if(settings->imapaint.brush) { + Brush *brush= settings->imapaint.brush; + + if(sima->menunr==-2) { + MTex *mtex= brush->mtex[brush->texact]; + ID *id= (ID*)((mtex)? mtex->tex: NULL); + if(G.qual & LR_CTRLKEY) { + activate_databrowse_imasel(id, ID_TE, 0, B_SIMABTEXBROWSE, &sima->menunr, do_image_buttons); + } else { + activate_databrowse(id, ID_TE, 0, B_SIMABTEXBROWSE, &sima->menunr, do_image_buttons); + } + break; + } + else if(sima->menunr < 0) break; + + if(brush_texture_set_nr(brush, sima->menunr)) { + BIF_undo_push("Browse Brush Texture"); + allqueue(REDRAWBUTSSHADING, 0); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + } + } + break; + case B_SIMABTEXDELETE: + if(settings->imapaint.brush) { + if (brush_texture_delete(settings->imapaint.brush)) { + BIF_undo_push("Unlink Brush Texture"); + allqueue(REDRAWBUTSSHADING, 0); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); + } + } + break; + case B_SIMA_PLAY: + play_anim(0); + break; + case B_SIMA_RECORD: + imagespace_composite_flipbook(curarea); + break; + } +#endif } +#if 0 +static void do_image_buttons_set_uvlayer_callback(void *act, void *data) +{ + CustomData_set_layer_active(&G.editMesh->fdata, CD_MTFACE, *((int *)act)); + + BIF_undo_push("Set Active UV Texture"); + allqueue(REDRAWVIEW3D, 0); + allqueue(REDRAWBUTSEDIT, 0); + allqueue(REDRAWIMAGE, 0); +} +#endif void image_header_buttons(const bContext *C, ARegion *ar) { + bScreen *sc= CTX_wm_screen(C); ScrArea *sa= CTX_wm_area(C); + Scene *scene= CTX_data_scene(C); + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + Image *ima; + ImBuf *ibuf; uiBlock *block; - int xco, yco= 3; + uiBut *but; + PointerRNA spaceptr, uvptr, sceneptr; + int xco, yco= 3, show_uvedit, show_render, show_paint; + + /* retrieve state */ + ima= get_space_image(sima); + ibuf= get_space_image_buffer(sima); + + show_render= get_space_image_show_render(sima); + show_paint= get_space_image_show_paint(sima); + show_uvedit= get_space_image_show_uvedit(sima, CTX_data_edit_object(C)); + + RNA_pointer_create(&sc->id, &RNA_SpaceImageEditor, sima, &spaceptr); + RNA_pointer_create(&sc->id, &RNA_SpaceUVEditor, sima, &uvptr); + RNA_id_pointer_create(&scene->id, &sceneptr); + /* create block */ block= uiBeginBlock(C, ar, "header buttons", UI_EMBOSS, UI_HELV); uiBlockSetHandleFunc(block, do_image_buttons, NULL); xco= ED_area_header_standardbuttons(C, block, yco); + /* create pulldown menus */ if((sa->flag & HEADER_NO_PULLDOWN)==0) { + char *menuname; int xmax; - /* pull down menus */ uiBlockSetEmboss(block, UI_EMBOSSP); xmax= GetButStringLength("View"); - uiDefPulldownBut(block, dummy_viewmenu, CTX_wm_area(C), - "View", xco, yco-2, xmax-3, 24, ""); - xco+=XIC+xmax; + uiDefPulldownBut(block, image_viewmenu, NULL, "View", xco, yco-2, xmax-3, 24, ""); + xco+= xmax; + + if(show_uvedit) { + xmax= GetButStringLength("Select"); + uiDefPulldownBut(block, image_selectmenu, NULL, "Select", xco, yco-2, xmax-3, 24, ""); + xco+= xmax; + } + + menuname= (ibuf && (ibuf->userflags & IB_BITMAPDIRTY))? "Image*": "Image"; + xmax= GetButStringLength(menuname); + uiDefPulldownBut(block, image_imagemenu, NULL, menuname, xco, yco-2, xmax-3, 24, ""); + xco+= xmax; + + if(show_uvedit) { + xmax= GetButStringLength("UVs"); + uiDefPulldownBut(block, image_uvsmenu, NULL, "UVs", xco, yco-2, xmax-3, 24, ""); + xco+= xmax; + } } - + uiBlockSetEmboss(block, UI_EMBOSS); + /* image select */ +#if 0 + char naam[256]; + + /* This should not be a static var */ + static int headerbuttons_packdummy; + + headerbuttons_packdummy = 0; + + int allow_pin= (show_render)? 0: B_SIMAPIN; + + xco= 8 + std_libbuttons(block, xco, yco, allow_pin, &sima->pin, B_SIMABROWSE, ID_IM, 0, (ID *)ima, 0, &(sima->imanr), 0, 0, B_IMAGEDELETE, 0, 0); + + if(ima && !ELEM3(ima->source, IMA_SRC_SEQUENCE, IMA_SRC_MOVIE, IMA_SRC_VIEWER) && ima->ok) { + + if (ima->packedfile) { + headerbuttons_packdummy = 1; + } + if (ima->packedfile && ibuf && (ibuf->userflags & IB_BITMAPDIRTY)) + uiDefIconButBitI(block, TOG, 1, B_SIMA_REPACK, ICON_UGLYPACKAGE, xco,yco,XIC,YIC, &headerbuttons_packdummy, 0, 0, 0, 0, "Re-Pack this image as PNG"); + else + uiDefIconButBitI(block, TOG, 1, B_SIMAPACKIMA, ICON_PACKAGE, xco,yco,XIC,YIC, &headerbuttons_packdummy, 0, 0, 0, 0, "Pack/Unpack this image"); + + xco+= XIC+8; + } +#endif + + /* uv editing */ + if(show_uvedit) { + /* pivot */ + uiDefIconTextButS(block, ICONTEXTROW, B_NOP, ICON_ROTATE, + "Pivot: %t|Bounding Box Center %x0|Median Point %x3|2D Cursor %x1", + xco,yco,XIC+10,YIC, &ar->v2d.around, 0, 3.0, 0, 0, + "Rotation/Scaling Pivot (Hotkeys: Comma, Shift Comma, Period)"); + xco+= XIC + 18; + + /* selection modes */ + uiDefIconButBitS(block, TOG, UV_SYNC_SELECTION, B_REDR, ICON_EDIT, xco,yco,XIC,YIC, &scene->toolsettings->uv_flag, 0, 0, 0, 0, "Sync UV and Mesh Selection"); + xco+= XIC+8; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + uiBlockBeginAlign(block); + + uiDefIconButBitS(block, TOG, SCE_SELECT_VERTEX, B_REDR, ICON_VERTEXSEL, + xco,yco,XIC,YIC, &scene->selectmode, 1.0, 0.0, 0, 0, "Vertex select mode (Ctrl Tab 1)"); + uiDefIconButBitS(block, TOG, SCE_SELECT_FACE, B_REDR, ICON_FACESEL, + xco+=XIC,yco,XIC,YIC, &scene->selectmode, 1.0, 0.0, 0, 0, "Face select mode (Ctrl Tab 3)"); + + uiBlockEndAlign(block); + } + else { + uiBlockBeginAlign(block); + + uiDefIconButS(block, ROW, B_REDR, ICON_VERTEXSEL, + xco,yco,XIC,YIC, &scene->toolsettings->uv_selectmode, 1.0, UV_SELECT_VERTEX, 0, 0, "Vertex select mode"); + uiDefIconButS(block, ROW, B_REDR, ICON_FACESEL, + xco+=XIC,yco,XIC,YIC, &scene->toolsettings->uv_selectmode, 1.0, UV_SELECT_FACE, 0, 0, "Face select mode"); + uiDefIconButS(block, ROW, B_REDR, ICON_MESH, + xco+=XIC,yco,XIC,YIC, &scene->toolsettings->uv_selectmode, 1.0, UV_SELECT_ISLAND, 0, 0, "Island select mode"); + + uiBlockEndAlign(block); + + /* would use these if const's could go in strings + * SI_STICKY_LOC SI_STICKY_DISABLE SI_STICKY_VERTEX */ + but = uiDefIconTextButC(block, ICONTEXTROW, B_REDR, ICON_STICKY_UVS_LOC, + "Sticky UV Selection: %t|Disable%x1|Shared Location%x0|Shared Vertex%x2", + xco+=XIC+10,yco,XIC+10,YIC, &(sima->sticky), 0, 3.0, 0, 0, + "Sticky UV Selection (Hotkeys: Shift C, Alt C, Ctrl C)"); + } + + xco+= XIC + 16; + + /* snap options, identical to options in 3d view header */ + uiBlockBeginAlign(block); + + if (scene->snap_flag & SCE_SNAP) { + uiDefIconButBitS(block, TOG, SCE_SNAP, B_REDR, ICON_SNAP_GEO,xco,yco,XIC,YIC, &scene->snap_flag, 0, 0, 0, 0, "Use Snap or Grid (Shift Tab)."); + xco+= XIC; + uiDefButS(block, MENU, B_NOP, "Mode%t|Closest%x0|Center%x1|Median%x2",xco,yco,70,YIC, &scene->snap_target, 0, 0, 0, 0, "Snap Target Mode."); + xco+= 70; + } + else { + uiDefIconButBitS(block, TOG, SCE_SNAP, B_REDR, ICON_SNAP_GEAR,xco,yco,XIC,YIC, &scene->snap_flag, 0, 0, 0, 0, "Snap while Ctrl is held during transform (Shift Tab)."); + xco+= XIC; + } + + uiBlockEndAlign(block); + xco+= 10; + + /* uv layers */ + { + Object *obedit= CTX_data_edit_object(C); + char menustr[34*MAX_MTFACE]; + static int act; + + image_menu_uvlayers(obedit, menustr, &act); + + but = uiDefButI(block, MENU, B_NOP, menustr ,xco,yco,85,YIC, &act, 0, 0, 0, 0, "Active UV Layer for editing."); + // uiButSetFunc(but, do_image_buttons_set_uvlayer_callback, &act, NULL); + + xco+= 90; + } + } + + if(ima) { + RenderResult *rr; + + xco+= 8; + + /* render layers and passes */ + rr= BKE_image_get_renderresult(scene, ima); + if(rr) { + uiBlockBeginAlign(block); +#if 0 + uiblock_layer_pass_buttons(block, rr, &sima->iuser, B_REDR, xco, 0, 160); +#endif + uiBlockEndAlign(block); + xco+= 166; + } + + /* painting */ + uiDefIconButR(block, TOG, B_REDR, ICON_TPAINT_HLT, xco,yco,XIC,YIC, &spaceptr, "image_painting", 0, 0, 0, 0, 0, NULL); + xco+= XIC+8; + + /* image draw options */ + uiBlockBeginAlign(block); + uiDefIconButR(block, ROW, B_REDR, ICON_TEXTURE, xco,yco,XIC,YIC, &spaceptr, "draw_channels", 0, 0, 0, 0, 0, NULL); + xco+= XIC; + if(ibuf==NULL || ibuf->channels==4) { + uiDefIconButR(block, ROW, B_REDR, ICON_TRANSP_HLT, xco,yco,XIC,YIC, &spaceptr, "draw_channels", 0, 0, SI_USE_ALPHA, 0, 0, NULL); + xco+= XIC; + uiDefIconButR(block, ROW, B_REDR, ICON_DOT, xco,yco,XIC,YIC, &spaceptr, "draw_channels", 0, 0, SI_SHOW_ALPHA, 0, 0, NULL); + xco+= XIC; + } + if(ibuf) { + if(ibuf->zbuf || ibuf->zbuf_float || (ibuf->channels==1)) { + uiDefIconButR(block, ROW, B_REDR, ICON_SOLID, xco,yco,XIC,YIC, &spaceptr, "draw_channels", 0, 0, SI_SHOW_ZBUF, 0, 0, NULL); + xco+= XIC; + } + } + xco+= 8; + + /* record & play */ + uiBlockBeginAlign(block); + if(ima->type==IMA_TYPE_COMPOSITE) { + uiDefIconButO(block, BUT, "IMAGE_OT_record_composite", WM_OP_INVOKE_REGION_WIN, ICON_REC, xco, yco, XIC, YIC, NULL); // Record Composite + xco+= XIC; + } + if((ima->type==IMA_TYPE_COMPOSITE) || ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) { + uiDefIconButO(block, BUT, "IMAGE_OT_play_composite", WM_OP_INVOKE_REGION_WIN, ICON_PLAY, xco, yco, XIC, YIC, NULL); // PLAY + xco+= XIC; + } + uiBlockEndAlign(block); + xco+= 8; + } + + /* draw lock */ + uiDefIconButR(block, ICONTOG, 0, ICON_UNLOCKED, xco,yco,XIC,YIC, &spaceptr, "update_automatically", 0, 0, 0, 0, 0, NULL); + /* always as last */ UI_view2d_totRect_set(&ar->v2d, xco+XIC+80, ar->v2d.tot.ymax-ar->v2d.tot.ymin); @@ -130,4 +1329,3 @@ void image_header_buttons(const bContext *C, ARegion *ar) uiDrawBlock(C, block); } - diff --git a/source/blender/editors/space_image/image_intern.h b/source/blender/editors/space_image/image_intern.h index 42187447445..85d4c476150 100644 --- a/source/blender/editors/space_image/image_intern.h +++ b/source/blender/editors/space_image/image_intern.h @@ -25,15 +25,51 @@ * * ***** END GPL LICENSE BLOCK ***** */ + #ifndef ED_IMAGE_INTERN_H #define ED_IMAGE_INTERN_H /* internal exports only */ +struct bContext; +struct ARegion; +struct SpaceImage; +struct Object; +struct Image; +struct ImBuf; +struct wmOperatorType; +struct Scene; + +/* space_image.c */ +struct Image *get_space_image(struct SpaceImage *sima); +void set_space_image(struct SpaceImage *sima, struct Scene *scene, struct Object *obedit, struct Image *ima); + +struct ImBuf *get_space_image_buffer(struct SpaceImage *sima); +void get_space_image_size(struct SpaceImage *sima, int *width, int *height); +void get_space_image_aspect(struct SpaceImage *sima, float *aspx, float *aspy); +void get_space_image_zoom(struct SpaceImage *sima, struct ARegion *ar, float *zoomx, float *zoomy); +int get_space_image_show_render(struct SpaceImage *sima); +int get_space_image_show_paint(struct SpaceImage *sima); +int get_space_image_show_uvedit(struct SpaceImage *sima, struct Object *obedit); +int get_space_image_show_uvshadow(struct SpaceImage *sima, struct Object *obedit); /* image_header.c */ -void image_header_buttons(const bContext *C, ARegion *ar); +void image_header_buttons(const struct bContext *C, struct ARegion *ar); + +/* image_draw.c */ +void draw_image_main(struct SpaceImage *sima, struct ARegion *ar, struct Scene *scene); + +/* image_ops.c */ +void IMAGE_OT_view_all(struct wmOperatorType *ot); +void IMAGE_OT_view_pan(struct wmOperatorType *ot); +void IMAGE_OT_view_selected(struct wmOperatorType *ot); +void IMAGE_OT_view_zoom(struct wmOperatorType *ot); +void IMAGE_OT_view_zoom_in(struct wmOperatorType *ot); +void IMAGE_OT_view_zoom_out(struct wmOperatorType *ot); +void IMAGE_OT_view_zoom_ratio(struct wmOperatorType *ot); +/* uvedit_draw.c */ +void draw_uvedit_main(struct SpaceImage *sima, struct ARegion *ar, struct Scene *scene, struct Object *obedit); #endif /* ED_IMAGE_INTERN_H */ diff --git a/source/blender/editors/space_image/image_ops.c b/source/blender/editors/space_image/image_ops.c new file mode 100644 index 00000000000..0205f9b85cf --- /dev/null +++ b/source/blender/editors/space_image/image_ops.c @@ -0,0 +1,1178 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> +#include <stdlib.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_image_types.h" +#include "DNA_node_types.h" +#include "DNA_object_types.h" +#include "DNA_packedFile_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_userdef_types.h" +#include "DNA_windowmanager_types.h" + +#include "BKE_colortools.h" +#include "BKE_context.h" +#include "BKE_image.h" +#include "BKE_global.h" +#include "BKE_library.h" +#include "BKE_node.h" +#include "BKE_packedFile.h" +#include "BKE_screen.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" + +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "RE_pipeline.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_types.h" + +#include "ED_screen.h" +#include "ED_uvedit.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "image_intern.h" + +void imagespace_composite_flipbook(SpaceImage *sima, Scene *scene) +{ + ImBuf *ibuf; + int cfrao= scene->r.cfra; + int sfra, efra; + + if(sima->iuser.frames<2) + return; + if(scene->nodetree==NULL) + return; + + sfra= sima->iuser.sfra; + efra= sima->iuser.sfra + sima->iuser.frames-1; + scene->nodetree->test_break= NULL; // XXX blender_test_break; + + for(scene->r.cfra=sfra; scene->r.cfra<=efra; scene->r.cfra++) { + + // XXX set_timecursor(CFRA); + + BKE_image_all_free_anim_ibufs(CFRA); + ntreeCompositTagAnimated(scene->nodetree); + ntreeCompositExecTree(scene->nodetree, &scene->r, scene->r.cfra!=cfrao); /* 1 is no previews */ + + // XXX force_draw(0); + + ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser); + /* save memory in flipbooks */ + if(ibuf) + imb_freerectfloatImBuf(ibuf); + + // XXX if(blender_test_break()) + // XXX break; + } + scene->nodetree->test_break= NULL; + // XXX waitcursor(0); + + // XXX play_anim(0); + + // XXX allqueue(REDRAWNODE, 1); + // XXX allqueue(REDRAWIMAGE, 1); + + scene->r.cfra= cfrao; +} + +/******************** view navigation utilities *********************/ + +static void sima_zoom_set(SpaceImage *sima, ARegion *ar, float zoom) +{ + float oldzoom= sima->zoom; + int width, height; + + sima->zoom= zoom; + + if (sima->zoom > 0.1f && sima->zoom < 4.0f) + return; + + /* check zoom limits */ + get_space_image_size(sima, &width, &height); + + width *= sima->zoom; + height *= sima->zoom; + + if((width < 4) && (height < 4)) + sima->zoom= oldzoom; + else if((ar->winrct.xmax - ar->winrct.xmin) <= sima->zoom) + sima->zoom= oldzoom; + else if((ar->winrct.ymax - ar->winrct.ymin) <= sima->zoom) + sima->zoom= oldzoom; +} + +static void sima_zoom_set_factor(SpaceImage *sima, ARegion *ar, float zoomfac) +{ + sima_zoom_set(sima, ar, sima->zoom*zoomfac); +} + +static int space_image_main_area_poll(bContext *C) +{ + SpaceLink *slink= CTX_wm_space_data(C); + ARegion *ar= CTX_wm_region(C); + + if(slink && (slink->spacetype == SPACE_IMAGE)) + return (ar && ar->type->regionid == RGN_TYPE_WINDOW); + + return 0; +} + +/********************** view pan operator *********************/ + +typedef struct ViewPanData { + float x, y; + float xof, yof; +} ViewPanData; + +static void view_pan_init(bContext *C, wmOperator *op, wmEvent *event) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ViewPanData *vpd; + + op->customdata= vpd= MEM_callocN(sizeof(ViewPanData), "ImageViewPanData"); + WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR); + + vpd->x= event->x; + vpd->y= event->y; + vpd->xof= sima->xof; + vpd->yof= sima->yof; + + WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op); +} + +static void view_pan_exit(bContext *C, wmOperator *op, int cancel) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ViewPanData *vpd= op->customdata; + + if(cancel) { + sima->xof= vpd->xof; + sima->yof= vpd->yof; + ED_area_tag_redraw(CTX_wm_area(C)); + } + + WM_cursor_restore(CTX_wm_window(C)); + MEM_freeN(op->customdata); +} + +static int view_pan_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + float offset[2]; + + RNA_float_get_array(op->ptr, "offset", offset); + sima->xof += offset[0]; + sima->yof += offset[1]; + + ED_area_tag_redraw(CTX_wm_area(C)); + + /* XXX notifier? */ +#if 0 + if(image_preview_active(curarea, NULL, NULL)) { + /* recalculates new preview rect */ + scrarea_do_windraw(curarea); + image_preview_event(2); + } +#endif + + return OPERATOR_FINISHED; +} + +static int view_pan_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + view_pan_init(C, op, event); + return OPERATOR_RUNNING_MODAL; +} + +static int view_pan_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ViewPanData *vpd= op->customdata; + float offset[2]; + + switch(event->type) { + case MOUSEMOVE: + sima->xof= vpd->xof; + sima->yof= vpd->yof; + offset[0]= (vpd->x - event->x)/sima->zoom; + offset[1]= (vpd->y - event->y)/sima->zoom; + RNA_float_set_array(op->ptr, "offset", offset); + view_pan_exec(C, op); + break; + case MIDDLEMOUSE: + if(event->val==0) { + view_pan_exit(C, op, 0); + return OPERATOR_FINISHED; + } + break; + } + + return OPERATOR_RUNNING_MODAL; +} + +static int view_pan_cancel(bContext *C, wmOperator *op) +{ + view_pan_exit(C, op, 1); + return OPERATOR_CANCELLED; +} + +void IMAGE_OT_view_pan(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name= "View Pan"; + ot->idname= "IMAGE_OT_view_pan"; + + /* api callbacks */ + ot->exec= view_pan_exec; + ot->invoke= view_pan_invoke; + ot->modal= view_pan_modal; + ot->cancel= view_pan_cancel; + ot->poll= space_image_main_area_poll; + + /* properties */ + prop= RNA_def_property(ot->srna, "offset", PROP_FLOAT, PROP_VECTOR); + RNA_def_property_array(prop, 2); + RNA_def_property_ui_text(prop, "Offset", "Offset in floating point units, 1.0 is the width and height of the image."); +} + +/********************** view zoom operator *********************/ + +typedef struct ViewZoomData { + float x, y; + float zoom; +} ViewZoomData; + +static void view_zoom_init(bContext *C, wmOperator *op, wmEvent *event) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ViewZoomData *vpd; + + op->customdata= vpd= MEM_callocN(sizeof(ViewZoomData), "ImageViewZoomData"); + WM_cursor_modal(CTX_wm_window(C), BC_NSEW_SCROLLCURSOR); + + vpd->x= event->x; + vpd->y= event->y; + vpd->zoom= sima->zoom; + + WM_event_add_modal_handler(C, &CTX_wm_window(C)->handlers, op); +} + +static void view_zoom_exit(bContext *C, wmOperator *op, int cancel) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ViewZoomData *vpd= op->customdata; + + if(cancel) { + sima->zoom= vpd->zoom; + ED_area_tag_redraw(CTX_wm_area(C)); + } + + WM_cursor_restore(CTX_wm_window(C)); + MEM_freeN(op->customdata); +} + +static int view_zoom_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ARegion *ar= CTX_wm_region(C); + + sima_zoom_set_factor(sima, ar, RNA_float_get(op->ptr, "factor")); + + ED_area_tag_redraw(CTX_wm_area(C)); + + /* XXX notifier? */ +#if 0 + if(image_preview_active(curarea, NULL, NULL)) { + /* recalculates new preview rect */ + scrarea_do_windraw(curarea); + image_preview_event(2); + } +#endif + + return OPERATOR_FINISHED; +} + +static int view_zoom_invoke(bContext *C, wmOperator *op, wmEvent *event) +{ + view_zoom_init(C, op, event); + return OPERATOR_RUNNING_MODAL; +} + +static int view_zoom_modal(bContext *C, wmOperator *op, wmEvent *event) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ARegion *ar= CTX_wm_region(C); + ViewZoomData *vpd= op->customdata; + float factor; + + switch(event->type) { + case MOUSEMOVE: + factor= 1.0 + (vpd->x-event->x+vpd->y-event->y)/300.0f; + RNA_float_set(op->ptr, "factor", factor); + sima_zoom_set(sima, ar, vpd->zoom*factor); + ED_area_tag_redraw(CTX_wm_area(C)); + break; + case MIDDLEMOUSE: + if(event->val==0) { + view_zoom_exit(C, op, 0); + return OPERATOR_FINISHED; + } + break; + } + + return OPERATOR_RUNNING_MODAL; +} + +static int view_zoom_cancel(bContext *C, wmOperator *op) +{ + view_zoom_exit(C, op, 1); + return OPERATOR_CANCELLED; +} + +void IMAGE_OT_view_zoom(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name= "View Zoom"; + ot->idname= "IMAGE_OT_view_zoom"; + + /* api callbacks */ + ot->exec= view_zoom_exec; + ot->invoke= view_zoom_invoke; + ot->modal= view_zoom_modal; + ot->cancel= view_zoom_cancel; + ot->poll= space_image_main_area_poll; + + /* properties */ + prop= RNA_def_property(ot->srna, "factor", PROP_FLOAT, PROP_UNSIGNED); + RNA_def_property_ui_text(prop, "Factor", "Zoom factor, values higher than 1.0 zoom in, lower values zoom out."); +} + +/********************** view all operator *********************/ + +/* Updates the fields of the View2D member of the SpaceImage struct. + * Default behavior is to reset the position of the image and set the zoom to 1 + * If the image will not fit within the window rectangle, the zoom is adjusted */ + +static int view_all_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima; + ARegion *ar; + Scene *scene; + Object *obedit; + Image *ima; + ImBuf *ibuf; + float aspx, aspy, zoomx, zoomy, w, h; + int width, height; + + /* retrieve state */ + sima= (SpaceImage*)CTX_wm_space_data(C); + ar= CTX_wm_region(C); + scene= (Scene*)CTX_data_scene(C); + obedit= CTX_data_edit_object(C); + + ima= get_space_image(sima); + ibuf= get_space_image_buffer(sima); + get_space_image_size(sima, &width, &height); + get_space_image_aspect(sima, &aspx, &aspy); + + w= width*aspx; + h= height*aspy; + + /* check if the image will fit in the image with zoom==1 */ + width = ar->winrct.xmax - ar->winrct.xmin + 1; + height = ar->winrct.ymax - ar->winrct.ymin + 1; + + if((w >= width || h >= height) && (width > 0 && height > 0)) { + /* find the zoom value that will fit the image in the image space */ + zoomx= width/w; + zoomy= height/h; + sima_zoom_set(sima, ar, 1.0f/power_of_2(1/MIN2(zoomx, zoomy))); + } + else + sima_zoom_set(sima, ar, 1.0f); + + sima->xof= sima->yof= 0.0f; + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_view_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "View All"; + ot->idname= "IMAGE_OT_view_all"; + + /* api callbacks */ + ot->exec= view_all_exec; + ot->poll= space_image_main_area_poll; +} + +/********************** view selected operator *********************/ + +static int view_selected_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima; + ARegion *ar; + Scene *scene; + Object *obedit; + Image *ima; + ImBuf *ibuf; + float size, min[2], max[2], d[2]; + int width, height; + + /* retrieve state */ + sima= (SpaceImage*)CTX_wm_space_data(C); + ar= CTX_wm_region(C); + scene= (Scene*)CTX_data_scene(C); + obedit= CTX_data_edit_object(C); + + ima= get_space_image(sima); + ibuf= get_space_image_buffer(sima); + get_space_image_size(sima, &width, &height); + + /* get bounds */ + if(!ED_uvedit_minmax(scene, ima, obedit, min, max)) + return OPERATOR_CANCELLED; + + /* adjust offset and zoom */ + sima->xof= (int)(((min[0] + max[0])*0.5f - 0.5f)*width); + sima->yof= (int)(((min[1] + max[1])*0.5f - 0.5f)*height); + + d[0]= max[0] - min[0]; + d[1]= max[1] - min[1]; + size= 0.5*MAX2(d[0], d[1])*MAX2(width, height)/256.0f; + + if(size<=0.01) size= 0.01; + sima_zoom_set(sima, ar, 0.7/size); + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_view_selected(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "View Center"; + ot->idname= "IMAGE_OT_view_selected"; + + /* api callbacks */ + ot->exec= view_selected_exec; + ot->poll= ED_operator_uvedit; +} + +/********************** view zoom in/out operator *********************/ + +static int view_zoom_in_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ARegion *ar= CTX_wm_region(C); + + sima_zoom_set_factor(sima, ar, 1.25f); + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_view_zoom_in(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "View Zoom In"; + ot->idname= "IMAGE_OT_view_zoom_in"; + + /* api callbacks */ + ot->exec= view_zoom_in_exec; + ot->poll= space_image_main_area_poll; +} + +static int view_zoom_out_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ARegion *ar= CTX_wm_region(C); + + sima_zoom_set_factor(sima, ar, 0.8f); + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_view_zoom_out(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "View Zoom Out"; + ot->idname= "IMAGE_OT_view_zoom_out"; + + /* api callbacks */ + ot->exec= view_zoom_out_exec; + ot->poll= space_image_main_area_poll; +} + +/********************** view zoom ratio operator *********************/ + +static int view_zoom_ratio_exec(bContext *C, wmOperator *op) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + ARegion *ar= CTX_wm_region(C); + + sima_zoom_set(sima, ar, RNA_float_get(op->ptr, "ratio")); + + /* ensure pixel exact locations for draw */ + sima->xof= (int)sima->xof; + sima->yof= (int)sima->yof; + + /* XXX notifier? */ +#if 0 + if(image_preview_active(curarea, NULL, NULL)) { + /* recalculates new preview rect */ + scrarea_do_windraw(curarea); + image_preview_event(2); + } +#endif + + ED_area_tag_redraw(CTX_wm_area(C)); + + return OPERATOR_FINISHED; +} + +void IMAGE_OT_view_zoom_ratio(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name= "View Zoom Ratio"; + ot->idname= "IMAGE_OT_view_zoom_ratio"; + + /* api callbacks */ + ot->exec= view_zoom_ratio_exec; + ot->poll= space_image_main_area_poll; + + /* properties */ + prop= RNA_def_property(ot->srna, "ratio", PROP_FLOAT, PROP_UNSIGNED); + RNA_def_property_ui_text(prop, "Ratio", "Zoom ratio, 1.0 is 1:1, higher is zoomed in, lower is zoomed out."); +} + +/* Image functions */ + +#if 0 +static void load_image_filesel(SpaceImage *sima, Scene *scene, Object *obedit, char *str) /* called from fileselect */ +{ + Image *ima= NULL; + + ima= BKE_add_image_file(str, scene->r.cfra); + if(ima) { + BKE_image_signal(ima, &sima->iuser, IMA_SIGNAL_RELOAD); + set_space_image(sima, scene, obedit, ima); + } + // XXX BIF_undo_push("Load image UV"); + // XXX allqueue(REDRAWIMAGE, 0); +} + +static void replace_image_filesel(SpaceImage *sima, char *str) /* called from fileselect */ +{ + if (!sima->image) + return; + + BLI_strncpy(sima->image->name, str, sizeof(sima->image->name)-1); /* we cant do much if the str is longer then 240 :/ */ + BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_RELOAD); + // XXX BIF_undo_push("Replace image UV"); + // XXX allqueue(REDRAWIMAGE, 0); + // XXX allqueue(REDRAWVIEW3D, 0); +} +#endif + +static void save_image_doit(SpaceImage *sima, Scene *scene, char *name) +{ + Image *ima= get_space_image(sima); + ImBuf *ibuf= get_space_image_buffer(sima); + int len; + char str[FILE_MAXDIR+FILE_MAXFILE]; + + if (ibuf) { + BLI_strncpy(str, name, sizeof(str)); + + BLI_convertstringcode(str, G.sce); + BLI_convertstringframe(str, scene->r.cfra); + + + if(scene->r.scemode & R_EXTENSION) { + BKE_add_image_extension(scene, str, sima->imtypenr); + BKE_add_image_extension(scene, name, sima->imtypenr); + } + + if (1) { // XXX saveover(str)) { + + /* enforce user setting for RGB or RGBA, but skip BW */ + if(scene->r.planes==32) + ibuf->depth= 32; + else if(scene->r.planes==24) + ibuf->depth= 24; + + // XXX waitcursor(1); + if(sima->imtypenr==R_MULTILAYER) { + RenderResult *rr= BKE_image_get_renderresult(scene, ima); + if(rr) { + RE_WriteRenderResult(rr, str, scene->r.quality); + + BLI_strncpy(ima->name, name, sizeof(ima->name)); + BLI_strncpy(ibuf->name, str, sizeof(ibuf->name)); + + /* should be function? nevertheless, saving only happens here */ + for(ibuf= ima->ibufs.first; ibuf; ibuf= ibuf->next) + ibuf->userflags &= ~IB_BITMAPDIRTY; + + } + else; // XXX error("Did not write, no Multilayer Image"); + } + else if (BKE_write_ibuf(scene, ibuf, str, sima->imtypenr, scene->r.subimtype, scene->r.quality)) { + BLI_strncpy(ima->name, name, sizeof(ima->name)); + BLI_strncpy(ibuf->name, str, sizeof(ibuf->name)); + + ibuf->userflags &= ~IB_BITMAPDIRTY; + + /* change type? */ + if( ELEM(ima->source, IMA_SRC_GENERATED, IMA_SRC_VIEWER)) { + ima->source= IMA_SRC_FILE; + ima->type= IMA_TYPE_IMAGE; + } + if(ima->type==IMA_TYPE_R_RESULT) + ima->type= IMA_TYPE_IMAGE; + + /* name image as how we saved it */ + len= strlen(str); + while (len > 0 && str[len - 1] != '/' && str[len - 1] != '\\') len--; + rename_id(&ima->id, str+len); + } + else { + ; // XXX error("Couldn't write image: %s", str); + } + + // XXX allqueue(REDRAWHEADERS, 0); + // XXX allqueue(REDRAWBUTSSHADING, 0); + + // XXX waitcursor(0); + } + } +} + +void open_image_sima(SpaceImage *sima, short imageselect) +{ + char name[FILE_MAXDIR+FILE_MAXFILE]; + + if(sima->image) + BLI_strncpy(name, sima->image->name, sizeof(name)); + else + BLI_strncpy(name, U.textudir, sizeof(name)); + + if(imageselect) + ; // XXX activate_imageselect(FILE_SPECIAL, "Open Image", name, load_image_filesel); + else + ; // XXX activate_fileselect(FILE_SPECIAL, "Open Image", name, load_image_filesel); +} + +void replace_image_sima(SpaceImage *sima, short imageselect) +{ + char name[FILE_MAXDIR+FILE_MAXFILE]; + + if(sima->image) + BLI_strncpy(name, sima->image->name, sizeof(name)); + else + BLI_strncpy(name, U.textudir, sizeof(name)); + + if(imageselect) + ; // XXX activate_imageselect(FILE_SPECIAL, "Replace Image", name, replace_image_filesel); + else + ; // XXX activate_fileselect(FILE_SPECIAL, "Replace Image", name, replace_image_filesel); +} + + +static char *filesel_imagetype_string(Image *ima) +{ + char *strp, *str= MEM_callocN(14*32, "menu for filesel"); + + strp= str; + str += sprintf(str, "Save Image as: %%t|"); + str += sprintf(str, "Targa %%x%d|", R_TARGA); + str += sprintf(str, "Targa Raw %%x%d|", R_RAWTGA); + str += sprintf(str, "PNG %%x%d|", R_PNG); + str += sprintf(str, "BMP %%x%d|", R_BMP); + str += sprintf(str, "Jpeg %%x%d|", R_JPEG90); + str += sprintf(str, "Iris %%x%d|", R_IRIS); + if(G.have_libtiff) + str += sprintf(str, "Tiff %%x%d|", R_TIFF); + str += sprintf(str, "Radiance HDR %%x%d|", R_RADHDR); + str += sprintf(str, "Cineon %%x%d|", R_CINEON); + str += sprintf(str, "DPX %%x%d|", R_DPX); +#ifdef WITH_OPENEXR + str += sprintf(str, "OpenEXR %%x%d|", R_OPENEXR); + /* saving sequences of multilayer won't work, they copy buffers */ + if(ima->source==IMA_SRC_SEQUENCE && ima->type==IMA_TYPE_MULTILAYER); + else str += sprintf(str, "MultiLayer %%x%d|", R_MULTILAYER); +#endif + return strp; +} + +/* always opens fileselect */ +void save_as_image_sima(SpaceImage *sima, Scene *scene) +{ + Image *ima = sima->image; + ImBuf *ibuf= get_space_image_buffer(sima); + char name[FILE_MAXDIR+FILE_MAXFILE]; + + if (ima) { + strcpy(name, ima->name); + + if (ibuf) { + char *strp; + + strp= filesel_imagetype_string(ima); + + /* cant save multilayer sequence, ima->rr isn't valid for a specific frame */ + if(ima->rr && !(ima->source==IMA_SRC_SEQUENCE && ima->type==IMA_TYPE_MULTILAYER)) + sima->imtypenr= R_MULTILAYER; + else if(ima->type==IMA_TYPE_R_RESULT) + sima->imtypenr= scene->r.imtype; + else sima->imtypenr= BKE_ftype_to_imtype(ibuf->ftype); + + // XXX activate_fileselect_menu(FILE_SPECIAL, "Save Image", name, strp, &sima->imtypenr, save_image_doit); + } + } +} + +/* if exists, saves over without fileselect */ +void save_image_sima(SpaceImage *sima, Scene *scene) +{ + Image *ima = get_space_image(sima); + ImBuf *ibuf= get_space_image_buffer(sima); + char name[FILE_MAXDIR+FILE_MAXFILE]; + + if (ima) { + strcpy(name, ima->name); + + if (ibuf) { + if (BLI_exists(ibuf->name)) { + if(BKE_image_get_renderresult(scene, ima)) + sima->imtypenr= R_MULTILAYER; + else + sima->imtypenr= BKE_ftype_to_imtype(ibuf->ftype); + + save_image_doit(sima, scene, ibuf->name); + } + else + save_as_image_sima(sima, scene); + } + } +} + +void save_image_sequence_sima(SpaceImage *sima) +{ + ImBuf *ibuf; + int tot= 0; + char di[FILE_MAX], fi[FILE_MAX]; + + if(sima->image==NULL) + return; + if(sima->image->source!=IMA_SRC_SEQUENCE) + return; + if(sima->image->type==IMA_TYPE_MULTILAYER) { + // XXX error("Cannot save Multilayer Sequences"); + return; + } + + /* get total */ + for(ibuf= sima->image->ibufs.first; ibuf; ibuf= ibuf->next) + if(ibuf->userflags & IB_BITMAPDIRTY) + tot++; + + if(tot==0) { + // XXX notice("No Images have been changed"); + return; + } + /* get a filename for menu */ + for(ibuf= sima->image->ibufs.first; ibuf; ibuf= ibuf->next) + if(ibuf->userflags & IB_BITMAPDIRTY) + break; + + BLI_strncpy(di, ibuf->name, FILE_MAX); + BLI_splitdirstring(di, fi); + + sprintf(fi, "%d Image(s) will be saved in %s", tot, di); + if(1) { // XXX okee(fi)) { + + for(ibuf= sima->image->ibufs.first; ibuf; ibuf= ibuf->next) { + if(ibuf->userflags & IB_BITMAPDIRTY) { + char name[FILE_MAX]; + BLI_strncpy(name, ibuf->name, sizeof(name)); + + BLI_convertstringcode(name, G.sce); + + if(0 == IMB_saveiff(ibuf, name, IB_rect | IB_zbuf | IB_zbuffloat)) { + // XXX error("Could not write image", name); + break; + } + printf("Saved: %s\n", ibuf->name); + ibuf->userflags &= ~IB_BITMAPDIRTY; + } + } + } +} + +void reload_image_sima(SpaceImage *sima) +{ + if (sima ) { + BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_RELOAD); + /* set_space_image(sima, scene, obedit, NULL); - do we really need this? */ + } + + // XXX allqueue(REDRAWIMAGE, 0); + // XXX allqueue(REDRAWVIEW3D, 0); + // XXX BIF_preview_changed(ID_TE); +} + +void new_image_sima(SpaceImage *sima, Scene *scene, Object *obedit) +{ + static int width= 1024, height= 1024; + static short uvtestgrid= 0; + static int floatbuf=0; + static float color[] = {0, 0, 0, 1}; + char name[22]; + Image *ima; + + strcpy(name, "Untitled"); + +#if 0 + add_numbut(0, TEX, "Name:", 0, 21, name, NULL); + add_numbut(1, NUM|INT, "Width:", 1, 16384, &width, NULL); + add_numbut(2, NUM|INT, "Height:", 1, 16384, &height, NULL); + add_numbut(3, COL, "", 0, 0, &color, NULL); + add_numbut(4, NUM|FLO, "Alpha:", 0.0, 1.0, &color[3], NULL); + add_numbut(5, TOG|SHO, "UV Test Grid", 0, 0, &uvtestgrid, NULL); + add_numbut(6, TOG|INT, "32 bit Float", 0, 0, &floatbuf, NULL); + if (!do_clever_numbuts("New Image", 7, REDRAW)) + return; +#endif + + ima = BKE_add_image_size(width, height, name, floatbuf, uvtestgrid, color); + set_space_image(sima, scene, obedit, ima); + BKE_image_signal(sima->image, &sima->iuser, IMA_SIGNAL_USER_NEW_IMAGE); + // XXX BIF_undo_push("Add image"); + + // XXX allqueue(REDRAWIMAGE, 0); + // XXX allqueue(REDRAWVIEW3D, 0); +} + +void pack_image_sima(SpaceImage *sima) +{ + Image *ima = sima->image; + + if (ima) { + if(ima->source!=IMA_SRC_SEQUENCE && ima->source!=IMA_SRC_MOVIE) { + if (ima->packedfile) { + if (G.fileflags & G_AUTOPACK) + if (1) // XXX okee("Disable AutoPack?")) + G.fileflags &= ~G_AUTOPACK; + + if ((G.fileflags & G_AUTOPACK) == 0) { + unpackImage(ima, PF_ASK); + // XXX BIF_undo_push("Unpack image"); + } + } + else { + ImBuf *ibuf= get_space_image_buffer(sima); + if (ibuf && (ibuf->userflags & IB_BITMAPDIRTY)) { + if(1) // XXX okee("Can't pack painted image. Use Repack as PNG?")) + BKE_image_memorypack(ima); + } + else { + ima->packedfile = newPackedFile(ima->name); + // XXX BIF_undo_push("Pack image"); + } + } + + // XXX allqueue(REDRAWBUTSSHADING, 0); + // XXX allqueue(REDRAWHEADERS, 0); + } + } +} + +/* XXX notifier? */ +#if 0 +/* goes over all ImageUsers, and sets frame numbers if auto-refresh is set */ +void BIF_image_update_frame(void) +{ + Tex *tex; + + /* texture users */ + for(tex= G.main->tex.first; tex; tex= tex->id.next) { + if(tex->type==TEX_IMAGE && tex->ima) + if(ELEM(tex->ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) + if(tex->iuser.flag & IMA_ANIM_ALWAYS) + BKE_image_user_calc_imanr(&tex->iuser, scene->r.cfra, 0); + + } + /* image window, compo node users */ + if(G.curscreen) { + ScrArea *sa; + for(sa= G.curscreen->areabase.first; sa; sa= sa->next) { + if(sa->spacetype==SPACE_VIEW3D) { + View3D *v3d= sa->spacedata.first; + if(v3d->bgpic) + if(v3d->bgpic->iuser.flag & IMA_ANIM_ALWAYS) + BKE_image_user_calc_imanr(&v3d->bgpic->iuser, scene->r.cfra, 0); + } + else if(sa->spacetype==SPACE_IMAGE) { + SpaceImage *sima= sa->spacedata.first; + if(sima->iuser.flag & IMA_ANIM_ALWAYS) + BKE_image_user_calc_imanr(&sima->iuser, scene->r.cfra, 0); + } + else if(sa->spacetype==SPACE_NODE) { + SpaceNode *snode= sa->spacedata.first; + if((snode->treetype==NTREE_COMPOSIT) && (snode->nodetree)) { + bNode *node; + for(node= snode->nodetree->nodes.first; node; node= node->next) { + if(node->id && node->type==CMP_NODE_IMAGE) { + Image *ima= (Image *)node->id; + ImageUser *iuser= node->storage; + if(ELEM(ima->source, IMA_SRC_MOVIE, IMA_SRC_SEQUENCE)) + if(iuser->flag & IMA_ANIM_ALWAYS) + BKE_image_user_calc_imanr(iuser, scene->r.cfra, 0); + } + } + } + } + } + } +} +#endif + +void image_pixel_aspect(Image *image, float *x, float *y) +{ + *x = *y = 1.0; + + if( (image == NULL) || + (image->type == IMA_TYPE_R_RESULT) || + (image->type == IMA_TYPE_COMPOSITE) || + (image->tpageflag & IMA_TILES) || + (image->aspx==0.0 || image->aspy==0.0) + ) { + return; + } + + /* x is always 1 */ + *y = image->aspy / image->aspx; +} + +void image_final_aspect(Image *image, float *x, float *y) +{ + *x = *y = 1.0; + + if( (image == NULL) || + (image->type == IMA_TYPE_R_RESULT) || + (image->type == IMA_TYPE_COMPOSITE) || + (image->tpageflag & IMA_TILES) || + (image->aspx==0.0 || image->aspy==0.0) + ) { + return; + } else { + ImBuf *ibuf= BKE_image_get_ibuf(image, NULL); + if (ibuf && ibuf->x && ibuf->y) { + *y = (image->aspy * ibuf->y) / (image->aspx * ibuf->x); + } else { + /* x is always 1 */ + *y = image->aspy / image->aspx; + } + } +} + +void sima_sample_color(SpaceImage *sima) +{ + ImBuf *ibuf= get_space_image_buffer(sima); + float fx, fy; + short mval[2], mvalo[2], firsttime=1; + + if(ibuf==NULL) + return; + + // XXX calc_image_view(sima, 'f'); + // XXX getmouseco_areawin(mvalo); + + while(0) { // XXX get_mbut() & L_MOUSE) { + + // XXX getmouseco_areawin(mval); + if(mval[0]!=mvalo[0] || mval[1]!=mvalo[1] || firsttime) { + firsttime= 0; + // XXX areamouseco_to_ipoco(G.v2d, mval, &fx, &fy); + + if(fx>=0.0 && fy>=0.0 && fx<1.0 && fy<1.0) { + float *fp= NULL, *zpf= NULL; + float vec[3]; + int *zp= NULL; + char *cp= NULL; + + int x= (int) (fx*ibuf->x); + int y= (int) (fy*ibuf->y); + + if(x>=ibuf->x) x= ibuf->x-1; + if(y>=ibuf->y) y= ibuf->y-1; + + if(ibuf->rect) + cp= (char *)(ibuf->rect + y*ibuf->x + x); + if(ibuf->zbuf) + zp= ibuf->zbuf + y*ibuf->x + x; + if(ibuf->zbuf_float) + zpf= ibuf->zbuf_float + y*ibuf->x + x; + if(ibuf->rect_float) + fp= (ibuf->rect_float + (ibuf->channels)*(y*ibuf->x + x)); + + if(fp==NULL) { + fp= vec; + vec[0]= (float)cp[0]/255.0f; + vec[1]= (float)cp[1]/255.0f; + vec[2]= (float)cp[2]/255.0f; + } + + if(sima->cumap) { + + if(ibuf->channels==4) { + if(0) { // XXX G.qual & LR_CTRLKEY) { + curvemapping_set_black_white(sima->cumap, NULL, fp); + curvemapping_do_ibuf(sima->cumap, ibuf); + } + else if(0) { // XXX G.qual & LR_SHIFTKEY) { + curvemapping_set_black_white(sima->cumap, fp, NULL); + curvemapping_do_ibuf(sima->cumap, ibuf); + } + } + } + +#if 0 + { + ScrArea *sa, *cur= curarea; + + node_curvemap_sample(fp); /* sends global to node editor */ + for(sa= G.curscreen->areabase.first; sa; sa= sa->next) { + if(sa->spacetype==SPACE_NODE) { + areawinset(sa->win); + scrarea_do_windraw(sa); + } + } + node_curvemap_sample(NULL); /* clears global in node editor */ + curarea= cur; + } + + areawinset(curarea->win); + scrarea_do_windraw(curarea); + myortho2(-0.375, curarea->winx-0.375, -0.375, curarea->winy-0.375); + glLoadIdentity(); + + sima_show_info(ibuf->channels, x, y, cp, (ibuf->rect_float)?fp:NULL, zp, zpf); + + screen_swapbuffers(); +#endif + + } + } + // XXX BIF_wait_for_statechange(); + } + + // XXX scrarea_queue_winredraw(curarea); +} + +void mouseco_to_curtile(SpaceImage *sima, struct Object *obedit) +{ + float fx, fy; + short mval[2]; + int show_uvedit; + + show_uvedit= get_space_image_show_uvedit(sima, obedit); + if(!show_uvedit) return; + + if(sima->image && sima->image->tpageflag & IMA_TILES) { + + sima->flag |= SI_EDITTILE; + + while(0) { // XXX get_mbut()&L_MOUSE) { + + // XXX calc_image_view(sima, 'f'); + + // XXX getmouseco_areawin(mval); + // XXX areamouseco_to_ipoco(G.v2d, mval, &fx, &fy); + + if(fx>=0.0 && fy>=0.0 && fx<1.0 && fy<1.0) { + + fx= (fx)*sima->image->xrep; + fy= (fy)*sima->image->yrep; + + mval[0]= fx; + mval[1]= fy; + + sima->curtile= mval[1]*sima->image->xrep + mval[0]; + } + + // XXX scrarea_do_windraw(curarea); + // XXX screen_swapbuffers(); + } + + sima->flag &= ~SI_EDITTILE; + + // XXX image_set_tile(sima, 2); + + // XXX allqueue(REDRAWVIEW3D, 0); + // XXX scrarea_queue_winredraw(curarea); + } +} + +/* Could be used for other 2D views also */ +void mouseco_to_cursor_sima(void) +{ + // XXX short mval[2]; + // XXX getmouseco_areawin(mval); + // XXX areamouseco_to_ipoco(G.v2d, mval, &G.v2d->cursor[0], &G.v2d->cursor[1]); + // XXX scrarea_queue_winredraw(curarea); +} + diff --git a/source/blender/editors/space_image/image_panels.c b/source/blender/editors/space_image/image_panels.c new file mode 100644 index 00000000000..39ceaa799e7 --- /dev/null +++ b/source/blender/editors/space_image/image_panels.c @@ -0,0 +1,631 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#if 0 + +/* ************ panel stuff ************* */ + +/* this function gets the values for cursor and vertex number buttons */ +static void image_transform_but_attr(int *imx, int *imy, int *step, int *digits) /*, float *xcoord, float *ycoord)*/ +{ + ImBuf *ibuf= imagewindow_get_ibuf(G.sima); + if(ibuf) { + *imx= ibuf->x; + *imy= ibuf->y; + } + + if (G.sima->flag & SI_COORDFLOATS) { + *step= 1; + *digits= 3; + } + else { + *step= 100; + *digits= 2; + } +} + + +/* is used for both read and write... */ +void image_editvertex_buts(uiBlock *block) +{ + static float ocent[2]; + float cent[2]= {0.0, 0.0}; + int imx= 256, imy= 256; + int nactive= 0, step, digits; + EditMesh *em = G.editMesh; + EditFace *efa; + MTFace *tf; + + if( is_uv_tface_editing_allowed_silent()==0 ) return; + + image_transform_but_attr(&imx, &imy, &step, &digits); + + for (efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (simaFaceDraw_Check(efa, tf)) { + + if (simaUVSel_Check(efa, tf, 0)) { + cent[0]+= tf->uv[0][0]; + cent[1]+= tf->uv[0][1]; + nactive++; + } + if (simaUVSel_Check(efa, tf, 1)) { + cent[0]+= tf->uv[1][0]; + cent[1]+= tf->uv[1][1]; + nactive++; + } + if (simaUVSel_Check(efa, tf, 2)) { + cent[0]+= tf->uv[2][0]; + cent[1]+= tf->uv[2][1]; + nactive++; + } + if (efa->v4 && simaUVSel_Check(efa, tf, 3)) { + cent[0]+= tf->uv[3][0]; + cent[1]+= tf->uv[3][1]; + nactive++; + } + } + } + + if(block) { // do the buttons + if (nactive) { + ocent[0]= cent[0]/nactive; + ocent[1]= cent[1]/nactive; + if (G.sima->flag & SI_COORDFLOATS) { + } else { + ocent[0] *= imx; + ocent[1] *= imy; + } + + //uiBlockBeginAlign(block); + if(nactive==1) { + uiDefButF(block, NUM, B_TRANS_IMAGE, "Vertex X:", 10, 10, 145, 19, &ocent[0], -10*imx, 10.0*imx, step, digits, ""); + uiDefButF(block, NUM, B_TRANS_IMAGE, "Vertex Y:", 165, 10, 145, 19, &ocent[1], -10*imy, 10.0*imy, step, digits, ""); + } + else { + uiDefButF(block, NUM, B_TRANS_IMAGE, "Median X:", 10, 10, 145, 19, &ocent[0], -10*imx, 10.0*imx, step, digits, ""); + uiDefButF(block, NUM, B_TRANS_IMAGE, "Median Y:", 165, 10, 145, 19, &ocent[1], -10*imy, 10.0*imy, step, digits, ""); + } + //uiBlockEndAlign(block); + } + } + else { // apply event + float delta[2]; + + cent[0]= cent[0]/nactive; + cent[1]= cent[1]/nactive; + + if (G.sima->flag & SI_COORDFLOATS) { + delta[0]= ocent[0]-cent[0]; + delta[1]= ocent[1]-cent[1]; + } + else { + delta[0]= ocent[0]/imx - cent[0]; + delta[1]= ocent[1]/imy - cent[1]; + } + + for (efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if (simaFaceDraw_Check(efa, tf)) { + if (simaUVSel_Check(efa, tf, 0)) { + tf->uv[0][0]+= delta[0]; + tf->uv[0][1]+= delta[1]; + } + if (simaUVSel_Check(efa, tf, 1)) { + tf->uv[1][0]+= delta[0]; + tf->uv[1][1]+= delta[1]; + } + if (simaUVSel_Check(efa, tf, 2)) { + tf->uv[2][0]+= delta[0]; + tf->uv[2][1]+= delta[1]; + } + if (efa->v4 && simaUVSel_Check(efa, tf, 3)) { + tf->uv[3][0]+= delta[0]; + tf->uv[3][1]+= delta[1]; + } + } + } + + allqueue(REDRAWVIEW3D, 0); + allqueue(REDRAWIMAGE, 0); + } +} + + +/* is used for both read and write... */ +void image_editcursor_buts(uiBlock *block) +{ + static float ocent[2]; + int imx= 256, imy= 256; + int step, digits; + + if( is_uv_tface_editing_allowed_silent()==0 ) return; + + image_transform_but_attr(&imx, &imy, &step, &digits); + + if(block) { // do the buttons + ocent[0]= G.v2d->cursor[0]; + ocent[1]= G.v2d->cursor[1]; + if (G.sima->flag & SI_COORDFLOATS) { + } else { + ocent[0] *= imx; + ocent[1] *= imy; + } + + uiBlockBeginAlign(block); + uiDefButF(block, NUM, B_CURSOR_IMAGE, "Cursor X:", 165, 120, 145, 19, &ocent[0], -10*imx, 10.0*imx, step, digits, ""); + uiDefButF(block, NUM, B_CURSOR_IMAGE, "Cursor Y:", 165, 100, 145, 19, &ocent[1], -10*imy, 10.0*imy, step, digits, ""); + uiBlockEndAlign(block); + } + else { // apply event + if (G.sima->flag & SI_COORDFLOATS) { + G.v2d->cursor[0]= ocent[0]; + G.v2d->cursor[1]= ocent[1]; + } + else { + G.v2d->cursor[0]= ocent[0]/imx; + G.v2d->cursor[1]= ocent[1]/imy; + } + allqueue(REDRAWIMAGE, 0); + } +} + + +void image_info(Image *ima, ImBuf *ibuf, char *str) +{ + int ofs= 0; + + str[0]= 0; + + if(ima==NULL) return; + if(ibuf==NULL) { + sprintf(str, "Can not get an image"); + return; + } + + if(ima->source==IMA_SRC_MOVIE) { + ofs= sprintf(str, "Movie "); + if(ima->anim) + ofs+= sprintf(str+ofs, "%d frs", IMB_anim_get_duration(ima->anim)); + } + else + ofs= sprintf(str, "Image "); + + ofs+= sprintf(str+ofs, ": size %d x %d,", ibuf->x, ibuf->y); + + if(ibuf->rect_float) { + if(ibuf->channels!=4) { + sprintf(str+ofs, "%d float channel(s)", ibuf->channels); + } + else if(ibuf->depth==32) + strcat(str, " RGBA float"); + else + strcat(str, " RGB float"); + } + else { + if(ibuf->depth==32) + strcat(str, " RGBA byte"); + else + strcat(str, " RGB byte"); + } + if(ibuf->zbuf || ibuf->zbuf_float) + strcat(str, " + Z"); + +} + +static void image_panel_properties(short cntrl) // IMAGE_HANDLER_PROPERTIES +{ + uiBlock *block; + + block= uiNewBlock(&curarea->uiblocks, "image_panel_properties", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(IMAGE_HANDLER_PROPERTIES); // for close and esc + if(uiNewPanel(curarea, block, "Image Properties", "Image", 10, 10, 318, 204)==0) + return; + + /* note, it draws no bottom half in facemode, for vertex buttons */ + uiblock_image_panel(block, &G.sima->image, &G.sima->iuser, B_REDR, B_REDR); + image_editvertex_buts(block); +} + +static void image_panel_game_properties(short cntrl) // IMAGE_HANDLER_GAME_PROPERTIES +{ + ImBuf *ibuf= BKE_image_get_ibuf(G.sima->image, &G.sima->iuser); + uiBlock *block; + + block= uiNewBlock(&curarea->uiblocks, "image_panel_game_properties", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(IMAGE_HANDLER_GAME_PROPERTIES); // for close and esc + if(uiNewPanel(curarea, block, "Real-time Properties", "Image", 10, 10, 318, 204)==0) + return; + + if (ibuf) { + char str[128]; + + image_info(G.sima->image, ibuf, str); + uiDefBut(block, LABEL, B_NOP, str, 10,180,300,19, 0, 0, 0, 0, 0, ""); + + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG, IMA_TWINANIM, B_TWINANIM, "Anim", 10,150,140,19, &G.sima->image->tpageflag, 0, 0, 0, 0, "Toggles use of animated texture"); + uiDefButS(block, NUM, B_TWINANIM, "Start:", 10,130,140,19, &G.sima->image->twsta, 0.0, 128.0, 0, 0, "Displays the start frame of an animated texture"); + uiDefButS(block, NUM, B_TWINANIM, "End:", 10,110,140,19, &G.sima->image->twend, 0.0, 128.0, 0, 0, "Displays the end frame of an animated texture"); + uiDefButS(block, NUM, B_NOP, "Speed", 10,90,140,19, &G.sima->image->animspeed, 1.0, 100.0, 0, 0, "Displays Speed of the animation in frames per second"); + uiBlockEndAlign(block); + + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG, IMA_TILES, B_SIMAGETILE, "Tiles", 160,150,140,19, &G.sima->image->tpageflag, 0, 0, 0, 0, "Toggles use of tilemode for faces (Shift LMB to pick the tile for selected faces)"); + uiDefButS(block, NUM, B_SIMA_REDR_IMA_3D, "X:", 160,130,70,19, &G.sima->image->xrep, 1.0, 16.0, 0, 0, "Sets the degree of repetition in the X direction"); + uiDefButS(block, NUM, B_SIMA_REDR_IMA_3D, "Y:", 230,130,70,19, &G.sima->image->yrep, 1.0, 16.0, 0, 0, "Sets the degree of repetition in the Y direction"); + uiBlockBeginAlign(block); + + uiBlockBeginAlign(block); + uiDefButBitS(block, TOG, IMA_CLAMP_U, B_SIMA3DVIEWDRAW, "ClampX", 160,100,70,19, &G.sima->image->tpageflag, 0, 0, 0, 0, "Disable texture repeating horizontaly"); + uiDefButBitS(block, TOG, IMA_CLAMP_V, B_SIMA3DVIEWDRAW, "ClampY", 230,100,70,19, &G.sima->image->tpageflag, 0, 0, 0, 0, "Disable texture repeating vertically"); + uiBlockEndAlign(block); + } +} + +static void image_panel_view_properties(short cntrl) // IMAGE_HANDLER_VIEW_PROPERTIES +{ + uiBlock *block; + + block= uiNewBlock(&curarea->uiblocks, "image_view_properties", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(IMAGE_HANDLER_VIEW_PROPERTIES); // for close and esc + if(uiNewPanel(curarea, block, "View Properties", "Image", 10, 10, 318, 204)==0) + return; + + + uiDefButBitI(block, TOG, SI_DRAW_TILE, B_REDR, "Repeat Image", 10,160,140,19, &G.sima->flag, 0, 0, 0, 0, "Repeat/Tile the image display"); + uiDefButBitI(block, TOG, SI_COORDFLOATS, B_REDR, "Normalized Coords", 165,160,145,19, &G.sima->flag, 0, 0, 0, 0, "Display coords from 0.0 to 1.0 rather then in pixels"); + + + if (G.sima->image) { + uiDefBut(block, LABEL, B_NOP, "Image Display:", 10,140,140,19, 0, 0, 0, 0, 0, ""); + uiBlockBeginAlign(block); + uiDefButF(block, NUM, B_REDR, "AspX:", 10,120,140,19, &G.sima->image->aspx, 0.1, 5000.0, 100, 0, "X Display Aspect for this image, does not affect renderingm 0 disables."); + uiDefButF(block, NUM, B_REDR, "AspY:", 10,100,140,19, &G.sima->image->aspy, 0.1, 5000.0, 100, 0, "X Display Aspect for this image, does not affect rendering 0 disables."); + uiBlockEndAlign(block); + } + + + if (EM_texFaceCheck()) { + uiDefBut(block, LABEL, B_NOP, "Draw Type:", 10, 80,120,19, 0, 0, 0, 0, 0, ""); + uiBlockBeginAlign(block); + uiDefButC(block, ROW, B_REDR, "Outline", 10,60,58,19, &G.sima->dt_uv, 0.0, SI_UVDT_OUTLINE, 0, 0, "Outline Wire UV drawtype"); + uiDefButC(block, ROW, B_REDR, "Dash", 68, 60,58,19, &G.sima->dt_uv, 0.0, SI_UVDT_DASH, 0, 0, "Dashed Wire UV drawtype"); + uiDefButC(block, ROW, B_REDR, "Black", 126, 60,58,19, &G.sima->dt_uv, 0.0, SI_UVDT_BLACK, 0, 0, "Black Wire UV drawtype"); + uiDefButC(block, ROW, B_REDR, "White", 184,60,58,19, &G.sima->dt_uv, 0.0, SI_UVDT_WHITE, 0, 0, "White Wire UV drawtype"); + + uiBlockEndAlign(block); + uiDefButBitI(block, TOG, SI_SMOOTH_UV, B_REDR, "Smooth", 250,60,60,19, &G.sima->flag, 0, 0, 0, 0, "Display smooth lines in the UV view"); + + + uiDefButBitI(block, TOG, G_DRAWFACES, B_REDR, "Faces", 10,30,60,19, &G.f, 0, 0, 0, 0, "Displays all faces as shades in the 3d view and UV editor"); + uiDefButBitI(block, TOG, G_DRAWEDGES, B_REDR, "Edges", 70, 30,60,19, &G.f, 0, 0, 0, 0, "Displays selected edges using hilights in the 3d view and UV editor"); + + uiDefButBitI(block, TOG, SI_DRAWSHADOW, B_REDR, "Final Shadow", 130, 30,110,19, &G.sima->flag, 0, 0, 0, 0, "Draw the final result from the objects modifiers"); + + uiDefButBitI(block, TOG, SI_DRAW_STRETCH, B_REDR, "UV Stretch", 10,0,100,19, &G.sima->flag, 0, 0, 0, 0, "Difference between UV's and the 3D coords (blue for low distortion, red is high)"); + if (G.sima->flag & SI_DRAW_STRETCH) { + uiBlockBeginAlign(block); + uiDefButC(block, ROW, B_REDR, "Area", 120,0,60,19, &G.sima->dt_uvstretch, 0.0, SI_UVDT_STRETCH_AREA, 0, 0, "Area distortion between UV's and 3D coords"); + uiDefButC(block, ROW, B_REDR, "Angle", 180,0,60,19, &G.sima->dt_uvstretch, 0.0, SI_UVDT_STRETCH_ANGLE, 0, 0, "Angle distortion between UV's and 3D coords"); + uiBlockEndAlign(block); + } + + } + image_editcursor_buts(block); +} + +static void image_panel_paint(short cntrl) // IMAGE_HANDLER_PAINT +{ + uiBlock *block; + if ((G.sima->image && (G.sima->flag & SI_DRAWTOOL))==0) { + return; + } + + block= uiNewBlock(&curarea->uiblocks, "image_panel_paint", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(IMAGE_HANDLER_PAINT); // for close and esc + if(uiNewPanel(curarea, block, "Image Paint", "Image", 10, 230, 318, 204)==0) + return; + + brush_buttons(block, 1, B_SIMANOTHING, B_SIMABRUSHCHANGE, B_SIMABRUSHBROWSE, B_SIMABRUSHLOCAL, B_SIMABRUSHDELETE, B_KEEPDATA, B_SIMABTEXBROWSE, B_SIMABTEXDELETE); +} + +static void image_panel_curves_reset(void *cumap_v, void *ibuf_v) +{ + CurveMapping *cumap = cumap_v; + int a; + + for(a=0; a<CM_TOT; a++) + curvemap_reset(cumap->cm+a, &cumap->clipr); + + cumap->black[0]=cumap->black[1]=cumap->black[2]= 0.0f; + cumap->white[0]=cumap->white[1]=cumap->white[2]= 1.0f; + curvemapping_set_black_white(cumap, NULL, NULL); + + curvemapping_changed(cumap, 0); + curvemapping_do_ibuf(cumap, ibuf_v); + + allqueue(REDRAWIMAGE, 0); +} + + +static void image_panel_curves(short cntrl) // IMAGE_HANDLER_CURVES +{ + ImBuf *ibuf; + uiBlock *block; + uiBut *bt; + + /* and we check for spare */ + ibuf= imagewindow_get_ibuf(G.sima); + + block= uiNewBlock(&curarea->uiblocks, "image_panel_curves", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(IMAGE_HANDLER_CURVES); // for close and esc + if(uiNewPanel(curarea, block, "Curves", "Image", 10, 450, 318, 204)==0) + return; + + if (ibuf) { + rctf rect; + + if(G.sima->cumap==NULL) + G.sima->cumap= curvemapping_add(4, 0.0f, 0.0f, 1.0f, 1.0f); + + rect.xmin= 110; rect.xmax= 310; + rect.ymin= 10; rect.ymax= 200; + curvemap_buttons(block, G.sima->cumap, 'c', B_SIMACURVES, B_REDR, &rect); + + /* curvemap min/max only works for RGBA */ + if(ibuf->channels==4) { + bt=uiDefBut(block, BUT, B_SIMARANGE, "Reset", 10, 160, 90, 19, NULL, 0.0f, 0.0f, 0, 0, "Reset Black/White point and curves"); + uiButSetFunc(bt, image_panel_curves_reset, G.sima->cumap, ibuf); + + uiBlockBeginAlign(block); + uiDefButF(block, NUM, B_SIMARANGE, "Min R:", 10, 120, 90, 19, G.sima->cumap->black, -1000.0f, 1000.0f, 10, 2, "Black level"); + uiDefButF(block, NUM, B_SIMARANGE, "Min G:", 10, 100, 90, 19, G.sima->cumap->black+1, -1000.0f, 1000.0f, 10, 2, "Black level"); + uiDefButF(block, NUM, B_SIMARANGE, "Min B:", 10, 80, 90, 19, G.sima->cumap->black+2, -1000.0f, 1000.0f, 10, 2, "Black level"); + + uiBlockBeginAlign(block); + uiDefButF(block, NUM, B_SIMARANGE, "Max R:", 10, 50, 90, 19, G.sima->cumap->white, -1000.0f, 1000.0f, 10, 2, "White level"); + uiDefButF(block, NUM, B_SIMARANGE, "Max G:", 10, 30, 90, 19, G.sima->cumap->white+1, -1000.0f, 1000.0f, 10, 2, "White level"); + uiDefButF(block, NUM, B_SIMARANGE, "Max B:", 10, 10, 90, 19, G.sima->cumap->white+2, -1000.0f, 1000.0f, 10, 2, "White level"); + } + } +} +/* 0: disable preview + otherwise refresh preview +*/ +void image_preview_event(int event) +{ + int exec= 0; + + + if(event==0) { + G.scene->r.scemode &= ~R_COMP_CROP; + exec= 1; + } + else { + if(image_preview_active(curarea, NULL, NULL)) { + G.scene->r.scemode |= R_COMP_CROP; + exec= 1; + } + else + G.scene->r.scemode &= ~R_COMP_CROP; + } + + if(exec && G.scene->nodetree) { + /* should work when no node editor in screen..., so we execute right away */ + + ntreeCompositTagGenerators(G.scene->nodetree); + + G.afbreek= 0; + G.scene->nodetree->timecursor= set_timecursor; + G.scene->nodetree->test_break= blender_test_break; + + BIF_store_spare(); + + ntreeCompositExecTree(G.scene->nodetree, &G.scene->r, 1); /* 1 is do_previews */ + + G.scene->nodetree->timecursor= NULL; + G.scene->nodetree->test_break= NULL; + + scrarea_do_windraw(curarea); + waitcursor(0); + + allqueue(REDRAWNODE, 1); + } +} + + +/* nothing drawn here, we use it to store values */ +static void preview_cb(struct ScrArea *sa, struct uiBlock *block) +{ + rctf dispf; + rcti *disprect= &G.scene->r.disprect; + int winx= (G.scene->r.size*G.scene->r.xsch)/100; + int winy= (G.scene->r.size*G.scene->r.ysch)/100; + short mval[2]; + + if(G.scene->r.mode & R_BORDER) { + winx*= (G.scene->r.border.xmax - G.scene->r.border.xmin); + winy*= (G.scene->r.border.ymax - G.scene->r.border.ymin); + } + + /* while dragging we need to update the rects, otherwise it doesn't end with correct one */ + + BLI_init_rctf(&dispf, 15.0f, (block->maxx - block->minx)-15.0f, 15.0f, (block->maxy - block->miny)-15.0f); + ui_graphics_to_window_rct(sa->win, &dispf, disprect); + + /* correction for gla draw */ + BLI_translate_rcti(disprect, -curarea->winrct.xmin, -curarea->winrct.ymin); + + calc_image_view(G.sima, 'p'); +// printf("winrct %d %d %d %d\n", disprect->xmin, disprect->ymin,disprect->xmax, disprect->ymax); + /* map to image space coordinates */ + mval[0]= disprect->xmin; mval[1]= disprect->ymin; + areamouseco_to_ipoco(G.v2d, mval, &dispf.xmin, &dispf.ymin); + mval[0]= disprect->xmax; mval[1]= disprect->ymax; + areamouseco_to_ipoco(G.v2d, mval, &dispf.xmax, &dispf.ymax); + + /* map to render coordinates */ + disprect->xmin= dispf.xmin; + disprect->xmax= dispf.xmax; + disprect->ymin= dispf.ymin; + disprect->ymax= dispf.ymax; + + CLAMP(disprect->xmin, 0, winx); + CLAMP(disprect->xmax, 0, winx); + CLAMP(disprect->ymin, 0, winy); + CLAMP(disprect->ymax, 0, winy); +// printf("drawrct %d %d %d %d\n", disprect->xmin, disprect->ymin,disprect->xmax, disprect->ymax); + +} + +static int is_preview_allowed(ScrArea *cur) +{ + SpaceImage *sima= cur->spacedata.first; + ScrArea *sa; + + /* check if another areawindow has preview set */ + for(sa=G.curscreen->areabase.first; sa; sa= sa->next) { + if(sa!=cur && sa->spacetype==SPACE_IMAGE) { + if(image_preview_active(sa, NULL, NULL)) + return 0; + } + } + /* check image type */ + if(sima->image==NULL || sima->image->type!=IMA_TYPE_COMPOSITE) + return 0; + + return 1; +} + +static void image_panel_preview(ScrArea *sa, short cntrl) // IMAGE_HANDLER_PREVIEW +{ + uiBlock *block; + SpaceImage *sima= sa->spacedata.first; + int ofsx, ofsy; + + if(is_preview_allowed(sa)==0) { + rem_blockhandler(sa, IMAGE_HANDLER_PREVIEW); + G.scene->r.scemode &= ~R_COMP_CROP; /* quite weak */ + return; + } + + block= uiNewBlock(&sa->uiblocks, "image_panel_preview", UI_EMBOSS, UI_HELV, sa->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | UI_PNL_SCALE | cntrl); + uiSetPanelHandler(IMAGE_HANDLER_PREVIEW); // for close and esc + + ofsx= -150+(sa->winx/2)/sima->blockscale; + ofsy= -100+(sa->winy/2)/sima->blockscale; + if(uiNewPanel(sa, block, "Preview", "Image", ofsx, ofsy, 300, 200)==0) return; + + uiBlockSetDrawExtraFunc(block, preview_cb); + +} + +static void image_panel_gpencil(short cntrl) // IMAGE_HANDLER_GREASEPENCIL +{ + uiBlock *block; + SpaceImage *sima; + + sima= curarea->spacedata.first; + + block= uiNewBlock(&curarea->uiblocks, "image_panel_gpencil", UI_EMBOSS, UI_HELV, curarea->win); + uiPanelControl(UI_PNL_SOLID | UI_PNL_CLOSE | cntrl); + uiSetPanelHandler(IMAGE_HANDLER_GREASEPENCIL); // for close and esc + if (uiNewPanel(curarea, block, "Grease Pencil", "SpaceImage", 100, 30, 318, 204)==0) return; + + /* allocate memory for gpd if drawing enabled (this must be done first or else we crash) */ + if (sima->flag & SI_DISPGP) { + if (sima->gpd == NULL) + gpencil_data_setactive(curarea, gpencil_data_addnew()); + } + + if (sima->flag & SI_DISPGP) { + bGPdata *gpd= sima->gpd; + short newheight; + + /* this is a variable height panel, newpanel doesnt force new size on existing panels */ + /* so first we make it default height */ + uiNewPanelHeight(block, 204); + + /* draw button for showing gpencil settings and drawings */ + uiDefButBitI(block, TOG, SI_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &sima->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Image/UV Editor (draw using Shift-LMB)"); + + /* extend the panel if the contents won't fit */ + newheight= draw_gpencil_panel(block, gpd, curarea); + uiNewPanelHeight(block, newheight); + } + else { + uiDefButBitI(block, TOG, SI_DISPGP, B_REDR, "Use Grease Pencil", 10, 225, 150, 20, &sima->flag, 0, 0, 0, 0, "Display freehand annotations overlay over this Image/UV Editor"); + uiDefBut(block, LABEL, 1, " ", 160, 180, 150, 20, NULL, 0.0, 0.0, 0, 0, ""); + } +} + +static void image_blockhandlers(ScrArea *sa) +{ + SpaceImage *sima= sa->spacedata.first; + short a; + + /* warning; blocks need to be freed each time, handlers dont remove */ + uiFreeBlocksWin(&sa->uiblocks, sa->win); + + for(a=0; a<SPACE_MAXHANDLER; a+=2) { + switch(sima->blockhandler[a]) { + case IMAGE_HANDLER_PROPERTIES: + image_panel_properties(sima->blockhandler[a+1]); + break; + case IMAGE_HANDLER_GAME_PROPERTIES: + image_panel_game_properties(sima->blockhandler[a+1]); + break; + case IMAGE_HANDLER_VIEW_PROPERTIES: + image_panel_view_properties(sima->blockhandler[a+1]); + break; + case IMAGE_HANDLER_PAINT: + image_panel_paint(sima->blockhandler[a+1]); + break; + case IMAGE_HANDLER_CURVES: + image_panel_curves(sima->blockhandler[a+1]); + break; + case IMAGE_HANDLER_PREVIEW: + image_panel_preview(sa, sima->blockhandler[a+1]); + break; + case IMAGE_HANDLER_GREASEPENCIL: + image_panel_gpencil(sima->blockhandler[a+1]); + break; + } + /* clear action value for event */ + sima->blockhandler[a+1]= 0; + } + uiDrawBlocksPanels(sa, 0); +} +#endif + diff --git a/source/blender/editors/space_image/image_render.c b/source/blender/editors/space_image/image_render.c new file mode 100644 index 00000000000..804cb8dc542 --- /dev/null +++ b/source/blender/editors/space_image/image_render.c @@ -0,0 +1,335 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> + +#include "DNA_image_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BKE_context.h" +#include "BKE_global.h" +#include "BKE_image.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "RE_pipeline.h" + +#define HEADER_HEIGHT 18 + +/* *********************** render callbacks ***************** */ + +/* set on initialize render, only one render output to imagewindow can exist, so the global isnt dangerous yet :) */ +static ScrArea *image_area= NULL; + +/* can get as well the full picture, as the parts while rendering */ +static void imagewindow_progress(ScrArea *sa, RenderResult *rr, volatile rcti *renrect) +{ + SpaceImage *sima= sa->spacedata.first; + float x1, y1, *rectf= NULL; + unsigned int *rect32= NULL; + int ymin, ymax, xmin, xmax; + + /* if renrect argument, we only display scanlines */ + if(renrect) { + /* if ymax==recty, rendering of layer is ready, we should not draw, other things happen... */ + if(rr->renlay==NULL || renrect->ymax>=rr->recty) + return; + + /* xmin here is first subrect x coord, xmax defines subrect width */ + xmin = renrect->xmin; + xmax = renrect->xmax - xmin; + if (xmax<2) return; + + ymin= renrect->ymin; + ymax= renrect->ymax - ymin; + if(ymax<2) + return; + renrect->ymin= renrect->ymax; + } + else { + xmin = ymin = 0; + xmax = rr->rectx - 2*rr->crop; + ymax = rr->recty - 2*rr->crop; + } + + /* image window cruft */ + + /* find current float rect for display, first case is after composit... still weak */ + if(rr->rectf) + rectf= rr->rectf; + else { + if(rr->rect32) + rect32= (unsigned int *)rr->rect32; + else { + if(rr->renlay==NULL || rr->renlay->rectf==NULL) return; + rectf= rr->renlay->rectf; + } + } + if(rectf) { + /* if scanline updates... */ + rectf+= 4*(rr->rectx*ymin + xmin); + + /* when rendering more pixels than needed, we crop away cruft */ + if(rr->crop) + rectf+= 4*(rr->crop*rr->rectx + rr->crop); + } + + /* tilerect defines drawing offset from (0,0) */ + /* however, tilerect (xmin, ymin) is first pixel */ + x1 = sima->centx + (rr->tilerect.xmin + rr->crop + xmin)*sima->zoom; + y1 = sima->centy + (rr->tilerect.ymin + rr->crop + ymin)*sima->zoom; + + /* needed for gla draw */ + // XXX { rcti rct= ar->winrct; rct.ymax-= HEADER_HEIGHT; glaDefine2DArea(&rct);} + + glPixelZoom(sima->zoom, sima->zoom); + + if(rect32) + glaDrawPixelsSafe(x1, y1, xmax, ymax, rr->rectx, GL_RGBA, GL_UNSIGNED_BYTE, rect32); + else + glaDrawPixelsSafe_to32(x1, y1, xmax, ymax, rr->rectx, rectf); + + glPixelZoom(1.0, 1.0); + +} + +/* in render window; display a couple of scanlines of rendered image */ +/* NOTE: called while render, so no malloc allowed! */ +static void imagewindow_progress_display_cb(RenderResult *rr, volatile rcti *rect) +{ + if (image_area) { + imagewindow_progress(image_area, rr, rect); + + /* no screen_swapbuffers, prevent any other window to draw */ + // XXX myswapbuffers(); + } +} + +/* unused, init_display_cb is called on each render */ +static void imagewindow_clear_display_cb(RenderResult *rr) +{ + if (image_area) { + } +} + +/* returns biggest area that is not uv/image editor. Note that it uses buttons */ +/* window as the last possible alternative. */ +static ScrArea *biggest_non_image_area(bContext *C) +{ + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa, *big= NULL; + int size, maxsize= 0, bwmaxsize= 0; + short foundwin= 0; + + for(sa= sc->areabase.first; sa; sa= sa->next) { + if(sa->winx > 10 && sa->winy > 10) { + size= sa->winx*sa->winy; + if(sa->spacetype == SPACE_BUTS) { + if(foundwin == 0 && size > bwmaxsize) { + bwmaxsize= size; + big= sa; + } + } + else if(sa->spacetype != SPACE_IMAGE && size > maxsize) { + maxsize= size; + big= sa; + foundwin= 1; + } + } + } + + return big; +} + +static ScrArea *biggest_area(bContext *C) +{ + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa, *big= NULL; + int size, maxsize= 0; + + for(sa= sc->areabase.first; sa; sa= sa->next) { + size= sa->winx*sa->winy; + if(size > maxsize) { + maxsize= size; + big= sa; + } + } + return big; +} + + +/* if R_DISPLAYIMAGE + use Image Window showing Render Result + else: turn largest non-image area into Image Window (not to frustrate texture or composite usage) + else: then we use Image Window anyway... + if R_DISPSCREEN + make a new temp fullscreen area with Image Window +*/ + +static ScrArea *find_area_showing_r_result(bContext *C) +{ + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa; + SpaceImage *sima; + + /* find an imagewindow showing render result */ + for(sa=sc->areabase.first; sa; sa= sa->next) { + if(sa->spacetype==SPACE_IMAGE) { + sima= sa->spacedata.first; + if(sima->image && sima->image->type==IMA_TYPE_R_RESULT) + break; + } + } + return sa; +} + +static ScrArea *imagewindow_set_render_display(bContext *C) +{ + ScrArea *sa; + SpaceImage *sima; + + sa= find_area_showing_r_result(C); + + if(sa==NULL) { + /* find largest open non-image area */ + sa= biggest_non_image_area(C); + if(sa) { + // XXX newspace(sa, SPACE_IMAGE); + sima= sa->spacedata.first; + + /* makes ESC go back to prev space */ + sima->flag |= SI_PREVSPACE; + } + else { + /* use any area of decent size */ + sa= biggest_area(C); + if(sa->spacetype!=SPACE_IMAGE) { + // XXX newspace(sa, SPACE_IMAGE); + sima= sa->spacedata.first; + + /* makes ESC go back to prev space */ + sima->flag |= SI_PREVSPACE; + } + } + } + + sima= sa->spacedata.first; + + /* get the correct image, and scale it */ + sima->image= BKE_image_verify_viewer(IMA_TYPE_R_RESULT, "Render Result"); + + if(0) { // XXX G.displaymode==R_DISPLAYSCREEN) { + if(sa->full==0) { + sima->flag |= SI_FULLWINDOW; + /* fullscreen works with lousy curarea */ + // XXX curarea= sa; + // XXX area_fullscreen(); + // XXX sa= curarea; + } + } + + return sa; +} + +static void imagewindow_init_display_cb(RenderResult *rr) +{ + bContext *C= NULL; // XXX + + image_area= imagewindow_set_render_display(C); + + if(image_area) { + SpaceImage *sima= image_area->spacedata.first; + + // XXX areawinset(image_area->win); + + /* calc location using original size (tiles don't tell) */ + sima->centx= (image_area->winx - sima->zoom*(float)rr->rectx)/2.0f; + sima->centy= (image_area->winy - sima->zoom*(float)rr->recty)/2.0f; + + sima->centx-= sima->zoom*sima->xof; + sima->centy-= sima->zoom*sima->yof; + + // XXX drawimagespace(image_area, sima); + // XXX if(image_area->headertype) scrarea_do_headdraw(image_area); + + /* no screen_swapbuffers, prevent any other window to draw */ + // XXX myswapbuffers(); + + // XXX allqueue(REDRAWIMAGE, 0); /* redraw in end */ + } +} + +/* coming from BIF_toggle_render_display() */ +void imagewindow_toggle_render(bContext *C) +{ + bScreen *sc= CTX_wm_screen(C); + ScrArea *sa; + + /* check if any imagewindow is showing temporal render output */ + for(sa=sc->areabase.first; sa; sa= sa->next) { + if(sa->spacetype==SPACE_IMAGE) { + SpaceImage *sima= sa->spacedata.first; + + if(sima->image && sima->image->type==IMA_TYPE_R_RESULT) + if(sima->flag & (SI_PREVSPACE|SI_FULLWINDOW)) + break; + } + } + + if(sa) { + // XXX addqueue(sa->win, ESCKEY, 1); /* also returns from fullscreen */ + } + else { + sa= imagewindow_set_render_display(C); + // XXX scrarea_queue_headredraw(sa); + // XXX scrarea_queue_winredraw(sa); + } +} + +/* NOTE: called while render, so no malloc allowed! */ +static void imagewindow_renderinfo_cb(RenderStats *rs) +{ + if(image_area) { + // XXX BIF_make_render_text(rs); + + // XXX sima_draw_render_info(sima, ar); + + /* no screen_swapbuffers, prevent any other window to draw */ + // XXX myswapbuffers(); + } +} + +void imagewindow_render_callbacks(Render *re) +{ + RE_display_init_cb(re, imagewindow_init_display_cb); + RE_display_draw_cb(re, imagewindow_progress_display_cb); + RE_display_clear_cb(re, imagewindow_clear_display_cb); + RE_stats_draw_cb(re, imagewindow_renderinfo_cb); +} + diff --git a/source/blender/editors/space_image/space_image.c b/source/blender/editors/space_image/space_image.c index 3ac6d3f870b..893ace078ad 100644 --- a/source/blender/editors/space_image/space_image.c +++ b/source/blender/editors/space_image/space_image.c @@ -30,6 +30,8 @@ #include <stdio.h> #include "DNA_image_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" #include "DNA_object_types.h" #include "DNA_space_types.h" #include "DNA_scene_types.h" @@ -39,16 +41,27 @@ #include "BLI_blenlib.h" #include "BLI_arithb.h" +#include "BLI_editVert.h" #include "BLI_rand.h" #include "BKE_colortools.h" #include "BKE_context.h" +#include "BKE_image.h" #include "BKE_screen.h" +#include "BKE_utildefines.h" +#include "IMB_imbuf.h" +#include "IMB_imbuf_types.h" + +#include "ED_mesh.h" #include "ED_space_api.h" #include "ED_screen.h" +#include "ED_uvedit.h" #include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "RNA_access.h" #include "WM_api.h" #include "WM_types.h" @@ -57,9 +70,7 @@ #include "UI_resources.h" #include "UI_view2d.h" -#include "ED_markers.h" - -#include "image_intern.h" // own include +#include "image_intern.h" /* ******************** default callbacks for image space ***************** */ @@ -76,7 +87,6 @@ static SpaceLink *image_new(const bContext *C) simage->iuser.fie_ima= 2; simage->iuser.frames= 100; - /* header */ ar= MEM_callocN(sizeof(ARegion), "header for image"); @@ -126,25 +136,201 @@ static SpaceLink *image_duplicate(SpaceLink *sl) return (SpaceLink *)simagen; } +void image_operatortypes(void) +{ + WM_operatortype_append(IMAGE_OT_view_all); + WM_operatortype_append(IMAGE_OT_view_pan); + WM_operatortype_append(IMAGE_OT_view_selected); + WM_operatortype_append(IMAGE_OT_view_zoom); + WM_operatortype_append(IMAGE_OT_view_zoom_in); + WM_operatortype_append(IMAGE_OT_view_zoom_out); + WM_operatortype_append(IMAGE_OT_view_zoom_ratio); +} + +void image_keymap(struct wmWindowManager *wm) +{ + ListBase *keymap= WM_keymap_listbase(wm, "Image", SPACE_IMAGE, 0); + + WM_keymap_add_item(keymap, "IMAGE_OT_view_all", HOMEKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_view_selected", PADPERIOD, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_view_pan", MIDDLEMOUSE, KM_PRESS, 0, 0); + + WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", WHEELINMOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_out", WHEELOUTMOUSE, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_in", PADPLUSKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_out", PADMINUS, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom", MIDDLEMOUSE, KM_PRESS, KM_CTRL, 0); + + RNA_float_set(WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_ratio", PAD8, KM_PRESS, KM_SHIFT, 0)->ptr, "ratio", 8.0f); + RNA_float_set(WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_ratio", PAD4, KM_PRESS, KM_SHIFT, 0)->ptr, "ratio", 4.0f); + RNA_float_set(WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_ratio", PAD2, KM_PRESS, KM_SHIFT, 0)->ptr, "ratio", 2.0f); + RNA_float_set(WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_ratio", PAD1, KM_PRESS, 0, 0)->ptr, "ratio", 1.0f); + RNA_float_set(WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_ratio", PAD2, KM_PRESS, 0, 0)->ptr, "ratio", 0.5f); + RNA_float_set(WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_ratio", PAD4, KM_PRESS, 0, 0)->ptr, "ratio", 0.25f); + RNA_float_set(WM_keymap_add_item(keymap, "IMAGE_OT_view_zoom_ratio", PAD8, KM_PRESS, 0, 0)->ptr, "ratio", 0.125f); +} + +static void image_refresh(const bContext *C, ScrArea *sa) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + Object *obedit= CTX_data_edit_object(C); + Image *ima; + + ima= get_space_image(sima); + + /* check if we have to set the image from the editmesh */ + if(ima && (ima->source==IMA_SRC_VIEWER || sima->pin)); + else if(obedit && obedit->type == OB_MESH) { + Mesh *me= (Mesh*)obedit->data; + EditMesh *em= me->edit_mesh; + MTFace *tf; + + if(EM_texFaceCheck(em)) { + sima->image= ima= NULL; + + tf = EM_get_active_mtface(em, NULL, NULL, 1); /* partially selected face is ok */ + + if(tf && (tf->mode & TF_TEX)) { + /* don't need to check for pin here, see above */ + sima->image= ima= tf->tpage; + + if(sima->flag & SI_EDITTILE); + else sima->curtile= tf->tile; + + if(ima) { + if(tf->mode & TF_TILES) + ima->tpageflag |= IMA_TILES; + else + ima->tpageflag &= ~IMA_TILES; + } + } + } + } +} + +static void image_listener(ScrArea *sa, wmNotifier *wmn) +{ + /* context changes */ + switch(wmn->category) { + case NC_SCENE: + switch(wmn->data) { + case ND_MODE: + ED_area_tag_refresh(sa); + ED_area_tag_redraw(sa); + break; + } + break; + } +} + +static int image_context(const bContext *C, const bContextDataMember *member, bContextDataResult *result) +{ + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + + if(member == CTX_data_edit_image) { + CTX_data_pointer_set(result, get_space_image(sima)); + return 1; + } + else if(member == CTX_data_edit_image_buffer) { + CTX_data_pointer_set(result, get_space_image_buffer(sima)); + return 1; + } + + return 0; +} + +/************************** main region ***************************/ + +/* sets up the fields of the View2D from zoom and offset */ +static void image_main_area_set_view2d(SpaceImage *sima, ARegion *ar) +{ + Image *ima= get_space_image(sima); + float x1, y1, w, h; + int width, height, winx, winy; + +#if 0 + if(image_preview_active(curarea, &xim, &yim)); + else if(sima->image) { + ImBuf *ibuf= imagewindow_get_ibuf(sima); + float xuser_asp, yuser_asp; + + image_pixel_aspect(sima->image, &xuser_asp, &yuser_asp); + if(ibuf) { + xim= ibuf->x * xuser_asp; + yim= ibuf->y * yuser_asp; + } + else if( sima->image->type==IMA_TYPE_R_RESULT ) { + /* not very important, just nice */ + xim= (G.scene->r.xsch*G.scene->r.size)/100; + yim= (G.scene->r.ysch*G.scene->r.size)/100; + } + } +#endif + + get_space_image_size(sima, &width, &height); + + w= width; + h= height; + + if(ima) + h *= ima->aspy/ima->aspx; + + winx= ar->winrct.xmax - ar->winrct.xmin + 1; + winy= ar->winrct.ymax - ar->winrct.ymin + 1; + + ar->v2d.tot.xmin= 0; + ar->v2d.tot.ymin= 0; + ar->v2d.tot.xmax= w; + ar->v2d.tot.ymax= h; + + ar->v2d.mask.xmin= ar->v2d.mask.ymin= 0; + ar->v2d.mask.xmax= winx; + ar->v2d.mask.ymax= winy; + /* which part of the image space do we see? */ + /* same calculation as in lrectwrite: area left and down*/ + x1= ar->winrct.xmin+(winx-sima->zoom*w)/2; + y1= ar->winrct.ymin+(winy-sima->zoom*h)/2; + + x1-= sima->zoom*sima->xof; + y1-= sima->zoom*sima->yof; + + /* relative display right */ + ar->v2d.cur.xmin= ((ar->winrct.xmin - (float)x1)/sima->zoom); + ar->v2d.cur.xmax= ar->v2d.cur.xmin + ((float)winx/sima->zoom); + + /* relative display left */ + ar->v2d.cur.ymin= ((ar->winrct.ymin-(float)y1)/sima->zoom); + ar->v2d.cur.ymax= ar->v2d.cur.ymin + ((float)winy/sima->zoom); + + /* normalize 0.0..1.0 */ + ar->v2d.cur.xmin /= w; + ar->v2d.cur.xmax /= w; + ar->v2d.cur.ymin /= h; + ar->v2d.cur.ymax /= h; +} /* add handlers, stuff you only do once or on area/region changes */ static void image_main_area_init(wmWindowManager *wm, ARegion *ar) { ListBase *keymap; - UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_STANDARD, ar->winx, ar->winy); + // image space manages own v2d + // UI_view2d_region_reinit(&ar->v2d, V2D_COMMONVIEW_STANDARD, ar->winx, ar->winy); /* own keymap */ - keymap= WM_keymap_listbase(wm, "Image", SPACE_IMAGE, 0); /* XXX weak? */ + keymap= WM_keymap_listbase(wm, "Image", SPACE_IMAGE, 0); WM_event_add_keymap_handler_bb(&ar->handlers, keymap, &ar->v2d.mask, &ar->winrct); } static void image_main_area_draw(const bContext *C, ARegion *ar) { /* draw entirely, view changes should be handled here */ - // SpaceImage *simage= (SpaceImage*)CTX_wm_space_data(C); + SpaceImage *sima= (SpaceImage*)CTX_wm_space_data(C); + Object *obedit= CTX_data_edit_object(C); + Scene *scene= CTX_data_scene(C); View2D *v2d= &ar->v2d; + //View2DScrollers *scrollers; float col[3]; /* clear and setup matrix */ @@ -152,27 +338,56 @@ static void image_main_area_draw(const bContext *C, ARegion *ar) glClearColor(col[0], col[1], col[2], 0.0); glClear(GL_COLOR_BUFFER_BIT); - UI_view2d_view_ortho(C, v2d); + /* we set view2d from own zoom and offset each time */ + image_main_area_set_view2d(sima, ar); - /* data... */ - - - /* reset view matrix */ + /* we draw image in pixelspace */ + draw_image_main(sima, ar, scene); + + /* and uvs in 0.0-1.0 space */ + UI_view2d_view_ortho(C, v2d); + draw_uvedit_main(sima, ar, scene, obedit); UI_view2d_view_restore(C); /* scrollers? */ + /*scrollers= UI_view2d_scrollers_calc(C, v2d, V2D_UNIT_VALUES, V2D_GRID_CLAMP, V2D_ARG_DUMMY, V2D_ARG_DUMMY); + UI_view2d_scrollers_draw(C, v2d, scrollers); + UI_view2d_scrollers_free(scrollers);*/ } -void image_operatortypes(void) +static void image_modal_keymaps(wmWindowManager *wm, ARegion *ar, int stype) { + ListBase *keymap; + keymap= WM_keymap_listbase(wm, "UVEdit", 0, 0); + if(stype==NS_EDITMODE_MESH) + WM_event_add_keymap_handler(&ar->handlers, keymap); + else + WM_event_remove_keymap_handler(&ar->handlers, keymap); } -void image_keymap(struct wmWindowManager *wm) +static void image_main_area_listener(ARegion *ar, wmNotifier *wmn) { - + /* context changes */ + switch(wmn->category) { + case NC_SCENE: + switch(wmn->data) { + case ND_MODE: + image_modal_keymaps(wmn->wm, ar, wmn->subtype); + break; + } + break; + case NC_OBJECT: + switch(wmn->data) { + case ND_GEOM_SELECT: + ED_region_tag_redraw(ar); + break; + } + } } +/************************* header region **************************/ + /* add handlers, stuff you only do once or on area/region changes */ static void image_header_area_init(wmWindowManager *wm, ARegion *ar) { @@ -201,10 +416,7 @@ static void image_header_area_draw(const bContext *C, ARegion *ar) UI_view2d_view_restore(C); } -static void image_main_area_listener(ARegion *ar, wmNotifier *wmn) -{ - /* context changes */ -} +/**************************** spacetype *****************************/ /* only called once, from space/spacetypes.c */ void ED_spacetype_image(void) @@ -220,6 +432,9 @@ void ED_spacetype_image(void) st->duplicate= image_duplicate; st->operatortypes= image_operatortypes; st->keymap= image_keymap; + st->refresh= image_refresh; + st->listener= image_listener; + st->context= image_context; /* regions: main window */ art= MEM_callocN(sizeof(ARegionType), "spacetype image region"); @@ -227,7 +442,7 @@ void ED_spacetype_image(void) art->init= image_main_area_init; art->draw= image_main_area_draw; art->listener= image_main_area_listener; - art->keymapflag= ED_KEYMAP_VIEW2D; + art->keymapflag= 0; BLI_addhead(&st->regiontypes, art); @@ -242,18 +457,128 @@ void ED_spacetype_image(void) BLI_addhead(&st->regiontypes, art); - /* regions: channels */ - art= MEM_callocN(sizeof(ARegionType), "spacetype image region"); - art->regionid = RGN_TYPE_CHANNELS; - art->minsizex= 80; - art->keymapflag= ED_KEYMAP_UI|ED_KEYMAP_VIEW2D; - -// art->init= image_channel_area_init; -// art->draw= image_channel_area_draw; - - BLI_addhead(&st->regiontypes, art); - BKE_spacetype_register(st); } +/**************************** common state *****************************/ + +Image *get_space_image(SpaceImage *sima) +{ + return sima->image; +} + +/* called to assign images to UV faces */ +void set_space_image(SpaceImage *sima, Scene *scene, Object *obedit, Image *ima) +{ + ED_uvedit_assign_image(scene, obedit, ima, sima->image); + + /* change the space ima after because uvedit_face_visible uses the space ima + * to check if the face is displayed in UV-localview */ + sima->image= ima; + + if(ima == NULL || ima->type==IMA_TYPE_R_RESULT || ima->type==IMA_TYPE_COMPOSITE) + sima->flag &= ~SI_DRAWTOOL; +} + +ImBuf *get_space_image_buffer(SpaceImage *sima) +{ + ImBuf *ibuf; + + if(sima->image) { +#if 0 + if(sima->image->type==IMA_TYPE_R_RESULT && BIF_show_render_spare()) + return BIF_render_spare_imbuf(); + else +#endif + ibuf= BKE_image_get_ibuf(sima->image, &sima->iuser); + + if(ibuf->rect || ibuf->rect_float) + return ibuf; + } + + return NULL; +} + +void get_space_image_size(SpaceImage *sima, int *width, int *height) +{ + ImBuf *ibuf; + + ibuf= get_space_image_buffer(sima); + + if(ibuf && ibuf->x > 0 && ibuf->y > 0) { + *width= ibuf->x; + *height= ibuf->y; + } + /* I know a bit weak... but preview uses not actual image size */ + // XXX else if(image_preview_active(sima, width, height)); + else { + *width= 256; + *height= 256; + } +} + +void get_space_image_aspect(SpaceImage *sima, float *aspx, float *aspy) +{ + Image *ima; + + ima= get_space_image(sima); + + *aspx= *aspy= 1.0; + + if((ima == NULL) || (ima->type == IMA_TYPE_R_RESULT) || (ima->type == IMA_TYPE_COMPOSITE) || + (ima->tpageflag & IMA_TILES) || (ima->aspx==0.0 || ima->aspy==0.0)) + return; + + /* x is always 1 */ + *aspy = ima->aspy/ima->aspx; +} + +void get_space_image_zoom(SpaceImage *sima, ARegion *ar, float *zoomx, float *zoomy) +{ + int width, height; + + get_space_image_size(sima, &width, &height); + + *zoomx= (float)(ar->winrct.xmax - ar->winrct.xmin)/(float)((ar->v2d.cur.xmax - ar->v2d.cur.xmin)*width); + *zoomy= (float)(ar->winrct.ymax - ar->winrct.ymin)/(float)((ar->v2d.cur.ymax - ar->v2d.cur.ymin)*height); +} + +int get_space_image_show_render(SpaceImage *sima) +{ + return (sima->image && ELEM(sima->image->type, IMA_TYPE_R_RESULT, IMA_TYPE_COMPOSITE)); +} + +int get_space_image_show_paint(SpaceImage *sima) +{ + if(get_space_image_show_render(sima)) + return 0; + + return (sima->flag & SI_DRAWTOOL); +} + +int get_space_image_show_uvedit(SpaceImage *sima, Object *obedit) +{ + if(get_space_image_show_render(sima)) + return 0; + if(get_space_image_show_paint(sima)) + return 0; + + if(obedit && obedit->type == OB_MESH) + return EM_texFaceCheck(((Mesh*)obedit->data)->edit_mesh); + + return 0; +} + +int get_space_image_show_uvshadow(SpaceImage *sima, Object *obedit) +{ + if(get_space_image_show_render(sima)) + return 0; + + if(get_space_image_show_paint(sima)) + if(obedit && obedit->type == OB_MESH) + return EM_texFaceCheck(((Mesh*)obedit->data)->edit_mesh); + + return 0; +} + diff --git a/source/blender/editors/uvedit/Makefile b/source/blender/editors/uvedit/Makefile new file mode 100644 index 00000000000..bd19bfdacb2 --- /dev/null +++ b/source/blender/editors/uvedit/Makefile @@ -0,0 +1,54 @@ +# +# $Id: Makefile 14 2002-10-13 15:57:19Z hans $ +# +# ***** BEGIN GPL LICENSE BLOCK ***** +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software Foundation, +# Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. +# +# The Original Code is Copyright (C) 2007 Blender Foundation +# All rights reserved. +# +# The Original Code is: all of this file. +# +# Contributor(s): none yet. +# +# ***** END GPL LICENSE BLOCK ***** +# +# Makes module object directory and bounces make to subdirectories. + +LIBNAME = ed_uvedit +DIR = $(OCGDIR)/blender/$(LIBNAME) + +include nan_compile.mk + +CFLAGS += $(LEVEL_1_C_WARNINGS) + +CPPFLAGS += -I$(NAN_GLEW)/include +CPPFLAGS += -I$(OPENGL_HEADERS) + +# not very neat.... +CPPFLAGS += -I../../windowmanager +CPPFLAGS += -I../../blenkernel +CPPFLAGS += -I../../blenlib +CPPFLAGS += -I../../makesdna +CPPFLAGS += -I../../makesrna +CPPFLAGS += -I../../imbuf +CPPFLAGS += -I$(NAN_GUARDEDALLOC)/include +CPPFLAGS += -I$(NAN_OPENNL)/include + +# own include + +CPPFLAGS += -I../include + diff --git a/source/blender/editors/uvedit/SConscript b/source/blender/editors/uvedit/SConscript new file mode 100644 index 00000000000..29989a5b439 --- /dev/null +++ b/source/blender/editors/uvedit/SConscript @@ -0,0 +1,10 @@ +#!/usr/bin/python +Import ('env') + +sources = env.Glob('*.c') + +incs = '../include ../../blenlib ../../blenkernel ../../makesdna ../../imbuf' +incs += ' ../../windowmanager #/intern/guardedalloc #/extern/glew/include' +incs += ' ../../makesrna #/intern/opennl/extern' + +env.BlenderLib ( 'bf_editors_uvedit', sources, Split(incs), [], libtype=['core'], priority=[45] ) diff --git a/source/blender/editors/uvedit/uvedit_draw.c b/source/blender/editors/uvedit/uvedit_draw.c new file mode 100644 index 00000000000..d363639acbc --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_draw.c @@ -0,0 +1,808 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * Contributor(s): Blender Foundation, 2002-2009 + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <float.h> +#include <math.h> +#include <stdlib.h> + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BKE_customdata.h" +#include "BKE_DerivedMesh.h" +#include "BKE_global.h" // XXX make edge and face drawing flags local +#include "BKE_object.h" +#include "BKE_utildefines.h" + +#include "BLI_arithb.h" +#include "BLI_editVert.h" + +#include "BIF_gl.h" +#include "BIF_glutil.h" + +#include "ED_mesh.h" + +#include "UI_resources.h" + +#include "../space_image/image_intern.h" +#include "uvedit_intern.h" + +/* XXX make this draw extra for transform operator */ +#if 0 +static void draw_image_transform(ImBuf *ibuf, float xuser_asp, float yuser_asp) +{ +#if 0 + if(G.moving) { + float aspx, aspy, center[3]; + + BIF_drawConstraint(); + + if(ibuf==0 || ibuf->rect==0 || ibuf->x==0 || ibuf->y==0) { + aspx= aspy= 1.0; + } + else { + aspx= (256.0/ibuf->x) * xuser_asp; + aspy= (256.0/ibuf->y) * yuser_asp; + } + BIF_getPropCenter(center); + + /* scale and translate the circle into place and draw it */ + glPushMatrix(); + glScalef(aspx, aspy, 1.0); + glTranslatef((1/aspx)*center[0] - center[0], + (1/aspy)*center[1] - center[1], 0.0); + + BIF_drawPropCircle(); + + glPopMatrix(); + } +#endif +} +#endif + +static void drawcursor_sima(SpaceImage *sima, ARegion *ar) +{ + View2D *v2d= &ar->v2d; + float zoomx, zoomy, w, h; + int width, height; + + get_space_image_size(sima, &width, &height); + get_space_image_zoom(sima, ar, &zoomx, &zoomy); + + w= zoomx*width/256.0f; + h= zoomy*height/256.0f; + + cpack(0xFFFFFF); + glTranslatef(v2d->cursor[0], v2d->cursor[1], 0.0f); + fdrawline(-0.05/w, 0, 0, 0.05/h); + fdrawline(0, 0.05/h, 0.05/w, 0); + fdrawline(0.05/w, 0, 0, -0.05/h); + fdrawline(0, -0.05/h, -0.05/w, 0); + + setlinestyle(4); + cpack(0xFF); + fdrawline(-0.05/w, 0, 0, 0.05/h); + fdrawline(0, 0.05/h, 0.05/w, 0); + fdrawline(0.05/w, 0, 0, -0.05/h); + fdrawline(0, -0.05/h, -0.05/w, 0); + + + setlinestyle(0); + cpack(0x0); + fdrawline(-0.020/w, 0, -0.1/w, 0); + fdrawline(0.1/w, 0, .020/w, 0); + fdrawline(0, -0.020/h, 0, -0.1/h); + fdrawline(0, 0.1/h, 0, 0.020/h); + + setlinestyle(1); + cpack(0xFFFFFF); + fdrawline(-0.020/w, 0, -0.1/w, 0); + fdrawline(0.1/w, 0, .020/w, 0); + fdrawline(0, -0.020/h, 0, -0.1/h); + fdrawline(0, 0.1/h, 0, 0.020/h); + + glTranslatef(-v2d->cursor[0], -v2d->cursor[1], 0.0f); + setlinestyle(0); +} + +static int draw_uvs_face_check(Scene *scene) +{ + /* checks if we are selecting only faces */ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(scene->selectmode == SCE_SELECT_FACE) + return 2; + else if(scene->selectmode & SCE_SELECT_FACE) + return 1; + else + return 0; + } + else + return (scene->toolsettings->uv_selectmode == UV_SELECT_FACE); +} + +static void draw_uvs_shadow(SpaceImage *sima, Object *obedit) +{ + EditMesh *em; + EditFace *efa; + TFace *tf; + + em= ((Mesh*)obedit->data)->edit_mesh; + + /* draws the grey mesh when painting */ + glColor3ub(112, 112, 112); + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + glBegin(GL_LINE_LOOP); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + } +} + +static int draw_uvs_dm_shadow(DerivedMesh *dm) +{ + /* draw shadow mesh - this is the mesh with the modifier applied */ + + if(dm && dm->drawUVEdges && CustomData_has_layer(&dm->faceData, CD_MTFACE)) { + glColor3ub(112, 112, 112); + dm->drawUVEdges(dm); + return 1; + } + + return 0; +} + +static void draw_uvs_stretch(SpaceImage *sima, Scene *scene, EditMesh *em, MTFace *activetf) +{ + EditFace *efa; + MTFace *tf; + Image *ima= sima->image; + float aspx, aspy, col[4], tf_uv[4][2]; + + aspx= 1.0f; + aspy= 1.0f; + //XXX transform_aspect_ratio_tf_uv(&aspx, &aspy); + + switch(sima->dt_uvstretch) { + case SI_UVDT_STRETCH_AREA: + { + float totarea=0.0f, totuvarea=0.0f, areadiff, uvarea, area; + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uv_copy_aspect(tf->uv, tf_uv, aspx, aspy); + + totarea += EM_face_area(efa); + //totuvarea += tf_area(tf, efa->v4!=0); + totuvarea += uv_area(tf_uv, efa->v4!=0); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + efa->tmp.p = tf; + } + else { + if(tf == activetf) + activetf= NULL; + efa->tmp.p = NULL; + } + } + + if(totarea < FLT_EPSILON || totuvarea < FLT_EPSILON) { + col[0] = 1.0; + col[1] = col[2] = 0.0; + glColor3fv(col); + for(efa= em->faces.first; efa; efa= efa->next) { + if((tf=(MTFace *)efa->tmp.p)) { + glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + } + } + } + else { + for(efa= em->faces.first; efa; efa= efa->next) { + if((tf=(MTFace *)efa->tmp.p)) { + area = EM_face_area(efa) / totarea; + uv_copy_aspect(tf->uv, tf_uv, aspx, aspy); + //uvarea = tf_area(tf, efa->v4!=0) / totuvarea; + uvarea = uv_area(tf_uv, efa->v4!=0) / totuvarea; + + if(area < FLT_EPSILON || uvarea < FLT_EPSILON) + areadiff = 1.0; + else if(area>uvarea) + areadiff = 1.0-(uvarea/area); + else + areadiff = 1.0-(area/uvarea); + + weight_to_rgb(areadiff, col, col+1, col+2); + glColor3fv(col); + + glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + } + } + } + break; + } + case SI_UVDT_STRETCH_ANGLE: + { + float uvang1,uvang2,uvang3,uvang4; + float ang1,ang2,ang3,ang4; + float av1[3], av2[3], av3[3], av4[3]; /* use for 2d and 3d angle vectors */ + float a; + + col[3] = 0.5; /* hard coded alpha, not that nice */ + + glShadeModel(GL_SMOOTH); + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + efa->tmp.p = tf; + uv_copy_aspect(tf->uv, tf_uv, aspx, aspy); + if(efa->v4) { + +#if 0 /* Simple but slow, better reuse normalized vectors */ + uvang1 = VecAngle3_2D(tf_uv[3], tf_uv[0], tf_uv[1]); + ang1 = VecAngle3(efa->v4->co, efa->v1->co, efa->v2->co); + + uvang2 = VecAngle3_2D(tf_uv[0], tf_uv[1], tf_uv[2]); + ang2 = VecAngle3(efa->v1->co, efa->v2->co, efa->v3->co); + + uvang3 = VecAngle3_2D(tf_uv[1], tf_uv[2], tf_uv[3]); + ang3 = VecAngle3(efa->v2->co, efa->v3->co, efa->v4->co); + + uvang4 = VecAngle3_2D(tf_uv[2], tf_uv[3], tf_uv[0]); + ang4 = VecAngle3(efa->v3->co, efa->v4->co, efa->v1->co); +#endif + + /* uv angles */ + VECSUB2D(av1, tf_uv[3], tf_uv[0]); Normalize2(av1); + VECSUB2D(av2, tf_uv[0], tf_uv[1]); Normalize2(av2); + VECSUB2D(av3, tf_uv[1], tf_uv[2]); Normalize2(av3); + VECSUB2D(av4, tf_uv[2], tf_uv[3]); Normalize2(av4); + + /* This is the correct angle however we are only comparing angles + * uvang1 = 90-((NormalizedVecAngle2_2D(av1, av2) * 180.0/M_PI)-90);*/ + uvang1 = NormalizedVecAngle2_2D(av1, av2)*180.0/M_PI; + uvang2 = NormalizedVecAngle2_2D(av2, av3)*180.0/M_PI; + uvang3 = NormalizedVecAngle2_2D(av3, av4)*180.0/M_PI; + uvang4 = NormalizedVecAngle2_2D(av4, av1)*180.0/M_PI; + + /* 3d angles */ + VECSUB(av1, efa->v4->co, efa->v1->co); Normalize(av1); + VECSUB(av2, efa->v1->co, efa->v2->co); Normalize(av2); + VECSUB(av3, efa->v2->co, efa->v3->co); Normalize(av3); + VECSUB(av4, efa->v3->co, efa->v4->co); Normalize(av4); + + /* This is the correct angle however we are only comparing angles + * ang1 = 90-((NormalizedVecAngle2(av1, av2) * 180.0/M_PI)-90);*/ + ang1 = NormalizedVecAngle2(av1, av2)*180.0/M_PI; + ang2 = NormalizedVecAngle2(av2, av3)*180.0/M_PI; + ang3 = NormalizedVecAngle2(av3, av4)*180.0/M_PI; + ang4 = NormalizedVecAngle2(av4, av1)*180.0/M_PI; + + glBegin(GL_QUADS); + + /* This simple makes the angles display worse then they really are ;) + * 1.0-pow((1.0-a), 2) */ + + a = fabs(uvang1-ang1)/180.0; + weight_to_rgb(1.0-pow((1.0-a), 2), col, col+1, col+2); + glColor3fv(col); + glVertex2fv(tf->uv[0]); + a = fabs(uvang2-ang2)/180.0; + weight_to_rgb(1.0-pow((1.0-a), 2), col, col+1, col+2); + glColor3fv(col); + glVertex2fv(tf->uv[1]); + a = fabs(uvang3-ang3)/180.0; + weight_to_rgb(1.0-pow((1.0-a), 2), col, col+1, col+2); + glColor3fv(col); + glVertex2fv(tf->uv[2]); + a = fabs(uvang4-ang4)/180.0; + weight_to_rgb(1.0-pow((1.0-a), 2), col, col+1, col+2); + glColor3fv(col); + glVertex2fv(tf->uv[3]); + + } + else { +#if 0 /* Simple but slow, better reuse normalized vectors */ + uvang1 = VecAngle3_2D(tf_uv[2], tf_uv[0], tf_uv[1]); + ang1 = VecAngle3(efa->v3->co, efa->v1->co, efa->v2->co); + + uvang2 = VecAngle3_2D(tf_uv[0], tf_uv[1], tf_uv[2]); + ang2 = VecAngle3(efa->v1->co, efa->v2->co, efa->v3->co); + + uvang3 = 180-(uvang1+uvang2); + ang3 = 180-(ang1+ang2); +#endif + + /* uv angles */ + VECSUB2D(av1, tf_uv[2], tf_uv[0]); Normalize2(av1); + VECSUB2D(av2, tf_uv[0], tf_uv[1]); Normalize2(av2); + VECSUB2D(av3, tf_uv[1], tf_uv[2]); Normalize2(av3); + + /* This is the correct angle however we are only comparing angles + * uvang1 = 90-((NormalizedVecAngle2_2D(av1, av2) * 180.0/M_PI)-90); */ + uvang1 = NormalizedVecAngle2_2D(av1, av2)*180.0/M_PI; + uvang2 = NormalizedVecAngle2_2D(av2, av3)*180.0/M_PI; + uvang3 = NormalizedVecAngle2_2D(av3, av1)*180.0/M_PI; + + /* 3d angles */ + VECSUB(av1, efa->v3->co, efa->v1->co); Normalize(av1); + VECSUB(av2, efa->v1->co, efa->v2->co); Normalize(av2); + VECSUB(av3, efa->v2->co, efa->v3->co); Normalize(av3); + /* This is the correct angle however we are only comparing angles + * ang1 = 90-((NormalizedVecAngle2(av1, av2) * 180.0/M_PI)-90); */ + ang1 = NormalizedVecAngle2(av1, av2)*180.0/M_PI; + ang2 = NormalizedVecAngle2(av2, av3)*180.0/M_PI; + ang3 = NormalizedVecAngle2(av3, av1)*180.0/M_PI; + + /* This simple makes the angles display worse then they really are ;) + * 1.0-pow((1.0-a), 2) */ + + glBegin(GL_TRIANGLES); + a = fabs(uvang1-ang1)/180.0; + weight_to_rgb(1.0-pow((1.0-a), 2), col, col+1, col+2); + glColor3fv(col); + glVertex2fv(tf->uv[0]); + a = fabs(uvang2-ang2)/180.0; + weight_to_rgb(1.0-pow((1.0-a), 2), col, col+1, col+2); + glColor3fv(col); + glVertex2fv(tf->uv[1]); + a = fabs(uvang3-ang3)/180.0; + weight_to_rgb(1.0-pow((1.0-a), 2), col, col+1, col+2); + glColor3fv(col); + glVertex2fv(tf->uv[2]); + } + glEnd(); + } + else { + if(tf == activetf) + activetf= NULL; + efa->tmp.p = NULL; + } + } + + glShadeModel(GL_FLAT); + break; + } + } +} + +/* draws uv's in the image space */ +static void draw_uvs(SpaceImage *sima, Scene *scene, Object *obedit) +{ + EditMesh *em; + EditFace *efa, *efa_act; + MTFace *tf, *activetf = NULL; + DerivedMesh *finaldm, *cagedm; + char col1[4], col2[4]; + float pointsize; + int drawfaces, lastsel, sel; + Image *ima= sima->image; + + em= ((Mesh*)obedit->data)->edit_mesh; + activetf= EM_get_active_mtface(em, &efa_act, NULL, 0); /* will be set to NULL if hidden */ + + drawfaces= draw_uvs_face_check(scene); + +#if 0 + calc_image_view(G.sima, 'f'); /* float */ + myortho2(G.v2d->cur.xmin, G.v2d->cur.xmax, G.v2d->cur.ymin, G.v2d->cur.ymax); + glLoadIdentity(); +#endif + + /* 1. draw shadow mesh */ + + if(sima->flag & SI_DRAWSHADOW) { + /* first try existing derivedmesh */ + if(!draw_uvs_dm_shadow(em->derivedFinal)) { + /* create one if it does not exist */ + cagedm = editmesh_get_derived_cage_and_final(scene, obedit, em, &finaldm, CD_MASK_BAREMESH|CD_MASK_MTFACE); + + /* when sync selection is enabled, all faces are drawn (except for hidden) + * so if cage is the same as the final, theres no point in drawing this */ + if(!((scene->toolsettings->uv_flag & UV_SYNC_SELECTION) && (cagedm == finaldm))) + draw_uvs_dm_shadow(finaldm); + + /* release derivedmesh again */ + if(cagedm != finaldm) cagedm->release(cagedm); + finaldm->release(finaldm); + } + } + + /* 2. draw colored faces */ + + if(sima->flag & SI_DRAW_STRETCH) { + draw_uvs_stretch(sima, scene, em, activetf); + } + else if(G.f & G_DRAWFACES) { + /* draw transparent faces */ + UI_GetThemeColor4ubv(TH_FACE, col1); + UI_GetThemeColor4ubv(TH_FACE_SELECT, col2); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + glEnable(GL_BLEND); + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + efa->tmp.p = tf; + if(tf==activetf) continue; /* important the temp pointer is set above */ + + if(uvedit_face_selected(scene, efa, tf)) + glColor4ubv((GLubyte *)col2); + else + glColor4ubv((GLubyte *)col1); + + glBegin(efa->v4?GL_QUADS:GL_TRIANGLES); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + } + else { + if(tf == activetf) + activetf= NULL; + efa->tmp.p = NULL; + } + } + glDisable(GL_BLEND); + } + else { + /* would be nice to do this within a draw loop but most below are optional, so it would involve too many checks */ + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + efa->tmp.p = tf; + } + else { + if(tf == activetf) + activetf= NULL; + efa->tmp.p = NULL; + } + } + + } + + /* 3. draw active face stippled */ + + if(activetf) { + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + UI_ThemeColor4(TH_EDITMESH_ACTIVE); + + glEnable(GL_POLYGON_STIPPLE); + glPolygonStipple(stipple_quarttone); + + glBegin(efa_act->v4? GL_QUADS: GL_TRIANGLES); + glVertex2fv(activetf->uv[0]); + glVertex2fv(activetf->uv[1]); + glVertex2fv(activetf->uv[2]); + if(efa_act->v4) glVertex2fv(activetf->uv[3]); + glEnd(); + + glDisable(GL_POLYGON_STIPPLE); + glDisable(GL_BLEND); + } + + /* 4. draw edges */ + + if(sima->flag & SI_SMOOTH_UV) { + glEnable(GL_LINE_SMOOTH); + glEnable(GL_BLEND); + glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); + } + + switch(sima->dt_uv) { + case SI_UVDT_DASH: + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf) { + cpack(0x111111); + + glBegin(GL_LINE_LOOP); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + + setlinestyle(2); + cpack(0x909090); + + glBegin(GL_LINE_STRIP); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2fv(tf->uv[0]); + if(efa->v4) glVertex2fv(tf->uv[3]); + else glVertex2fv(tf->uv[2]); + glEnd(); + + glBegin(GL_LINE_STRIP); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + + setlinestyle(0); + } + } + break; + case SI_UVDT_BLACK: /* black/white */ + case SI_UVDT_WHITE: + cpack((sima->dt_uv==SI_UVDT_WHITE) ? 0xFFFFFF : 0x0); + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf) { + glBegin(GL_LINE_LOOP); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + } + } + break; + case SI_UVDT_OUTLINE: + glLineWidth(3); + cpack(0x0); + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf) { + glBegin(GL_LINE_LOOP); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + } + } + + glLineWidth(1); + col2[0] = col2[1] = col2[2] = 128; col2[3] = 255; + glColor4ubv((unsigned char *)col2); + + if(G.f & G_DRAWEDGES) { + glShadeModel(GL_SMOOTH); + UI_GetThemeColor4ubv(TH_VERTEX_SELECT, col1); + lastsel = sel = 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf) { + glBegin(GL_LINE_LOOP); + sel = (uvedit_uv_selected(scene, efa, tf, 0) ? 1 : 0); + if(sel != lastsel) { glColor4ubv(sel ? (GLubyte *)col1 : (GLubyte *)col2); lastsel = sel; } + glVertex2fv(tf->uv[0]); + + sel = uvedit_uv_selected(scene, efa, tf, 1) ? 1 : 0; + if(sel != lastsel) { glColor4ubv(sel ? (GLubyte *)col1 : (GLubyte *)col2); lastsel = sel; } + glVertex2fv(tf->uv[1]); + + sel = uvedit_uv_selected(scene, efa, tf, 2) ? 1 : 0; + if(sel != lastsel) { glColor4ubv(sel ? (GLubyte *)col1 : (GLubyte *)col2); lastsel = sel; } + glVertex2fv(tf->uv[2]); + + if(efa->v4) { + sel = uvedit_uv_selected(scene, efa, tf, 3) ? 1 : 0; + if(sel != lastsel) { glColor4ubv(sel ? (GLubyte *)col1 : (GLubyte *)col2); lastsel = sel; } + glVertex2fv(tf->uv[3]); + } + + glEnd(); + } + } + glShadeModel(GL_FLAT); + } + else { + /* no nice edges */ + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf) { + glBegin(GL_LINE_LOOP); + glVertex2fv(tf->uv[0]); + glVertex2fv(tf->uv[1]); + glVertex2fv(tf->uv[2]); + if(efa->v4) glVertex2fv(tf->uv[3]); + glEnd(); + } + } + } + + break; + } + + if(sima->flag & SI_SMOOTH_UV) { + glDisable(GL_LINE_SMOOTH); + glDisable(GL_BLEND); + } + + /* 5. draw face centers */ + + if(drawfaces) { + float cent[2]; + + pointsize = UI_GetThemeValuef(TH_FACEDOT_SIZE); + glPointSize(pointsize); // TODO - drawobject.c changes this value after - Investigate! + + /* unselected faces */ + UI_ThemeColor(TH_WIRE); + + bglBegin(GL_POINTS); + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf && !uvedit_face_selected(scene, efa, tf)) { + uv_center(tf->uv, cent, efa->v4 != NULL); + bglVertex2fv(cent); + } + } + bglEnd(); + + /* selected faces */ + UI_ThemeColor(TH_FACE_DOT); + + bglBegin(GL_POINTS); + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf && uvedit_face_selected(scene, efa, tf)) { + uv_center(tf->uv, cent, efa->v4 != NULL); + bglVertex2fv(cent); + } + } + bglEnd(); + } + + /* 6. draw uv vertices */ + + if(drawfaces != 2) { /* 2 means Mesh Face Mode */ + /* unselected uvs */ + UI_ThemeColor(TH_VERTEX); + pointsize = UI_GetThemeValuef(TH_VERTEX_SIZE); + glPointSize(pointsize); + + bglBegin(GL_POINTS); + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf) { + if(!uvedit_uv_selected(scene, efa, tf, 0)) + bglVertex2fv(tf->uv[0]); + if(!uvedit_uv_selected(scene, efa, tf, 1)) + bglVertex2fv(tf->uv[1]); + if(!uvedit_uv_selected(scene, efa, tf, 2)) + bglVertex2fv(tf->uv[2]); + if(efa->v4 && !uvedit_uv_selected(scene, efa, tf, 3)) + bglVertex2fv(tf->uv[3]); + } + } + bglEnd(); + + /* pinned uvs */ + /* give odd pointsizes odd pin pointsizes */ + glPointSize(pointsize*2 + (((int)pointsize % 2)? (-1): 0)); + cpack(0xFF); + + bglBegin(GL_POINTS); + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf) { + if(tf->unwrap & TF_PIN1) + bglVertex2fv(tf->uv[0]); + if(tf->unwrap & TF_PIN2) + bglVertex2fv(tf->uv[1]); + if(tf->unwrap & TF_PIN3) + bglVertex2fv(tf->uv[2]); + if(efa->v4 && (tf->unwrap & TF_PIN4)) + bglVertex2fv(tf->uv[3]); + } + } + bglEnd(); + + /* selected uvs */ + UI_ThemeColor(TH_VERTEX_SELECT); + glPointSize(pointsize); + + bglBegin(GL_POINTS); + for(efa= em->faces.first; efa; efa= efa->next) { + tf= (MTFace *)efa->tmp.p; /* visible faces cached */ + + if(tf) { + if(uvedit_uv_selected(scene, efa, tf, 0)) + bglVertex2fv(tf->uv[0]); + if(uvedit_uv_selected(scene, efa, tf, 1)) + bglVertex2fv(tf->uv[1]); + if(uvedit_uv_selected(scene, efa, tf, 2)) + bglVertex2fv(tf->uv[2]); + if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) + bglVertex2fv(tf->uv[3]); + } + } + bglEnd(); + } + + glPointSize(1.0); +} + +void draw_uvedit_main(SpaceImage *sima, ARegion *ar, Scene *scene, Object *obedit) +{ + int show_uvedit, show_uvshadow; + + show_uvedit= get_space_image_show_uvedit(sima, obedit); + show_uvshadow= get_space_image_show_uvshadow(sima, obedit); + + if(show_uvedit || show_uvshadow) { + /* this is basically the same object_handle_update as in the 3d view, + * here we have to do it as well for the object we are editing if we + * are displaying the final result */ + if(obedit && (sima->flag & SI_DRAWSHADOW)) + object_handle_update(scene, obedit); + + if(show_uvshadow) + draw_uvs_shadow(sima, obedit); + else + draw_uvs(sima, scene, obedit); + + if(show_uvedit) + drawcursor_sima(sima, ar); + + // XXX becomes operator draw extra: + // draw_image_transform(ibuf, xuser_asp, yuser_asp); + } +} + diff --git a/source/blender/editors/uvedit/uvedit_intern.h b/source/blender/editors/uvedit/uvedit_intern.h new file mode 100644 index 00000000000..99656bf9fbc --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_intern.h @@ -0,0 +1,72 @@ +/** + * $Id: + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2008 Blender Foundation. + * All rights reserved. + * + * + * Contributor(s): Blender Foundation + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#ifndef ED_UVEDIT_INTERN_H +#define ED_UVEDIT_INTERN_H + +struct SpaceImage; +struct EditFace; +struct MTFace; +struct Scene; +struct Image; +struct Object; +struct wmOperatorType; + +#define UV_SELECT_ALL 1 +#define UV_SELECT_PINNED 2 + +/* id can be from 0 to 3 */ +#define TF_PIN_MASK(id) (TF_PIN1 << id) +#define TF_SEL_MASK(id) (TF_SEL1 << id) + +/* state testing */ +int uvedit_test(struct Object *obedit); +int uvedit_test_silent(struct Object *obedit); + +/* visibility and selection */ +int uvedit_face_visible_nolocal(struct Scene *scene, struct EditFace *efa); +int uvedit_face_visible(struct Scene *scene, struct Image *ima, struct EditFace *efa, struct MTFace *tf); + +int uvedit_face_selected(struct Scene *scene, struct EditFace *efa, struct MTFace *tf); +void uvedit_face_select(struct Scene *scene, struct EditFace *efa, struct MTFace *tf); +void uvedit_face_deselect(struct Scene *scene, struct EditFace *efa, struct MTFace *tf); + +int uvedit_uv_selected(struct Scene *scene, struct EditFace *efa, struct MTFace *tf, int i); +void uvedit_uv_select(struct Scene *scene, struct EditFace *efa, struct MTFace *tf, int i); +void uvedit_uv_deselect(struct Scene *scene, struct EditFace *efa, struct MTFace *tf, int i); + +/* geometric utilities */ +void uv_center(float uv[][2], float cent[2], int quad); +float uv_area(float uv[][2], int quad); +void uv_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy); + +/* operators */ +void UV_OT_de_select_all(struct wmOperatorType *ot); + +#endif /* ED_UVEDIT_INTERN_H */ + diff --git a/source/blender/editors/uvedit/uvedit_ops.c b/source/blender/editors/uvedit/uvedit_ops.c new file mode 100644 index 00000000000..7dc4b6919e9 --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_ops.c @@ -0,0 +1,2401 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <stdlib.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_object_types.h" +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_space_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_windowmanager_types.h" + +#include "BLI_arithb.h" +#include "BLI_blenlib.h" +#include "BLI_editVert.h" + +#include "BKE_context.h" +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_image.h" +#include "BKE_library.h" +#include "BKE_mesh.h" +#include "BKE_utildefines.h" + +#include "IMB_imbuf_types.h" // XXX remove? + +#include "ED_mesh.h" +#include "ED_screen.h" + +#include "RNA_access.h" +#include "RNA_define.h" +#include "RNA_types.h" + +#include "WM_api.h" +#include "WM_types.h" + +#include "uvedit_intern.h" + +/* local prototypes */ +static void sel_uvco_inside_radius(SpaceImage *sima, Scene *scene, short sel, EditFace *efa, MTFace *tface, int index, float *offset, float *ell, short select_index); +void uvedit_selectionCB(SpaceImage *sima, Scene *scene, ARegion *ar, short selecting, Object *obedit, short *mval, float rad); /* used in edit.c */ +void select_edgeloop_tface_uv(bContext *C, Scene *scene, Image *ima, Object *obedit, EditFace *startefa, int starta, int shift, int *flush); +void select_linked_tface_uv(bContext *C, Scene *scene, Image *ima, Object *obedit, int mode); +void uvface_setsel__internal(bContext *C, SpaceImage *sima, Scene *scene, Object *obedit, short select); + +/************************* state testing ************************/ + +int uvedit_test_silent(Object *obedit) +{ + if(obedit->type != OB_MESH) + return 0; + + return EM_texFaceCheck(((Mesh*)obedit->data)->edit_mesh); +} + +int uvedit_test(Object *obedit) +{ + // XXX if(!obedit) + // XXX error("Enter Edit Mode to perform this action"); + + return uvedit_test_silent(obedit); +} + +/************************* assign image ************************/ + +void ED_uvedit_assign_image(Scene *scene, Object *obedit, Image *ima, Image *previma) +{ + EditMesh *em; + EditFace *efa; + MTFace *tf; + int update; + + /* skip assigning these procedural images... */ + if(ima && (ima->type==IMA_TYPE_R_RESULT || ima->type==IMA_TYPE_COMPOSITE)) + return; + + /* verify we have a mesh we can work with */ + if(!obedit || (obedit->type != OB_MESH)) + return; + + em= ((Mesh*)obedit->data)->edit_mesh; + if(!em || !em->faces.first); + return; + + /* ensure we have a uv layer */ + if(!CustomData_has_layer(&em->fdata, CD_MTFACE)) { + EM_add_data_layer(em, &em->fdata, CD_MTFACE); + update= 1; + } + + /* now assign to all visible faces */ + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, previma, efa, tf)) { + if(ima) { + tf->tpage= ima; + tf->mode |= TF_TEX; + + if(ima->tpageflag & IMA_TILES) tf->mode |= TF_TILES; + else tf->mode &= ~TF_TILES; + + if(ima->id.us==0) id_us_plus(&ima->id); + else id_lib_extern(&ima->id); + } + else { + tf->tpage= NULL; + tf->mode &= ~TF_TEX; + } + + update = 1; + } + } + + /* and update depdency graph */ + if(update) { + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + /* XXX ensure undo, notifiers? */ + } +} + +/* dotile - 1, set the tile flag (from the space image) + * 2, set the tile index for the faces. */ +void ED_uvedit_set_tile(Scene *scene, Object *obedit, Image *ima, int curtile, int dotile) +{ + EditMesh *em; + EditFace *efa; + MTFace *tf; + + /* verify if we have something to do */ + if(!ima || !uvedit_test_silent(obedit)) + return; + + /* skip assigning these procedural images... */ + if(ima->type==IMA_TYPE_R_RESULT || ima->type==IMA_TYPE_COMPOSITE) + return; + + em= ((Mesh*)obedit->data)->edit_mesh; + + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(efa->h==0 && efa->f & SELECT) { + if(dotile==1) { + /* set tile flag */ + if(ima->tpageflag & IMA_TILES) + tf->mode |= TF_TILES; + else + tf->mode &= ~TF_TILES; + } + else if(dotile==2) + tf->tile= curtile; /* set tile index */ + } + } + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + // XXX WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); +} + +/*********************** connection testing *********************/ + +static void uvedit_connection_limit(bContext *C, float *limit, float pixellimit) +{ + ImBuf *ibuf= CTX_data_edit_image_buffer(C); + float width, height; + + if(ibuf && ibuf->x > 0 && ibuf->y > 0) { + width= ibuf->x; + height= ibuf->y; + } + else { + width= 256.0f; + height= 256.0f; + } + + limit[0]= pixellimit/width; + limit[1]= pixellimit/height; +} + +/*************** visibility and selection utilities **************/ + +int uvedit_face_visible_nolocal(Scene *scene, EditFace *efa) +{ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) + return (efa->h==0); + else + return (efa->h==0 && (efa->f & SELECT)); +} + +int uvedit_face_visible(Scene *scene, Image *ima, EditFace *efa, MTFace *tf) +{ + if(scene->toolsettings->uv_flag & UV_SHOW_SAME_IMAGE) + return (tf->tpage==ima)? uvedit_face_visible_nolocal(scene, efa): 0; + else + return uvedit_face_visible_nolocal(scene, efa); +} + +int uvedit_face_selected(Scene *scene, EditFace *efa, MTFace *tf) +{ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) + return (efa->f & SELECT); + else + return (!(~tf->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) &&(!efa->v4 || tf->flag & TF_SEL4)); +} + +void uvedit_face_select(Scene *scene, EditFace *efa, MTFace *tf) +{ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) + EM_select_face(efa, 1); + else + tf->flag |= (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); +} + +void uvedit_face_deselect(Scene *scene, EditFace *efa, MTFace *tf) +{ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) + EM_select_face(efa, 0); + else + tf->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); +} + +int uvedit_uv_selected(Scene *scene, EditFace *efa, MTFace *tf, int i) +{ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(scene->selectmode == SCE_SELECT_FACE) + return (efa->f & SELECT); + else + return (*(&efa->v1 + i))->f & SELECT; + } + else + return tf->flag & TF_SEL_MASK(i); +} + +void uvedit_uv_select(Scene *scene, EditFace *efa, MTFace *tf, int i) +{ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(scene->selectmode == SCE_SELECT_FACE) + EM_select_face(efa, 1); + else + (*(&efa->v1 + i))->f |= SELECT; + } + else + tf->flag |= TF_SEL_MASK(i); +} + +void uvedit_uv_deselect(Scene *scene, EditFace *efa, MTFace *tf, int i) +{ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(scene->selectmode == SCE_SELECT_FACE) + EM_select_face(efa, 0); + else + (*(&efa->v1 + i))->f &= ~SELECT; + } + else + tf->flag &= ~TF_SEL_MASK(i); +} + +/*********************** geometric utilities ***********************/ + +void uv_center(float uv[][2], float cent[2], int quad) +{ + if(quad) { + cent[0] = (uv[0][0] + uv[1][0] + uv[2][0] + uv[3][0]) / 4.0; + cent[1] = (uv[0][1] + uv[1][1] + uv[2][1] + uv[3][1]) / 4.0; + } + else { + cent[0] = (uv[0][0] + uv[1][0] + uv[2][0]) / 3.0; + cent[1] = (uv[0][1] + uv[1][1] + uv[2][1]) / 3.0; + } +} + +float uv_area(float uv[][2], int quad) +{ + if(quad) + return AreaF2Dfl(uv[0], uv[1], uv[2]) + AreaF2Dfl(uv[0], uv[2], uv[3]); + else + return AreaF2Dfl(uv[0], uv[1], uv[2]); +} + +void uv_copy_aspect(float uv_orig[][2], float uv[][2], float aspx, float aspy) +{ + uv[0][0] = uv_orig[0][0]*aspx; + uv[0][1] = uv_orig[0][1]*aspy; + + uv[1][0] = uv_orig[1][0]*aspx; + uv[1][1] = uv_orig[1][1]*aspy; + + uv[2][0] = uv_orig[2][0]*aspx; + uv[2][1] = uv_orig[2][1]*aspy; + + uv[3][0] = uv_orig[3][0]*aspx; + uv[3][1] = uv_orig[3][1]*aspy; +} + +int ED_uvedit_minmax(Scene *scene, Image *ima, Object *obedit, float *min, float *max) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tf; + int sel; + + INIT_MINMAX2(min, max); + + sel= 0; + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(uvedit_uv_selected(scene, efa, tf, 0)) { DO_MINMAX2(tf->uv[0], min, max); sel = 1; } + if(uvedit_uv_selected(scene, efa, tf, 1)) { DO_MINMAX2(tf->uv[1], min, max); sel = 1; } + if(uvedit_uv_selected(scene, efa, tf, 2)) { DO_MINMAX2(tf->uv[2], min, max); sel = 1; } + if(efa->v4 && (uvedit_uv_selected(scene, efa, tf, 3))) { DO_MINMAX2(tf->uv[3], min, max); sel = 1; } + } + } + + return sel; +} + +int uvedit_center(Scene *scene, Image *ima, Object *obedit, float *cent, int mode) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tf; + float min[2], max[2]; + int change= 0; + + if(mode==0) { + if(ED_uvedit_minmax(scene, ima, obedit, min, max)) + change = 1; + } + else if(mode==1) { + INIT_MINMAX2(min, max); + + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(uvedit_uv_selected(scene, efa, tf, 0)) { DO_MINMAX2(tf->uv[0], min, max); change= 1;} + if(uvedit_uv_selected(scene, efa, tf, 1)) { DO_MINMAX2(tf->uv[1], min, max); change= 1;} + if(uvedit_uv_selected(scene, efa, tf, 2)) { DO_MINMAX2(tf->uv[2], min, max); change= 1;} + if(efa->v4 && (uvedit_uv_selected(scene, efa, tf, 3))) { DO_MINMAX2(tf->uv[3], min, max); change= 1;} + } + } + } + + if(change) { + cent[0]= (min[0]+max[0])/2.0; + cent[1]= (min[1]+max[1])/2.0; + + return 1; + } + + return 0; +} + +/************************** constraints ****************************/ + +void uvedit_constrain_square(Scene *scene, Image *ima, EditMesh *em) +{ + EditFace *efa; + MTFace *tf; + + /* if 1 vertex selected: doit (with the selected vertex) */ + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->v4) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(uvedit_uv_selected(scene, efa, tf, 0)) { + if(tf->uv[1][0] == tf->uv[2][0] ) { + tf->uv[1][1]= tf->uv[0][1]; + tf->uv[3][0]= tf->uv[0][0]; + } + else { + tf->uv[1][0]= tf->uv[0][0]; + tf->uv[3][1]= tf->uv[0][1]; + } + + } + + if(uvedit_uv_selected(scene, efa, tf, 1)) { + if(tf->uv[2][1] == tf->uv[3][1] ) { + tf->uv[2][0]= tf->uv[1][0]; + tf->uv[0][1]= tf->uv[1][1]; + } + else { + tf->uv[2][1]= tf->uv[1][1]; + tf->uv[0][0]= tf->uv[1][0]; + } + + } + + if(uvedit_uv_selected(scene, efa, tf, 2)) { + if(tf->uv[3][0] == tf->uv[0][0] ) { + tf->uv[3][1]= tf->uv[2][1]; + tf->uv[1][0]= tf->uv[2][0]; + } + else { + tf->uv[3][0]= tf->uv[2][0]; + tf->uv[1][1]= tf->uv[2][1]; + } + } + + if(uvedit_uv_selected(scene, efa, tf, 3)) { + if(tf->uv[0][1] == tf->uv[1][1] ) { + tf->uv[0][0]= tf->uv[3][0]; + tf->uv[2][1]= tf->uv[3][1]; + } + else { + tf->uv[0][1]= tf->uv[3][1]; + tf->uv[2][0]= tf->uv[3][0]; + } + } + } + } + } +} + +/* ******************** mirror operator **************** */ + +/* XXX */ +#if 0 + short mode= 0; + mode= pupmenu("Mirror%t|X Axis%x1|Y Axis%x2|"); +#endif + +static int mirror_exec(bContext *C, wmOperator *op) +{ + float mat[3][3]; + int axis; + + Mat3One(mat); + axis= RNA_enum_get(op->ptr, "axis"); + + if(axis == 'x') { + /* XXX initTransform(TFM_MIRROR, CTX_NO_PET|CTX_AUTOCONFIRM); + BIF_setSingleAxisConstraint(mat[0], " on X axis"); + Transform(); */ + } + else { + /* XXX initTransform(TFM_MIRROR, CTX_NO_PET|CTX_AUTOCONFIRM); + BIF_setSingleAxisConstraint(mat[1], " on Y axis"); + Transform(); */ + } + + return OPERATOR_FINISHED; +} + +void UV_OT_mirror(wmOperatorType *ot) +{ + PropertyRNA *prop; + static EnumPropertyItem axis_items[] = { + {'x', "MIRROR_X", "Mirror X", "Mirror UVs over X axis."}, + {'y', "MIRROR_Y", "Mirror Y", "Mirror UVs over Y axis."}, + {0, NULL, NULL, NULL}}; + + /* identifiers */ + ot->name= "Mirror"; + ot->idname= "UV_OT_mirror"; + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* api callbacks */ + ot->exec= mirror_exec; + ot->poll= ED_operator_uvedit; + + /* properties */ + prop= RNA_def_property(ot->srna, "axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, axis_items); + RNA_def_property_enum_default(prop, 'x'); + RNA_def_property_ui_text(prop, "Axis", "Axis to mirror UV locations over."); +} + +/* ******************** align operator **************** */ + +/* XXX */ +#if 0 +void weld_align_menu_tface_uv(bContext *C) +{ + short mode= 0; + + mode= pupmenu("Weld/Align%t|Weld%x1|Align Auto%x2|Align X%x3|Align Y%x4"); + + if(mode==-1) return; + if(mode==1) weld_align_uv(C, 'w'); + else if(mode==2) weld_align_uv(C, 'a'); + else if(mode==3) weld_align_uv(C, 'x'); + else if(mode==4) weld_align_uv(C, 'y'); +} +#endif + +static void weld_align_uv(bContext *C, int tool) +{ + Scene *scene; + Object *obedit; + Image *ima; + EditMesh *em; + EditFace *efa; + MTFace *tf; + float cent[2], min[2], max[2]; + + scene= CTX_data_scene(C); + obedit= CTX_data_edit_object(C); + em= ((Mesh*)obedit->data)->edit_mesh; + ima= CTX_data_edit_image(C); + + INIT_MINMAX2(min, max); + + if(tool == 'a') { + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(uvedit_uv_selected(scene, efa, tf, 0)) + DO_MINMAX2(tf->uv[0], min, max) + if(uvedit_uv_selected(scene, efa, tf, 1)) + DO_MINMAX2(tf->uv[1], min, max) + if(uvedit_uv_selected(scene, efa, tf, 2)) + DO_MINMAX2(tf->uv[2], min, max) + if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) + DO_MINMAX2(tf->uv[3], min, max) + } + } + + tool= (max[0]-min[0] >= max[1]-min[1])? 'y': 'x'; + } + + uvedit_center(scene, ima, obedit, cent, 0); + + if(tool == 'x' || tool == 'w') { + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(uvedit_uv_selected(scene, efa, tf, 0)) + tf->uv[0][0]= cent[0]; + if(uvedit_uv_selected(scene, efa, tf, 1)) + tf->uv[1][0]= cent[0]; + if(uvedit_uv_selected(scene, efa, tf, 2)) + tf->uv[2][0]= cent[0]; + if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) + tf->uv[3][0]= cent[0]; + } + } + } + + if(tool == 'y' || tool == 'w') { + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(uvedit_uv_selected(scene, efa, tf, 0)) + tf->uv[0][1]= cent[1]; + if(uvedit_uv_selected(scene, efa, tf, 1)) + tf->uv[1][1]= cent[1]; + if(uvedit_uv_selected(scene, efa, tf, 2)) + tf->uv[2][1]= cent[1]; + if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) + tf->uv[3][1]= cent[1]; + } + } + } + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); // XXX +} + +static int align_exec(bContext *C, wmOperator *op) +{ + weld_align_uv(C, RNA_enum_get(op->ptr, "axis")); + + return OPERATOR_FINISHED; +} + +void UV_OT_align(wmOperatorType *ot) +{ + PropertyRNA *prop; + static EnumPropertyItem axis_items[] = { + {'a', "ALIGN_AUTO", "Align Auto", "Automatically choose the axis on which there is most alignment already."}, + {'x', "ALIGN_X", "Align X", "Align UVs on X axis."}, + {'y', "ALIGN_Y", "Align Y", "Align UVs on Y axis."}, + {0, NULL, NULL, NULL}}; + + /* identifiers */ + ot->name= "Align"; + ot->idname= "UV_OT_align"; + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* api callbacks */ + ot->exec= align_exec; + ot->poll= ED_operator_uvedit; + + /* properties */ + prop= RNA_def_property(ot->srna, "axis", PROP_ENUM, PROP_NONE); + RNA_def_property_enum_items(prop, axis_items); + RNA_def_property_enum_default(prop, 'a'); + RNA_def_property_ui_text(prop, "Axis", "Axis to align UV locations on."); +} + +/* ******************** weld operator **************** */ + +static int weld_exec(bContext *C, wmOperator *op) +{ + weld_align_uv(C, 'w'); + + return OPERATOR_FINISHED; +} + +void UV_OT_weld(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Weld"; + ot->idname= "UV_OT_weld"; + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* api callbacks */ + ot->exec= weld_exec; + ot->poll= ED_operator_uvedit; +} + +/* ******************** stitch operator **************** */ + +/* just for averaging UVs */ +typedef struct UVVertAverage { + float uv[2]; + int count; +} UVVertAverage; + +static int stitch_exec(bContext *C, wmOperator *op) +{ + Scene *scene; + Object *obedit; + EditMesh *em; + EditFace *efa; + EditVert *eve; + Image *ima; + MTFace *tf; + + scene= CTX_data_scene(C); + obedit= CTX_data_edit_object(C); + em= ((Mesh*)obedit->data)->edit_mesh; + ima= CTX_data_edit_image(C); + + if(RNA_boolean_get(op->ptr, "use_limit")) { + UvVertMap *vmap; + UvMapVert *vlist, *iterv; + float newuv[2], limit[2], pixels; + int a, vtot; + + pixels= RNA_float_get(op->ptr, "limit"); + uvedit_connection_limit(C, limit, pixels); + + EM_init_index_arrays(em, 0, 0, 1); + vmap= EM_make_uv_vert_map(em, 1, 0, limit); + + if(vmap == NULL) + return OPERATOR_CANCELLED; + + for(a=0, eve= em->verts.first; eve; a++, eve= eve->next) { + vlist= EM_get_uv_map_vert(vmap, a); + + while(vlist) { + newuv[0]= 0; newuv[1]= 0; + vtot= 0; + + for(iterv=vlist; iterv; iterv=iterv->next) { + if((iterv != vlist) && iterv->separate) + break; + + efa = EM_get_face_for_index(iterv->f); + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_uv_selected(scene, efa, tf, iterv->tfindex)) { + newuv[0] += tf->uv[iterv->tfindex][0]; + newuv[1] += tf->uv[iterv->tfindex][1]; + vtot++; + } + } + + if(vtot > 1) { + newuv[0] /= vtot; newuv[1] /= vtot; + + for(iterv=vlist; iterv; iterv=iterv->next) { + if((iterv != vlist) && iterv->separate) + break; + + efa = EM_get_face_for_index(iterv->f); + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_uv_selected(scene, efa, tf, iterv->tfindex)) { + tf->uv[iterv->tfindex][0]= newuv[0]; + tf->uv[iterv->tfindex][1]= newuv[1]; + } + } + } + + vlist= iterv; + } + } + + EM_free_uv_vert_map(vmap); + EM_free_index_arrays(); + } + else { + UVVertAverage *uv_average, *uvav; + int count; + + // index and count verts + for(count=0, eve=em->verts.first; eve; count++, eve= eve->next) + eve->tmp.l = count; + + uv_average= MEM_callocN(sizeof(UVVertAverage)*count, "Stitch"); + + // gather uv averages per vert + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(uvedit_uv_selected(scene, efa, tf, 0)) { + uvav = uv_average + efa->v1->tmp.l; + uvav->count++; + uvav->uv[0] += tf->uv[0][0]; + uvav->uv[1] += tf->uv[0][1]; + } + + if(uvedit_uv_selected(scene, efa, tf, 1)) { + uvav = uv_average + efa->v2->tmp.l; + uvav->count++; + uvav->uv[0] += tf->uv[1][0]; + uvav->uv[1] += tf->uv[1][1]; + } + + if(uvedit_uv_selected(scene, efa, tf, 2)) { + uvav = uv_average + efa->v3->tmp.l; + uvav->count++; + uvav->uv[0] += tf->uv[2][0]; + uvav->uv[1] += tf->uv[2][1]; + } + + if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) { + uvav = uv_average + efa->v4->tmp.l; + uvav->count++; + uvav->uv[0] += tf->uv[3][0]; + uvav->uv[1] += tf->uv[3][1]; + } + } + } + + // apply uv welding + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(uvedit_uv_selected(scene, efa, tf, 0)) { + uvav = uv_average + efa->v1->tmp.l; + tf->uv[0][0] = uvav->uv[0]/uvav->count; + tf->uv[0][1] = uvav->uv[1]/uvav->count; + } + + if(uvedit_uv_selected(scene, efa, tf, 1)) { + uvav = uv_average + efa->v2->tmp.l; + tf->uv[1][0] = uvav->uv[0]/uvav->count; + tf->uv[1][1] = uvav->uv[1]/uvav->count; + } + + if(uvedit_uv_selected(scene, efa, tf, 2)) { + uvav = uv_average + efa->v3->tmp.l; + tf->uv[2][0] = uvav->uv[0]/uvav->count; + tf->uv[2][1] = uvav->uv[1]/uvav->count; + } + + if(efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) { + uvav = uv_average + efa->v4->tmp.l; + tf->uv[3][0] = uvav->uv[0]/uvav->count; + tf->uv[3][1] = uvav->uv[1]/uvav->count; + } + } + } + + MEM_freeN(uv_average); + } + + // XXX if(sima->flag & SI_BE_SQUARE) + // XXX uvedit_constrain_square(scene, sima->image, em); + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); // XXX + + return OPERATOR_FINISHED; +} + +void UV_OT_stitch(wmOperatorType *ot) +{ + PropertyRNA *prop; + + /* identifiers */ + ot->name= "Stitch"; + ot->idname= "UV_OT_stitch"; + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* api callbacks */ + ot->exec= stitch_exec; + ot->poll= ED_operator_uvedit; + + /* properties */ + prop= RNA_def_property(ot->srna, "use_limit", PROP_BOOLEAN, PROP_NONE); + RNA_def_property_boolean_default(prop, 1); + RNA_def_property_ui_text(prop, "Use Limit", "Stitch UVs within a specified limit distance."); + + prop= RNA_def_property(ot->srna, "limit", PROP_FLOAT, PROP_UNSIGNED); + RNA_def_property_float_default(prop, 20.0); + RNA_def_property_ui_text(prop, "Limit", "Limit distance in image pixels."); +} + +/* ******************** (de)select all operator **************** */ + +static int select_inverse_exec(bContext *C, wmOperator *op) +{ + Scene *scene; + Object *obedit; + EditMesh *em; + EditFace *efa; + Image *ima; + MTFace *tf; + + scene= CTX_data_scene(C); + obedit= CTX_data_edit_object(C); + em= ((Mesh*)obedit->data)->edit_mesh; + ima= CTX_data_edit_image(C); + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + // XXX selectswap_mesh(); + } + else { + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + tf->flag ^= TF_SEL1; + tf->flag ^= TF_SEL2; + tf->flag ^= TF_SEL3; + if(efa->v4) tf->flag ^= TF_SEL4; + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void UV_OT_select_inverse(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select Invert"; + ot->idname= "UV_OT_select_inverse"; + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* api callbacks */ + ot->exec= select_inverse_exec; + ot->poll= ED_operator_uvedit; +} + +/* ******************** (de)select all operator **************** */ + +static int de_select_all_exec(bContext *C, wmOperator *op) +{ + Scene *scene; + Object *obedit; + EditMesh *em; + EditFace *efa; + Image *ima; + MTFace *tf; + int sel; + + scene= CTX_data_scene(C); + obedit= CTX_data_edit_object(C); + em= ((Mesh*)obedit->data)->edit_mesh; + ima= CTX_data_edit_image(C); + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + // XXX deselectall_mesh(); + } + else { + sel= 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(tf->flag & (TF_SEL1+TF_SEL2+TF_SEL3+TF_SEL4)) { + sel= 1; + break; + } + } + } + + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(efa->v4) { + if(sel) tf->flag &= ~(TF_SEL1+TF_SEL2+TF_SEL3+TF_SEL4); + else tf->flag |= (TF_SEL1+TF_SEL2+TF_SEL3+TF_SEL4); + } + else { + if(sel) tf->flag &= ~(TF_SEL1+TF_SEL2+TF_SEL3+TF_SEL4); + else tf->flag |= (TF_SEL1+TF_SEL2+TF_SEL3); + } + } + } + } + + WM_event_add_notifier(C, NC_OBJECT|ND_GEOM_SELECT, obedit); + + return OPERATOR_FINISHED; +} + +void UV_OT_de_select_all(wmOperatorType *ot) +{ + /* identifiers */ + ot->name= "Select/Deselect All"; + ot->idname= "UV_OT_de_select_all"; + ot->flag= OPTYPE_REGISTER/*|OPTYPE_UNDO*/; + + /* api callbacks */ + ot->exec= de_select_all_exec; + ot->poll= ED_operator_uvedit; +} + +/* ******************** mouse select operator **************** */ + +static int msel_hit(float *limit, unsigned int *hitarray, unsigned int vertexid, float **uv, float *uv2, int sticky) +{ + int i; + for(i=0; i< 4; i++) { + if(hitarray[i] == vertexid) { + if(sticky == 2) { + if(fabs(uv[i][0]-uv2[0]) < limit[0] && + fabs(uv[i][1]-uv2[1]) < limit[1]) + return 1; + } + else return 1; + } + } + return 0; +} + +static void find_nearest_uv_edge(Scene *scene, Image *ima, Object *obedit, MTFace **nearesttf, EditFace **nearestefa, int *nearestedge) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + MTFace *tf; + EditFace *efa; + float mvalf[2], v1[2], v2[2]; + int i, nverts, mindist, dist, uval1[2], uval2[2]; + short mval[2]; + + mval[0]= mval[1]= 0; // XXX getmouseco_areawin(mval); + mvalf[0]= mval[0]; + mvalf[1]= mval[1]; + + mindist= 0x7FFFFFF; + *nearesttf= NULL; + *nearestefa= NULL; + *nearestedge= 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(uvedit_face_visible(scene, ima, efa, tf)) { + nverts= efa->v4? 4: 3; + for(i=0; i<nverts; i++) { + uval1[0]= uval1[1]= 0; // XXX uvco_to_areaco_noclip(tf->uv[i], uval1); + uval2[0]= uval2[1]= 0; // XXX uvco_to_areaco_noclip(tf->uv[(i+1)%nverts], uval2); + + v1[0]= uval1[0]; + v1[1]= uval1[1]; + v2[0]= uval2[0]; + v2[1]= uval2[1]; + + dist= PdistVL2Dfl(mvalf, v1, v2); + if(dist < mindist) { + *nearesttf= tf; + *nearestefa= efa; + *nearestedge= i; + mindist= dist; + } + } + } + } +} + +static void find_nearest_tface(Scene *scene, Image *ima, Object *obedit, MTFace **nearesttf, EditFace **nearestefa) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + MTFace *tf; + EditFace *efa; + int i, nverts, mindist, dist, fcenter[2], uval[2]; + short mval[2]; + + mval[0]= mval[1]= 0; // XXX getmouseco_areawin(mval); + + mindist= 0x7FFFFFF; + *nearesttf= NULL; + *nearestefa= NULL; + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + fcenter[0]= fcenter[1]= 0; + nverts= efa->v4? 4: 3; + for(i=0; i<nverts; i++) { + uval[0]= uval[0]= 0; // XXX uvco_to_areaco_noclip(tf->uv[i], uval); + fcenter[0] += uval[0]; + fcenter[1] += uval[1]; + } + + fcenter[0] /= nverts; + fcenter[1] /= nverts; + + dist= abs(mval[0]- fcenter[0])+ abs(mval[1]- fcenter[1]); + if(dist < mindist) { + *nearesttf= tf; + *nearestefa= efa; + mindist= dist; + } + } + } +} + +static int nearest_uv_between(MTFace *tf, int nverts, int id, short *mval, int *uval) +{ + float m[3], v1[3], v2[3], c1, c2; + int id1, id2; + + id1= (id+nverts-1)%nverts; + id2= (id+nverts+1)%nverts; + + m[0] = (float)(mval[0]-uval[0]); + m[1] = (float)(mval[1]-uval[1]); + Vec2Subf(v1, tf->uv[id1], tf->uv[id]); + Vec2Subf(v2, tf->uv[id2], tf->uv[id]); + + /* m and v2 on same side of v-v1? */ + c1= v1[0]*m[1] - v1[1]*m[0]; + c2= v1[0]*v2[1] - v1[1]*v2[0]; + + if(c1*c2 < 0.0f) + return 0; + + /* m and v1 on same side of v-v2? */ + c1= v2[0]*m[1] - v2[1]*m[0]; + c2= v2[0]*v1[1] - v2[1]*v1[0]; + + return (c1*c2 >= 0.0f); +} + +void find_nearest_uv(Scene *scene, Image *ima, Object *obedit, MTFace **nearesttf, EditFace **nearestefa, unsigned int *nearestv, int *nearestuv) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tf; + int i, nverts, mindist, dist, uval[2]; + short mval[2]; + + mval[0]= mval[1]= 0; // XXX getmouseco_areawin(mval); + + mindist= 0x7FFFFFF; + if(nearesttf) *nearesttf= NULL; + if(nearestefa) *nearestefa= NULL; + + if(nearestv) { + EditVert *ev; + for(i=0, ev=em->verts.first; ev; ev = ev->next, i++) + ev->tmp.l = i; + } + + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + nverts= efa->v4? 4: 3; + for(i=0; i<nverts; i++) { + uval[0]= uval[1]= 0; // XXX uvco_to_areaco_noclip(tf->uv[i], uval); + dist= abs(mval[0]-uval[0]) + abs(mval[1]-uval[1]); + + if(uvedit_uv_selected(scene, efa, tf, i)) + dist += 5; + + if(dist<=mindist) { + if(dist==mindist) + if(!nearest_uv_between(tf, nverts, i, mval, uval)) + continue; + + mindist= dist; + *nearestuv= i; + + if(nearesttf) *nearesttf= tf; + if(nearestefa) *nearestefa= efa; + if(nearestv) { + if(i==0) *nearestv= efa->v1->tmp.l; + else if(i==1) *nearestv= efa->v2->tmp.l; + else if(i==2) *nearestv= efa->v3->tmp.l; + else *nearestv= efa->v4->tmp.l; + } + } + } + } + } +} + +void mouse_select_sima(bContext *C, SpaceImage *sima, Scene *scene, Image *ima, Object *obedit) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tf, *nearesttf; + EditFace *nearestefa=NULL; + int a, selectsticky, edgeloop, actface, nearestuv, nearestedge, i, shift, island=0; + char sticky= 0; + int flush = 0; /* 0 == dont flush, 1 == sel, -1 == desel; only use when selection sync is enabled */ + unsigned int hitv[4], nearestv; + float *hituv[4], limit[2]; + + if(!uvedit_test(obedit)) return; + + uvedit_connection_limit(C, limit, 0.05); + + edgeloop= 0; // XXX G.qual & LR_ALTKEY; + shift= 0; // XXX G.qual & LR_SHIFTKEY; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + /* copy from mesh */ + if(scene->selectmode == SCE_SELECT_FACE) { + actface= 1; + sticky= 0; + } + else { + actface= scene->selectmode & SCE_SELECT_FACE; + sticky= 2; + } + } + else { + /* normal operation */ + actface= scene->toolsettings->uv_selectmode == UV_SELECT_FACE; + island= scene->toolsettings->uv_selectmode == UV_SELECT_ISLAND; + + switch(sima->sticky) { + case SI_STICKY_LOC: + sticky=2; + break; + case SI_STICKY_DISABLE: + sticky=0; + break; + case SI_STICKY_VERTEX: + if(0) // XXX G.qual & LR_CTRLKEY) + sticky=0; + else + sticky=1; + break; + } + } + + if(edgeloop) { + find_nearest_uv_edge(scene, ima, obedit, &nearesttf, &nearestefa, &nearestedge); + if(nearesttf==NULL) + return; + + select_edgeloop_tface_uv(C, scene, ima, obedit, nearestefa, nearestedge, shift, &flush); + } + else if(actface) { + find_nearest_tface(scene, ima, obedit, &nearesttf, &nearestefa); + if(nearesttf==NULL) + return; + + EM_set_actFace(em, nearestefa); + + for(i=0; i<4; i++) + hituv[i]= nearesttf->uv[i]; + + hitv[0]= nearestefa->v1->tmp.l; + hitv[1]= nearestefa->v2->tmp.l; + hitv[2]= nearestefa->v3->tmp.l; + + if(nearestefa->v4) hitv[3]= nearestefa->v4->tmp.l; + else hitv[3]= 0xFFFFFFFF; + } + else if(island) { + + } + else { + find_nearest_uv(scene, ima, obedit, &nearesttf, &nearestefa, &nearestv, &nearestuv); + if(nearesttf==NULL) + return; + + if(sticky) { + for(i=0; i<4; i++) + hitv[i]= 0xFFFFFFFF; + hitv[nearestuv]= nearestv; + hituv[nearestuv]= nearesttf->uv[nearestuv]; + } + } + + if(island) { + if(shift) select_linked_tface_uv(C, scene, ima, obedit, 1); + else select_linked_tface_uv(C, scene, ima, obedit, 0); + } + else if(!edgeloop && shift) { + /* (de)select face */ + if(actface) { + if(uvedit_face_selected(scene, nearestefa, nearesttf)) { + uvedit_face_deselect(scene, nearestefa, nearesttf); + selectsticky= 0; + } + else { + uvedit_face_select(scene, nearestefa, nearesttf); + selectsticky= 1; + } + flush = -1; + } + /* (de)select uv node */ + else { + if(uvedit_uv_selected(scene, nearestefa, nearesttf, nearestuv)) { + uvedit_uv_deselect(scene, nearestefa, nearesttf, nearestuv); + selectsticky= 0; + } + else { + uvedit_uv_select(scene, nearestefa, nearesttf, nearestuv); + selectsticky= 1; + } + flush = 1; + } + + /* (de)select sticky uv nodes */ + if(sticky || actface) { + EditVert *ev; + + for(a=0, ev=em->verts.first; ev; ev = ev->next, a++) + ev->tmp.l = a; + + /* deselect */ + if(selectsticky==0) { + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + /*if(nearesttf && tf!=nearesttf) tf->flag &=~ TF_ACTIVE;*/ /* TODO - deal with editmesh active face */ + if(!sticky) continue; + + if(msel_hit(limit, hitv, efa->v1->tmp.l, hituv, tf->uv[0], sticky)) + uvedit_uv_deselect(scene, efa, tf, 0); + if(msel_hit(limit, hitv, efa->v2->tmp.l, hituv, tf->uv[1], sticky)) + uvedit_uv_deselect(scene, efa, tf, 1); + if(msel_hit(limit, hitv, efa->v3->tmp.l, hituv, tf->uv[2], sticky)) + uvedit_uv_deselect(scene, efa, tf, 2); + if(efa->v4) + if(msel_hit(limit, hitv, efa->v4->tmp.l, hituv, tf->uv[3], sticky)) + uvedit_uv_deselect(scene, efa, tf, 3); + } + } + flush = -1; + } + /* select */ + else { + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(!sticky) continue; + if(msel_hit(limit, hitv, efa->v1->tmp.l, hituv, tf->uv[0], sticky)) + uvedit_uv_select(scene, efa, tf, 0); + if(msel_hit(limit, hitv, efa->v2->tmp.l, hituv, tf->uv[1], sticky)) + uvedit_uv_select(scene, efa, tf, 1); + if(msel_hit(limit, hitv, efa->v3->tmp.l, hituv, tf->uv[2], sticky)) + uvedit_uv_select(scene, efa, tf, 2); + if(efa->v4) + if(msel_hit(limit, hitv, efa->v4->tmp.l, hituv, tf->uv[3], sticky)) + uvedit_uv_select(scene, efa, tf, 3); + } + } + + if(actface) + EM_set_actFace(em, nearestefa); + + flush = 1; + } + } + } + else if(!edgeloop) { + /* select face and deselect other faces */ + if(actface) { + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uvedit_face_deselect(scene, efa, tf); + } + if(nearesttf) { + uvedit_face_select(scene, nearestefa, nearesttf); + EM_set_actFace(em, nearestefa); + } + + } + + /* deselect uvs, and select sticky uvs */ + for(efa= em->faces.first; efa; efa= efa->next) { + tf= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(!actface) uvedit_face_deselect(scene, efa, tf); + if(!sticky) continue; + + if(msel_hit(limit, hitv, efa->v1->tmp.l, hituv, tf->uv[0], sticky)) + uvedit_uv_select(scene, efa, tf, 0); + if(msel_hit(limit, hitv, efa->v2->tmp.l, hituv, tf->uv[1], sticky)) + uvedit_uv_select(scene, efa, tf, 1); + if(msel_hit(limit, hitv, efa->v3->tmp.l, hituv, tf->uv[2], sticky)) + uvedit_uv_select(scene, efa, tf, 2); + if(efa->v4) + if(msel_hit(limit, hitv, efa->v4->tmp.l, hituv, tf->uv[3], sticky)) + uvedit_uv_select(scene, efa, tf, 3); + flush= 1; + } + } + + if(!actface) { + uvedit_uv_select(scene, nearestefa, nearesttf, nearestuv); + flush= 1; + } + } + + // XXX force_draw(1); + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + /* flush for mesh selection */ + if(scene->selectmode != SCE_SELECT_FACE) { + if(flush==1) EM_select_flush(em); + else if(flush==-1) EM_deselect_flush(em); + } + // XXX allqueue(REDRAWVIEW3D, 0); /* mesh selection has changed */ + } + + // XXX BIF_undo_push("Select UV"); + // XXX rightmouse_transform(); +} + +void borderselect_sima(bContext *C, SpaceImage *sima, Scene *scene, Image *ima, Object *obedit, short whichuvs) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tface; + rcti rect; + rctf rectf; + int val, ok = 1; + short mval[2], select; + + if(!uvedit_test(obedit)) return; + + val= 0; // XXX get_border(&rect, 3); + select = 0; // XXX (val==LEFTMOUSE) ? 1 : 0; + + if(val) { + mval[0]= rect.xmin; + mval[1]= rect.ymin; + // XXX areamouseco_to_ipoco(G.v2d, mval, &rectf.xmin, &rectf.ymin); + mval[0]= rect.xmax; + mval[1]= rect.ymax; + // XXX areamouseco_to_ipoco(G.v2d, mval, &rectf.xmax, &rectf.ymax); + + if(0) { // XXX draw_uvs_face_check() && whichuvs != UV_SELECT_PINNED) { + float cent[2]; + ok = 0; + for(efa= em->faces.first; efa; efa= efa->next) { + /* assume not touched */ + efa->tmp.l = 0; + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tface)) { + uv_center(tface->uv, cent, efa->v4 != NULL); + if(BLI_in_rctf(&rectf, cent[0], cent[1])) { + efa->tmp.l = ok = 1; + } + } + } + /* (de)selects all tagged faces and deals with sticky modes */ + if(ok) + uvface_setsel__internal(C, sima, scene, obedit, select); + } + else { + for(efa= em->faces.first; efa; efa= efa->next) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tface)) { + if(whichuvs == UV_SELECT_ALL || (scene->toolsettings->uv_flag & UV_SYNC_SELECTION) ) { + /* UV_SYNC_SELECTION - cant do pinned selection */ + if(BLI_in_rctf(&rectf, tface->uv[0][0], tface->uv[0][1])) { + if(select) uvedit_uv_select(scene, efa, tface, 0); + else uvedit_uv_deselect(scene, efa, tface, 0); + } + if(BLI_in_rctf(&rectf, tface->uv[1][0], tface->uv[1][1])) { + if(select) uvedit_uv_select(scene, efa, tface, 1); + else uvedit_uv_deselect(scene, efa, tface, 1); + } + if(BLI_in_rctf(&rectf, tface->uv[2][0], tface->uv[2][1])) { + if(select) uvedit_uv_select(scene, efa, tface, 2); + else uvedit_uv_deselect(scene, efa, tface, 2); + } + if(efa->v4 && BLI_in_rctf(&rectf, tface->uv[3][0], tface->uv[3][1])) { + if(select) uvedit_uv_select(scene, efa, tface, 3); + else uvedit_uv_deselect(scene, efa, tface, 3); + } + } else if(whichuvs == UV_SELECT_PINNED) { + if((tface->unwrap & TF_PIN1) && + BLI_in_rctf(&rectf, tface->uv[0][0], tface->uv[0][1])) { + + if(select) uvedit_uv_select(scene, efa, tface, 0); + else uvedit_uv_deselect(scene, efa, tface, 0); + } + if((tface->unwrap & TF_PIN2) && + BLI_in_rctf(&rectf, tface->uv[1][0], tface->uv[1][1])) { + + if(select) uvedit_uv_select(scene, efa, tface, 1); + else uvedit_uv_deselect(scene, efa, tface, 1); + } + if((tface->unwrap & TF_PIN3) && + BLI_in_rctf(&rectf, tface->uv[2][0], tface->uv[2][1])) { + + if(select) uvedit_uv_select(scene, efa, tface, 2); + else uvedit_uv_deselect(scene, efa, tface, 2); + } + if((efa->v4) && (tface->unwrap & TF_PIN4) && BLI_in_rctf(&rectf, tface->uv[3][0], tface->uv[3][1])) { + if(select) uvedit_uv_select(scene, efa, tface, 3); + else uvedit_uv_deselect(scene, efa, tface, 3); + } + } + } + } + } + if(ok) { + /* make sure newly selected vert selection is updated*/ + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + if(scene->selectmode != SCE_SELECT_FACE) { + if(select) EM_select_flush(em); + else EM_deselect_flush(em); + } + } + // XXX allqueue(REDRAWVIEW3D, 0); /* mesh selection has changed */ + + // XXX BIF_undo_push("Border select UV"); + // XXX scrarea_queue_winredraw(curarea); + } + } +} + +int snap_uv_sel_to_curs(Scene *scene, Image *ima, Object *obedit, View2D *v2d) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tface; + short change = 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tface)) { + if(uvedit_uv_selected(scene, efa, tface, 0)) VECCOPY2D(tface->uv[0], v2d->cursor); + if(uvedit_uv_selected(scene, efa, tface, 1)) VECCOPY2D(tface->uv[1], v2d->cursor); + if(uvedit_uv_selected(scene, efa, tface, 2)) VECCOPY2D(tface->uv[2], v2d->cursor); + if(efa->v4) + if(uvedit_uv_selected(scene, efa, tface, 3)) VECCOPY2D(tface->uv[3], v2d->cursor); + change = 1; + } + } + return change; +} + +int snap_uv_sel_to_adj_unsel(Scene *scene, Image *ima, Object *obedit) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + EditVert *eve; + MTFace *tface; + short change = 0; + int count = 0; + float *coords; + short *usercount, users; + + /* set all verts to -1 : an unused index*/ + for(eve= em->verts.first; eve; eve= eve->next) + eve->tmp.l=-1; + + /* index every vert that has a selected UV using it, but only once so as to + * get unique indicies and to count how much to malloc */ + for(efa= em->faces.first; efa; efa= efa->next) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tface)) { + if(uvedit_uv_selected(scene, efa, tface, 0) && efa->v1->tmp.l==-1) efa->v1->tmp.l= count++; + if(uvedit_uv_selected(scene, efa, tface, 1) && efa->v2->tmp.l==-1) efa->v2->tmp.l= count++; + if(uvedit_uv_selected(scene, efa, tface, 2) && efa->v3->tmp.l==-1) efa->v3->tmp.l= count++; + if(efa->v4) + if(uvedit_uv_selected(scene, efa, tface, 3) && efa->v4->tmp.l==-1) efa->v4->tmp.l= count++; + change = 1; + + /* optional speedup */ + efa->tmp.p = tface; + } + else + efa->tmp.p = NULL; + } + + coords = MEM_callocN(sizeof(float)*count*2, "snap to adjacent coords"); + usercount = MEM_callocN(sizeof(short)*count, "snap to adjacent counts"); + + /* add all UV coords from visible, unselected UV coords as well as counting them to average later */ + for(efa= em->faces.first; efa; efa= efa->next) { +// tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); +// if(uvedit_face_visible(scene, ima, efa, tface)) { + if((tface=(MTFace *)efa->tmp.p)) { + + /* is this an unselected UV we can snap to? */ + if(efa->v1->tmp.l >= 0 && (!uvedit_uv_selected(scene, efa, tface, 0))) { + coords[efa->v1->tmp.l*2] += tface->uv[0][0]; + coords[(efa->v1->tmp.l*2)+1] += tface->uv[0][1]; + usercount[efa->v1->tmp.l]++; + change = 1; + } + if(efa->v2->tmp.l >= 0 && (!uvedit_uv_selected(scene, efa, tface, 1))) { + coords[efa->v2->tmp.l*2] += tface->uv[1][0]; + coords[(efa->v2->tmp.l*2)+1] += tface->uv[1][1]; + usercount[efa->v2->tmp.l]++; + change = 1; + } + if(efa->v3->tmp.l >= 0 && (!uvedit_uv_selected(scene, efa, tface, 2))) { + coords[efa->v3->tmp.l*2] += tface->uv[2][0]; + coords[(efa->v3->tmp.l*2)+1] += tface->uv[2][1]; + usercount[efa->v3->tmp.l]++; + change = 1; + } + + if(efa->v4) { + if(efa->v4->tmp.l >= 0 && (!uvedit_uv_selected(scene, efa, tface, 3))) { + coords[efa->v4->tmp.l*2] += tface->uv[3][0]; + coords[(efa->v4->tmp.l*2)+1] += tface->uv[3][1]; + usercount[efa->v4->tmp.l]++; + change = 1; + } + } + } + } + + /* no other verts selected, bail out */ + if(!change) { + MEM_freeN(coords); + MEM_freeN(usercount); + return change; + } + + /* copy the averaged unselected UVs back to the selected UVs */ + for(efa= em->faces.first; efa; efa= efa->next) { +// tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); +// if(uvedit_face_visible(scene, ima, efa, tface)) { + if((tface=(MTFace *)efa->tmp.p)) { + + if( uvedit_uv_selected(scene, efa, tface, 0) && + efa->v1->tmp.l >= 0 && + (users = usercount[efa->v1->tmp.l]) + ) { + tface->uv[0][0] = coords[efa->v1->tmp.l*2] / users; + tface->uv[0][1] = coords[(efa->v1->tmp.l*2)+1] / users; + } + + if( uvedit_uv_selected(scene, efa, tface, 1) && + efa->v2->tmp.l >= 0 && + (users = usercount[efa->v2->tmp.l]) + ) { + tface->uv[1][0] = coords[efa->v2->tmp.l*2] / users; + tface->uv[1][1] = coords[(efa->v2->tmp.l*2)+1] / users; + } + + if( uvedit_uv_selected(scene, efa, tface, 2) && + efa->v3->tmp.l >= 0 && + (users = usercount[efa->v3->tmp.l]) + ) { + tface->uv[2][0] = coords[efa->v3->tmp.l*2] / users; + tface->uv[2][1] = coords[(efa->v3->tmp.l*2)+1] / users; + } + + if(efa->v4) { + if( uvedit_uv_selected(scene, efa, tface, 3) && + efa->v4->tmp.l >= 0 && + (users = usercount[efa->v4->tmp.l]) + ) { + tface->uv[3][0] = coords[efa->v4->tmp.l*2] / users; + tface->uv[3][1] = coords[(efa->v4->tmp.l*2)+1] / users; + } + } + } + } + + MEM_freeN(coords); + MEM_freeN(usercount); + return change; +} + +void snap_coord_to_pixel(float *uvco, float w, float h) +{ + uvco[0] = ((float) ((int)((uvco[0]*w) + 0.5))) / w; + uvco[1] = ((float) ((int)((uvco[1]*h) + 0.5))) / h; +} + +int snap_uv_sel_to_pixels(SpaceImage *sima, Scene *scene, Object *obedit) /* warning, sanity checks must alredy be done */ +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + Image *ima= sima->image; + EditFace *efa; + MTFace *tface; + int width= 0, height= 0; + float w, h; + short change = 0; + + // XXX get_space_image_size(sima, &width, &height); + w = (float)width; + h = (float)height; + + for(efa= em->faces.first; efa; efa= efa->next) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tface)) { + if(uvedit_uv_selected(scene, efa, tface, 0)) snap_coord_to_pixel(tface->uv[0], w, h); + if(uvedit_uv_selected(scene, efa, tface, 1)) snap_coord_to_pixel(tface->uv[1], w, h); + if(uvedit_uv_selected(scene, efa, tface, 2)) snap_coord_to_pixel(tface->uv[2], w, h); + if(efa->v4) + if(uvedit_uv_selected(scene, efa, tface, 3)) snap_coord_to_pixel(tface->uv[3], w, h); + change = 1; + } + } + return change; +} + +void snap_uv_curs_to_pixels(SpaceImage *sima, View2D *v2d) +{ + int width= 0, height= 0; + + // XXX get_space_image_size(sima, &width, &height); + snap_coord_to_pixel(v2d->cursor, width, height); +} + +int snap_uv_curs_to_sel(Scene *scene, Image *ima, Object *obedit, View2D *v2d) +{ + if(!uvedit_test(obedit)) return 0; + return uvedit_center(scene, ima, obedit, v2d->cursor, 0); +} + +void snap_menu_sima(SpaceImage *sima, Scene *scene, Object *obedit, View2D *v2d) +{ + short event; + + if(!uvedit_test(obedit) || !v2d) return; /* !G.v2d should never happen */ + + event = 0; // XXX pupmenu("Snap %t|Selection -> Pixels%x1|Selection -> Cursor%x2|Selection -> Adjacent Unselected%x3|Cursor -> Selection%x4|Cursor -> Pixel%x5"); + switch (event) { + case 1: + if(snap_uv_sel_to_pixels(sima, scene, obedit)) { + // XXX BIF_undo_push("Snap UV Selection to Pixels"); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + } + break; + case 2: + if(snap_uv_sel_to_curs(scene, sima->image, obedit, v2d)) { + // XXX BIF_undo_push("Snap UV Selection to Cursor"); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + } + break; + case 3: + if(snap_uv_sel_to_adj_unsel(scene, sima->image, obedit)) { + // XXX BIF_undo_push("Snap UV Selection to Cursor"); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + } + break; + case 4: + if(snap_uv_curs_to_sel(scene, sima->image, obedit, v2d)) + // XXX allqueue(REDRAWIMAGE, 0); + break; + case 5: + snap_uv_curs_to_pixels(sima, v2d); + // XXX scrarea_queue_winredraw(curarea); + break; + } +} + + +/** This is an ugly function to set the Tface selection flags depending + * on whether its UV coordinates are inside the normalized + * area with radius rad and offset offset. These coordinates must be + * normalized to 1.0 + * Just for readability... + */ + +static void sel_uvco_inside_radius(SpaceImage *sima, Scene *scene, short sel, EditFace *efa, MTFace *tface, int index, float *offset, float *ell, short select_index) +{ + // normalized ellipse: ell[0] = scaleX, + // [1] = scaleY + + float *uv = tface->uv[index]; + float x, y, r2; + + x = (uv[0] - offset[0]) * ell[0]; + y = (uv[1] - offset[1]) * ell[1]; + + r2 = x * x + y * y; + if(r2 < 1.0) { + if(sel == 0 /* XXX LEFTMOUSE */) uvedit_uv_select(scene, efa, tface, select_index); + else uvedit_uv_deselect(scene, efa, tface, select_index); + } +} + +// see below: +/** gets image dimensions of the 2D view 'v' */ +static void getSpaceImageDimension(SpaceImage *sima, ARegion *ar, float *xy) +{ + float zoomx= 0, zoomy= 0; + int width= 0, height= 0; + + // XXX get_space_image_size(sima, &width, &height); + // XXX get_space_image_zoom(sima, ar, &zoomx, &zoomy); + + xy[0]= width*zoomx; + xy[1]= height*zoomy; +} + +/** Callback function called by circle_selectCB to enable + * brush select in UV editor. + */ + +void uvedit_selectionCB(SpaceImage *sima, Scene *scene, ARegion *ar, short selecting, Object *obedit, short *mval, float rad) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + float offset[2]; + MTFace *tface; + float ellipse[2]; // we need to deal with ellipses, as + // non square textures require for circle + // selection. this ellipse is normalized; r = 1.0 + + getSpaceImageDimension(sima, ar, ellipse); + ellipse[0] /= rad; + ellipse[1] /= rad; + + // XXX areamouseco_to_ipoco(G.v2d, mval, &offset[0], &offset[1]); + + if(selecting) { + for(efa= em->faces.first; efa; efa= efa->next) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + sel_uvco_inside_radius(sima, scene, selecting, efa, tface, 0, offset, ellipse, 0); + sel_uvco_inside_radius(sima, scene, selecting, efa, tface, 1, offset, ellipse, 1); + sel_uvco_inside_radius(sima, scene, selecting, efa, tface, 2, offset, ellipse, 2); + if(efa->v4) + sel_uvco_inside_radius(sima, scene, selecting, efa, tface, 3, offset, ellipse, 3); + } + + /* XXX */ +#if 0 + if(G.f & G_DRAWFACES) { /* full redraw only if necessary */ + draw_sel_circle(0, 0, 0, 0, 0); /* signal */ + force_draw(0); + } + else { /* force_draw() is no good here... */ + glDrawBuffer(GL_FRONT); + draw_uvs_sima(); + bglFlush(); + glDrawBuffer(GL_BACK); + } +#endif + + if(selecting == 0 /* XXX LEFTMOUSE */) EM_select_flush(em); + else EM_deselect_flush(em); + + if(sima->lock && (scene->toolsettings->uv_flag & UV_SYNC_SELECTION)) + ; // XXX force_draw_plus(SPACE_VIEW3D, 0); + } +} + +void select_linked_tface_uv(bContext *C, Scene *scene, Image *ima, Object *obedit, int mode) /* TODO */ +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa, *nearestefa=NULL; + MTFace *tf, *nearesttf=NULL; + UvVertMap *vmap; + UvMapVert *vlist, *iterv, *startv; + unsigned int *stack, stacksize= 0, nearestv; + char *flag; + int a, nearestuv, i, nverts, j; + float limit[2]; + + if(!uvedit_test(obedit)) return; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + // XXX error("Can't select linked when Sync Mesh Selection is enabled"); + return; + } + + if(mode == 2) { + nearesttf= NULL; + nearestuv= 0; + } + if(mode!=2) { + find_nearest_uv(scene, ima, obedit, &nearesttf, &nearestefa, &nearestv, &nearestuv); + if(nearesttf==NULL) + return; + } + + uvedit_connection_limit(C, limit, 0.05); + vmap= EM_make_uv_vert_map(em, 1, 1, limit); + if(vmap == NULL) + return; + + stack= MEM_mallocN(sizeof(*stack)* BLI_countlist(&em->faces), "UvLinkStack"); + flag= MEM_callocN(sizeof(*flag)*BLI_countlist(&em->faces), "UvLinkFlag"); + + if(mode == 2) { + for(a=0, efa= em->faces.first; efa; efa= efa->next, a++) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tf)) { + if(tf->flag & (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4)) { + stack[stacksize]= a; + stacksize++; + flag[a]= 1; + } + } + } + } + else { + for(a=0, efa= em->faces.first; efa; efa= efa->next, a++) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(tf == nearesttf) { + stack[stacksize]= a; + stacksize++; + flag[a]= 1; + break; + } + } + } + + while(stacksize > 0) { + stacksize--; + a= stack[stacksize]; + + for(j=0, efa= em->faces.first; efa; efa= efa->next, j++) { + if(j==a) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + break; + } + } + + nverts= efa->v4? 4: 3; + + for(i=0; i<nverts; i++) { + /* make_uv_vert_map_EM sets verts tmp.l to the indicies */ + vlist= EM_get_uv_map_vert(vmap, (*(&efa->v1 + i))->tmp.l); + + startv= vlist; + + for(iterv=vlist; iterv; iterv=iterv->next) { + if(iterv->separate) + startv= iterv; + if(iterv->f == a) + break; + } + + for(iterv=startv; iterv; iterv=iterv->next) { + if((startv != iterv) && (iterv->separate)) + break; + else if(!flag[iterv->f]) { + flag[iterv->f]= 1; + stack[stacksize]= iterv->f;; + stacksize++; + } + } + } + } + + if(mode==0 || mode==2) { + for(a=0, efa= em->faces.first; efa; efa= efa->next, a++) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(flag[a]) + tf->flag |= (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + else + tf->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } + } + else if(mode==1) { + for(a=0, efa= em->faces.first; efa; efa= efa->next, a++) { + if(flag[a]) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(efa->v4) { + if((tf->flag & (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4))) + break; + } + else if(tf->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) + break; + } + } + + if(efa) { + for(a=0, efa= em->faces.first; efa; efa= efa->next, a++) { + if(flag[a]) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + tf->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } + } + } + else { + for(a=0, efa= em->faces.first; efa; efa= efa->next, a++) { + if(flag[a]) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + tf->flag |= (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } + } + } + } + + MEM_freeN(stack); + MEM_freeN(flag); + EM_free_uv_vert_map(vmap); + + // XXX BIF_undo_push("Select linked UV"); + // XXX scrarea_queue_winredraw(curarea); +} + +void unlink_selection(Scene *scene, Image *ima, Object *obedit) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tface; + + if(!uvedit_test(obedit)) return; + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + ; // XXX error("Can't select unlinked when Sync Mesh Selection is enabled"); + return; + } + + for(efa= em->faces.first; efa; efa= efa->next) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tface)) { + if(efa->v4) { + if(~tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4)) + tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3|TF_SEL4); + } + else { + if(~tface->flag & (TF_SEL1|TF_SEL2|TF_SEL3)) + tface->flag &= ~(TF_SEL1|TF_SEL2|TF_SEL3); + } + } + } + + // XXX BIF_undo_push("Unlink UV selection"); + // XXX scrarea_queue_winredraw(curarea); +} + +/* this function sets the selection on tagged faces + * This is needed because setting the selection on a face is done in + * a number of places but it also needs to respect the sticky modes + * for the UV verts - dealing with the sticky modes is best done in a seperate function + * + * de-selects faces that have been tagged on efa->tmp.l + */ +void uvface_setsel__internal(bContext *C, SpaceImage *sima, Scene *scene, Object *obedit, short select) +{ + + /* All functions calling this should call + * draw_uvs_face_check() + */ + + + /* selecting UV Faces with some modes requires us to change + * the selection in other faces (depending on the stickt mode) + * + * This only needs to be done when the Mesh is not used for selection + * (So for sticky modes - vertex or location based) + * */ + + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tf; + int nverts, i; + + if((scene->toolsettings->uv_flag & UV_SYNC_SELECTION)==0 && sima->sticky == SI_STICKY_VERTEX) { + /* tag all verts as untouched, + * then touch the ones that have a face center in the loop + * and select all MTFace UV's that use a touched vert */ + + EditVert *eve; + + for(eve= em->verts.first; eve; eve= eve->next) + eve->tmp.l = 0; + + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->tmp.l) { + if(efa->v4) + efa->v1->tmp.l= efa->v2->tmp.l= efa->v3->tmp.l= efa->v4->tmp.l=1; + else + efa->v1->tmp.l= efa->v2->tmp.l= efa->v3->tmp.l= 1; + } + } + /* now select tagged verts */ + for(efa= em->faces.first; efa; efa= efa->next) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + nverts= efa->v4? 4: 3; + for(i=0; i<nverts; i++) { + if((*(&efa->v1 + i))->tmp.l) { + if(select) + uvedit_uv_select(scene, efa, tf, i); + else + uvedit_uv_deselect(scene, efa, tf, i); + } + } + } + } else if((scene->toolsettings->uv_flag & UV_SYNC_SELECTION)==0 && sima->sticky == SI_STICKY_LOC) { + EditFace *efa_vlist; + MTFace *tf_vlist; + UvMapVert *start_vlist=NULL, *vlist_iter; + struct UvVertMap *vmap; + float limit[2]; + int efa_index; + //EditVert *eve; /* removed vert counting for now */ + //int a; + + uvedit_connection_limit(C, limit, 0.05); + + EM_init_index_arrays(em, 0, 0, 1); + vmap= EM_make_uv_vert_map(em, 0, 0, limit); + + /* verts are numbered above in make_uv_vert_map_EM, make sure this stays true! */ + /*for(a=0, eve= em->verts.first; eve; a++, eve= eve->next) + eve->tmp.l = a; */ + + if(vmap == NULL) + return; + + for(efa_index=0, efa= em->faces.first; efa; efa_index++, efa= efa->next) { + if(efa->tmp.l) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + nverts= efa->v4? 4: 3; + + for(i=0; i<nverts; i++) { + if(select) + uvedit_uv_select(scene, efa, tf, i); + else + uvedit_uv_deselect(scene, efa, tf, i); + + vlist_iter= EM_get_uv_map_vert(vmap, (*(&efa->v1 + i))->tmp.l); + + while (vlist_iter) { + if(vlist_iter->separate) + start_vlist = vlist_iter; + + if(efa_index == vlist_iter->f) + break; + + vlist_iter = vlist_iter->next; + } + + vlist_iter = start_vlist; + while (vlist_iter) { + + if(vlist_iter != start_vlist && vlist_iter->separate) + break; + + if(efa_index != vlist_iter->f) { + efa_vlist = EM_get_face_for_index(vlist_iter->f); + tf_vlist = CustomData_em_get(&em->fdata, efa_vlist->data, CD_MTFACE); + + if(select) + uvedit_uv_select(scene, efa_vlist, tf_vlist, vlist_iter->tfindex); + else + uvedit_uv_deselect(scene, efa_vlist, tf_vlist, vlist_iter->tfindex); + } + vlist_iter = vlist_iter->next; + } + } + } + } + EM_free_index_arrays(); + EM_free_uv_vert_map(vmap); + + } + else { /* SI_STICKY_DISABLE or scene->toolsettings->uv_flag & UV_SYNC_SELECTION */ + for(efa= em->faces.first; efa; efa= efa->next) { + if(efa->tmp.l) { + tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(select) + uvedit_face_select(scene, efa, tf); + else + uvedit_face_deselect(scene, efa, tf); + } + } + } +} + +void pin_tface_uv(Scene *scene, Image *ima, Object *obedit, int mode) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tface; + + if(!uvedit_test(obedit)) return; + + for(efa= em->faces.first; efa; efa= efa->next) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tface)) { + if(mode ==1) { + if(uvedit_uv_selected(scene, efa, tface, 0)) tface->unwrap |= TF_PIN1; + if(uvedit_uv_selected(scene, efa, tface, 1)) tface->unwrap |= TF_PIN2; + if(uvedit_uv_selected(scene, efa, tface, 2)) tface->unwrap |= TF_PIN3; + if(efa->v4) + if(uvedit_uv_selected(scene, efa, tface, 3)) tface->unwrap |= TF_PIN4; + } + else if(mode ==0) { + if(uvedit_uv_selected(scene, efa, tface, 0)) tface->unwrap &= ~TF_PIN1; + if(uvedit_uv_selected(scene, efa, tface, 1)) tface->unwrap &= ~TF_PIN2; + if(uvedit_uv_selected(scene, efa, tface, 2)) tface->unwrap &= ~TF_PIN3; + if(efa->v4) + if(uvedit_uv_selected(scene, efa, tface, 3)) tface->unwrap &= ~TF_PIN4; + } + } + } + + // XXX BIF_undo_push("Pin UV"); + // XXX scrarea_queue_winredraw(curarea); +} + +void select_pinned_tface_uv(Scene *scene, Image *ima, Object *obedit) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditFace *efa; + MTFace *tface; + + if(!uvedit_test(obedit)) return; + + for(efa= em->faces.first; efa; efa= efa->next) { + tface = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + if(uvedit_face_visible(scene, ima, efa, tface)) { + if(tface->unwrap & TF_PIN1) uvedit_uv_select(scene, efa, tface, 0); + if(tface->unwrap & TF_PIN2) uvedit_uv_select(scene, efa, tface, 1); + if(tface->unwrap & TF_PIN3) uvedit_uv_select(scene, efa, tface, 2); + if(efa->v4) { + if(tface->unwrap & TF_PIN4) uvedit_uv_select(scene, efa, tface, 3); + } + + } + } + + if(scene->toolsettings->uv_flag & UV_SYNC_SELECTION) { + // XXX allqueue(REDRAWVIEW3D, 0); /* mesh selection has changed */ + } + + // XXX BIF_undo_push("Select Pinned UVs"); + // XXX scrarea_queue_winredraw(curarea); +} + +/* UV edge loop select, follows same rules as editmesh */ + +static void uv_vertex_loop_flag(UvMapVert *first) +{ + UvMapVert *iterv; + int count= 0; + + for(iterv=first; iterv; iterv=iterv->next) { + if(iterv->separate && iterv!=first) + break; + + count++; + } + + if(count < 5) + first->flag= 1; +} + +static UvMapVert *uv_vertex_map_get(UvVertMap *vmap, EditFace *efa, int a) +{ + UvMapVert *iterv, *first; + + first= EM_get_uv_map_vert(vmap, (*(&efa->v1 + a))->tmp.l); + + for(iterv=first; iterv; iterv=iterv->next) { + if(iterv->separate) + first= iterv; + if(iterv->f == efa->tmp.l) + return first; + } + + return NULL; +} + +static int uv_edge_tag_faces(UvMapVert *first1, UvMapVert *first2, int *totface) +{ + UvMapVert *iterv1, *iterv2; + EditFace *efa; + int tot = 0; + + /* count number of faces this edge has */ + for(iterv1=first1; iterv1; iterv1=iterv1->next) { + if(iterv1->separate && iterv1 != first1) + break; + + for(iterv2=first2; iterv2; iterv2=iterv2->next) { + if(iterv2->separate && iterv2 != first2) + break; + + if(iterv1->f == iterv2->f) { + /* if face already tagged, don't do this edge */ + efa= EM_get_face_for_index(iterv1->f); + if(efa->f1) + return 0; + + tot++; + break; + } + } + } + + if(*totface == 0) /* start edge */ + *totface= tot; + else if(tot != *totface) /* check for same number of faces as start edge */ + return 0; + + /* tag the faces */ + for(iterv1=first1; iterv1; iterv1=iterv1->next) { + if(iterv1->separate && iterv1 != first1) + break; + + for(iterv2=first2; iterv2; iterv2=iterv2->next) { + if(iterv2->separate && iterv2 != first2) + break; + + if(iterv1->f == iterv2->f) { + efa= EM_get_face_for_index(iterv1->f); + efa->f1= 1; + break; + } + } + } + + return 1; +} + +void select_edgeloop_tface_uv(bContext *C, Scene *scene, Image *ima, Object *obedit, EditFace *startefa, int starta, int shift, int *flush) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + EditVert *eve; + EditFace *efa; + MTFace *tface; + UvVertMap *vmap; + UvMapVert *iterv1, *iterv2; + float limit[2]; + int a, count, looking, nverts, starttotface, select; + + if(!uvedit_test(obedit)) return; + + /* setup */ + EM_init_index_arrays(em, 0, 0, 1); + + uvedit_connection_limit(C, limit, 0.05); + vmap= EM_make_uv_vert_map(em, 0, 0, limit); + + for(count=0, eve=em->verts.first; eve; count++, eve= eve->next) + eve->tmp.l = count; + + for(count=0, efa= em->faces.first; efa; count++, efa= efa->next) { + if(!shift) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + uvedit_face_deselect(scene, efa, tface); + } + + efa->tmp.l= count; + efa->f1= 0; + } + + /* set flags for first face and verts */ + nverts= (startefa->v4)? 4: 3; + iterv1= uv_vertex_map_get(vmap, startefa, starta); + iterv2= uv_vertex_map_get(vmap, startefa, (starta+1)%nverts); + uv_vertex_loop_flag(iterv1); + uv_vertex_loop_flag(iterv2); + + starttotface= 0; + uv_edge_tag_faces(iterv1, iterv2, &starttotface); + + /* sorry, first edge isnt even ok */ + if(iterv1->flag==0 && iterv2->flag==0) looking= 0; + else looking= 1; + + /* iterate */ + while(looking) { + looking= 0; + + /* find correct valence edges which are not tagged yet, but connect to tagged one */ + for(efa= em->faces.first; efa; efa=efa->next) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if(!efa->f1 && uvedit_face_visible(scene, ima, efa, tface)) { + nverts= (efa->v4)? 4: 3; + for(a=0; a<nverts; a++) { + /* check face not hidden and not tagged */ + iterv1= uv_vertex_map_get(vmap, efa, a); + iterv2= uv_vertex_map_get(vmap, efa, (a+1)%nverts); + + /* check if vertex is tagged and has right valence */ + if(iterv1->flag || iterv2->flag) { + if(uv_edge_tag_faces(iterv1, iterv2, &starttotface)) { + looking= 1; + efa->f1= 1; + + uv_vertex_loop_flag(iterv1); + uv_vertex_loop_flag(iterv2); + break; + } + } + } + } + } + } + + /* do the actual select/deselect */ + nverts= (startefa->v4)? 4: 3; + iterv1= uv_vertex_map_get(vmap, startefa, starta); + iterv2= uv_vertex_map_get(vmap, startefa, (starta+1)%nverts); + iterv1->flag= 1; + iterv2->flag= 1; + + if(shift) { + tface= CustomData_em_get(&em->fdata, startefa->data, CD_MTFACE); + if(uvedit_uv_selected(scene, startefa, tface, starta) && uvedit_uv_selected(scene, startefa, tface, starta)) + select= 0; + else + select= 1; + } + else + select= 1; + + if(select) *flush= 1; + else *flush= -1; + + for(efa= em->faces.first; efa; efa=efa->next) { + tface= CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + nverts= (efa->v4)? 4: 3; + for(a=0; a<nverts; a++) { + iterv1= uv_vertex_map_get(vmap, efa, a); + + if(iterv1->flag) { + if(select) uvedit_uv_select(scene, efa, tface, a); + else uvedit_uv_deselect(scene, efa, tface, a); + } + } + } + + /* cleanup */ + EM_free_uv_vert_map(vmap); + EM_free_index_arrays(); +} + +/* ************************** registration **********************************/ + +void ED_operatortypes_uvedit(void) +{ + WM_operatortype_append(UV_OT_de_select_all); + WM_operatortype_append(UV_OT_select_inverse); + + WM_operatortype_append(UV_OT_align); + WM_operatortype_append(UV_OT_mirror); + WM_operatortype_append(UV_OT_stitch); + WM_operatortype_append(UV_OT_weld); +} + +void ED_keymap_uvedit(wmWindowManager *wm) +{ + ListBase *keymap= WM_keymap_listbase(wm, "UVEdit", 0, 0); + + WM_keymap_add_item(keymap, "UV_OT_de_select_all", AKEY, KM_PRESS, 0, 0); + WM_keymap_add_item(keymap, "UV_OT_select_inverse", IKEY, KM_PRESS, KM_CTRL, 0); + + WM_keymap_add_item(keymap, "UV_OT_stitch", VKEY, KM_PRESS, 0, 0); +} + diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.c b/source/blender/editors/uvedit/uvedit_parametrizer.c new file mode 100644 index 00000000000..635a7903d6f --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_parametrizer.c @@ -0,0 +1,4481 @@ + +#include "MEM_guardedalloc.h" + +#include "BLI_memarena.h" +#include "BLI_arithb.h" +#include "BLI_rand.h" +#include "BLI_heap.h" +#include "BLI_boxpack2d.h" + +#include "BKE_utildefines.h" + +#include "ONL_opennl.h" + +#include "uvedit_intern.h" +#include "uvedit_parametrizer.h" + +#include <math.h> +#include <stdlib.h> +#include <stdio.h> +#include <string.h> + +#include "BLO_sys_types.h" // for intptr_t support + +#if defined(_WIN32) +#define M_PI 3.14159265358979323846 +#endif + +/* Utils */ + +#if 0 + #define param_assert(condition); + #define param_warning(message); + #define param_test_equals_ptr(condition); + #define param_test_equals_int(condition); +#else + #define param_assert(condition) \ + if (!(condition)) \ + { /*printf("Assertion %s:%d\n", __FILE__, __LINE__); abort();*/ } + #define param_warning(message) \ + { /*printf("Warning %s:%d: %s\n", __FILE__, __LINE__, message);*/ } + #define param_test_equals_ptr(str, a, b) \ + if (a != b) \ + { /*printf("Equals %s => %p != %p\n", str, a, b);*/ }; + #define param_test_equals_int(str, a, b) \ + if (a != b) \ + { /*printf("Equals %s => %d != %d\n", str, a, b);*/ }; +#endif + +typedef enum PBool { + P_TRUE = 1, + P_FALSE = 0 +} PBool; + +/* Special Purpose Hash */ + +typedef intptr_t PHashKey; + +typedef struct PHashLink { + struct PHashLink *next; + PHashKey key; +} PHashLink; + +typedef struct PHash { + PHashLink **list; + PHashLink **buckets; + int size, cursize, cursize_id; +} PHash; + + + +struct PVert; +struct PEdge; +struct PFace; +struct PChart; +struct PHandle; + +/* Simplices */ + +typedef struct PVert { + struct PVert *nextlink; + + union PVertUnion { + PHashKey key; /* construct */ + int id; /* abf/lscm matrix index */ + float distortion; /* area smoothing */ + HeapNode *heaplink; /* edge collapsing */ + } u; + + struct PEdge *edge; + float *co; + float uv[2]; + unsigned char flag; + +} PVert; + +typedef struct PEdge { + struct PEdge *nextlink; + + union PEdgeUnion { + PHashKey key; /* construct */ + int id; /* abf matrix index */ + HeapNode *heaplink; /* fill holes */ + struct PEdge *nextcollapse; /* simplification */ + } u; + + struct PVert *vert; + struct PEdge *pair; + struct PEdge *next; + struct PFace *face; + float *orig_uv, old_uv[2]; + unsigned short flag; + +} PEdge; + +typedef struct PFace { + struct PFace *nextlink; + + union PFaceUnion { + PHashKey key; /* construct */ + int chart; /* construct splitting*/ + float area3d; /* stretch */ + int id; /* abf matrix index */ + } u; + + struct PEdge *edge; + unsigned char flag; + +} PFace; + +enum PVertFlag { + PVERT_PIN = 1, + PVERT_SELECT = 2, + PVERT_INTERIOR = 4, + PVERT_COLLAPSE = 8, + PVERT_SPLIT = 16 +}; + +enum PEdgeFlag { + PEDGE_SEAM = 1, + PEDGE_VERTEX_SPLIT = 2, + PEDGE_PIN = 4, + PEDGE_SELECT = 8, + PEDGE_DONE = 16, + PEDGE_FILLED = 32, + PEDGE_COLLAPSE = 64, + PEDGE_COLLAPSE_EDGE = 128, + PEDGE_COLLAPSE_PAIR = 256 +}; + +/* for flipping faces */ +#define PEDGE_VERTEX_FLAGS (PEDGE_PIN) + +enum PFaceFlag { + PFACE_CONNECTED = 1, + PFACE_FILLED = 2, + PFACE_COLLAPSE = 4 +}; + +/* Chart */ + +typedef struct PChart { + PVert *verts; + PEdge *edges; + PFace *faces; + int nverts, nedges, nfaces; + + PVert *collapsed_verts; + PEdge *collapsed_edges; + PFace *collapsed_faces; + + union PChartUnion { + struct PChartLscm { + NLContext context; + float *abf_alpha; + PVert *pin1, *pin2; + } lscm; + struct PChartPack { + float rescale, area; + float size[2], trans[2]; + } pack; + } u; + + unsigned char flag; + struct PHandle *handle; +} PChart; + +enum PChartFlag { + PCHART_NOPACK = 1 +}; + +enum PHandleState { + PHANDLE_STATE_ALLOCATED, + PHANDLE_STATE_CONSTRUCTED, + PHANDLE_STATE_LSCM, + PHANDLE_STATE_STRETCH +}; + +typedef struct PHandle { + enum PHandleState state; + MemArena *arena; + + PChart *construction_chart; + PHash *hash_verts; + PHash *hash_edges; + PHash *hash_faces; + + PChart **charts; + int ncharts; + + float aspx, aspy; + + RNG *rng; + float blend; +} PHandle; + + +/* PHash + - special purpose hash that keeps all its elements in a single linked list. + - after construction, this hash is thrown away, and the list remains. + - removing elements is not possible efficiently. +*/ + +static int PHashSizes[] = { + 1, 3, 5, 11, 17, 37, 67, 131, 257, 521, 1031, 2053, 4099, 8209, + 16411, 32771, 65537, 131101, 262147, 524309, 1048583, 2097169, + 4194319, 8388617, 16777259, 33554467, 67108879, 134217757, 268435459 +}; + +#define PHASH_hash(ph, item) (((uintptr_t) (item))%((unsigned int) (ph)->cursize)) +#define PHASH_edge(v1, v2) ((v1)^(v2)) + +static PHash *phash_new(PHashLink **list, int sizehint) +{ + PHash *ph = (PHash*)MEM_callocN(sizeof(PHash), "PHash"); + ph->size = 0; + ph->cursize_id = 0; + ph->list = list; + + while (PHashSizes[ph->cursize_id] < sizehint) + ph->cursize_id++; + + ph->cursize = PHashSizes[ph->cursize_id]; + ph->buckets = (PHashLink**)MEM_callocN(ph->cursize*sizeof(*ph->buckets), "PHashBuckets"); + + return ph; +} + +static void phash_delete(PHash *ph) +{ + MEM_freeN(ph->buckets); + MEM_freeN(ph); +} + +static int phash_size(PHash *ph) +{ + return ph->size; +} + +static void phash_insert(PHash *ph, PHashLink *link) +{ + int size = ph->cursize; + int hash = PHASH_hash(ph, link->key); + PHashLink *lookup = ph->buckets[hash]; + + if (lookup == NULL) { + /* insert in front of the list */ + ph->buckets[hash] = link; + link->next = *(ph->list); + *(ph->list) = link; + } + else { + /* insert after existing element */ + link->next = lookup->next; + lookup->next = link; + } + + ph->size++; + + if (ph->size > (size*3)) { + PHashLink *next = NULL, *first = *(ph->list); + + ph->cursize = PHashSizes[++ph->cursize_id]; + MEM_freeN(ph->buckets); + ph->buckets = (PHashLink**)MEM_callocN(ph->cursize*sizeof(*ph->buckets), "PHashBuckets"); + ph->size = 0; + *(ph->list) = NULL; + + for (link = first; link; link = next) { + next = link->next; + phash_insert(ph, link); + } + } +} + +static PHashLink *phash_lookup(PHash *ph, PHashKey key) +{ + PHashLink *link; + int hash = PHASH_hash(ph, key); + + for (link = ph->buckets[hash]; link; link = link->next) + if (link->key == key) + return link; + else if (PHASH_hash(ph, link->key) != hash) + return NULL; + + return link; +} + +static PHashLink *phash_next(PHash *ph, PHashKey key, PHashLink *link) +{ + int hash = PHASH_hash(ph, key); + + for (link = link->next; link; link = link->next) + if (link->key == key) + return link; + else if (PHASH_hash(ph, link->key) != hash) + return NULL; + + return link; +} + +/* Geometry */ + +static float p_vec_angle_cos(float *v1, float *v2, float *v3) +{ + float d1[3], d2[3]; + + d1[0] = v1[0] - v2[0]; + d1[1] = v1[1] - v2[1]; + d1[2] = v1[2] - v2[2]; + + d2[0] = v3[0] - v2[0]; + d2[1] = v3[1] - v2[1]; + d2[2] = v3[2] - v2[2]; + + Normalize(d1); + Normalize(d2); + + return d1[0]*d2[0] + d1[1]*d2[1] + d1[2]*d2[2]; +} + +static float p_vec_angle(float *v1, float *v2, float *v3) +{ + float dot = p_vec_angle_cos(v1, v2, v3); + + if (dot <= -1.0f) + return (float)M_PI; + else if (dot >= 1.0f) + return 0.0f; + else + return (float)acos(dot); +} + +static float p_vec2_angle(float *v1, float *v2, float *v3) +{ + float u1[3], u2[3], u3[3]; + + u1[0] = v1[0]; u1[1] = v1[1]; u1[2] = 0.0f; + u2[0] = v2[0]; u2[1] = v2[1]; u2[2] = 0.0f; + u3[0] = v3[0]; u3[1] = v3[1]; u3[2] = 0.0f; + + return p_vec_angle(u1, u2, u3); +} + +static void p_triangle_angles(float *v1, float *v2, float *v3, float *a1, float *a2, float *a3) +{ + *a1 = p_vec_angle(v3, v1, v2); + *a2 = p_vec_angle(v1, v2, v3); + *a3 = M_PI - *a2 - *a1; +} + +static void p_face_angles(PFace *f, float *a1, float *a2, float *a3) +{ + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert; + + p_triangle_angles(v1->co, v2->co, v3->co, a1, a2, a3); +} + +static float p_face_area(PFace *f) +{ + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert; + + return AreaT3Dfl(v1->co, v2->co, v3->co); +} + +static float p_area_signed(float *v1, float *v2, float *v3) +{ + return 0.5f*(((v2[0] - v1[0])*(v3[1] - v1[1])) - + ((v3[0] - v1[0])*(v2[1] - v1[1]))); +} + +static float p_face_uv_area_signed(PFace *f) +{ + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert; + + return 0.5f*(((v2->uv[0] - v1->uv[0])*(v3->uv[1] - v1->uv[1])) - + ((v3->uv[0] - v1->uv[0])*(v2->uv[1] - v1->uv[1]))); +} + +static float p_edge_length(PEdge *e) +{ + PVert *v1 = e->vert, *v2 = e->next->vert; + float d[3]; + + d[0] = v2->co[0] - v1->co[0]; + d[1] = v2->co[1] - v1->co[1]; + d[2] = v2->co[2] - v1->co[2]; + + return sqrt(d[0]*d[0] + d[1]*d[1] + d[2]*d[2]); +} + +static float p_edge_uv_length(PEdge *e) +{ + PVert *v1 = e->vert, *v2 = e->next->vert; + float d[3]; + + d[0] = v2->uv[0] - v1->uv[0]; + d[1] = v2->uv[1] - v1->uv[1]; + + return sqrt(d[0]*d[0] + d[1]*d[1]); +} + +static void p_chart_uv_bbox(PChart *chart, float *minv, float *maxv) +{ + PVert *v; + + INIT_MINMAX2(minv, maxv); + + for (v=chart->verts; v; v=v->nextlink) { + DO_MINMAX2(v->uv, minv, maxv); + } +} + +static void p_chart_uv_scale(PChart *chart, float scale) +{ + PVert *v; + + for (v=chart->verts; v; v=v->nextlink) { + v->uv[0] *= scale; + v->uv[1] *= scale; + } +} + +static void p_chart_uv_scale_xy(PChart *chart, float x, float y) +{ + PVert *v; + + for (v=chart->verts; v; v=v->nextlink) { + v->uv[0] *= x; + v->uv[1] *= y; + } +} + +static void p_chart_uv_translate(PChart *chart, float trans[2]) +{ + PVert *v; + + for (v=chart->verts; v; v=v->nextlink) { + v->uv[0] += trans[0]; + v->uv[1] += trans[1]; + } +} + +static PBool p_intersect_line_2d_dir(float *v1, float *dir1, float *v2, float *dir2, float *isect) +{ + float lmbda, div; + + div= dir2[0]*dir1[1] - dir2[1]*dir1[0]; + + if (div == 0.0f) + return P_FALSE; + + lmbda= ((v1[1]-v2[1])*dir1[0]-(v1[0]-v2[0])*dir1[1])/div; + isect[0] = v1[0] + lmbda*dir2[0]; + isect[1] = v1[1] + lmbda*dir2[1]; + + return P_TRUE; +} + +#if 0 +static PBool p_intersect_line_2d(float *v1, float *v2, float *v3, float *v4, float *isect) +{ + float dir1[2], dir2[2]; + + dir1[0] = v4[0] - v3[0]; + dir1[1] = v4[1] - v3[1]; + + dir2[0] = v2[0] - v1[0]; + dir2[1] = v2[1] - v1[1]; + + if (!p_intersect_line_2d_dir(v1, dir1, v2, dir2, isect)) { + /* parallel - should never happen in theory for polygon kernel, but + let's give a point nearby in case things go wrong */ + isect[0] = (v1[0] + v2[0])*0.5f; + isect[1] = (v1[1] + v2[1])*0.5f; + return P_FALSE; + } + + return P_TRUE; +} +#endif + +/* Topological Utilities */ + +static PEdge *p_wheel_edge_next(PEdge *e) +{ + return e->next->next->pair; +} + +static PEdge *p_wheel_edge_prev(PEdge *e) +{ + return (e->pair)? e->pair->next: NULL; +} + +static PEdge *p_boundary_edge_next(PEdge *e) +{ + return e->next->vert->edge; +} + +static PEdge *p_boundary_edge_prev(PEdge *e) +{ + PEdge *we = e, *last; + + do { + last = we; + we = p_wheel_edge_next(we); + } while (we && (we != e)); + + return last->next->next; +} + +static PBool p_vert_interior(PVert *v) +{ + return (v->edge->pair != NULL); +} + +static void p_face_flip(PFace *f) +{ + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert; + int f1 = e1->flag, f2 = e2->flag, f3 = e3->flag; + + e1->vert = v2; + e1->next = e3; + e1->flag = (f1 & ~PEDGE_VERTEX_FLAGS) | (f2 & PEDGE_VERTEX_FLAGS); + + e2->vert = v3; + e2->next = e1; + e2->flag = (f2 & ~PEDGE_VERTEX_FLAGS) | (f3 & PEDGE_VERTEX_FLAGS); + + e3->vert = v1; + e3->next = e2; + e3->flag = (f3 & ~PEDGE_VERTEX_FLAGS) | (f1 & PEDGE_VERTEX_FLAGS); +} + +#if 0 +static void p_chart_topological_sanity_check(PChart *chart) +{ + PVert *v; + PEdge *e; + + for (v=chart->verts; v; v=v->nextlink) + param_test_equals_ptr("v->edge->vert", v, v->edge->vert); + + for (e=chart->edges; e; e=e->nextlink) { + if (e->pair) { + param_test_equals_ptr("e->pair->pair", e, e->pair->pair); + param_test_equals_ptr("pair->vert", e->vert, e->pair->next->vert); + param_test_equals_ptr("pair->next->vert", e->next->vert, e->pair->vert); + } + } +} +#endif + +/* Loading / Flushing */ + +static void p_vert_load_pin_select_uvs(PHandle *handle, PVert *v) +{ + PEdge *e; + int nedges = 0, npins = 0; + float pinuv[2]; + + v->uv[0] = v->uv[1] = 0.0f; + pinuv[0] = pinuv[1] = 0.0f; + e = v->edge; + do { + if (e->orig_uv) { + if (e->flag & PEDGE_SELECT) + v->flag |= PVERT_SELECT; + + if (e->flag & PEDGE_PIN) { + pinuv[0] += e->orig_uv[0]*handle->aspx; + pinuv[1] += e->orig_uv[1]*handle->aspy; + npins++; + } + else { + v->uv[0] += e->orig_uv[0]*handle->aspx; + v->uv[1] += e->orig_uv[1]*handle->aspy; + } + + nedges++; + } + + e = p_wheel_edge_next(e); + } while (e && e != (v->edge)); + + if (npins > 0) { + v->uv[0] = pinuv[0]/npins; + v->uv[1] = pinuv[1]/npins; + v->flag |= PVERT_PIN; + } + else if (nedges > 0) { + v->uv[0] /= nedges; + v->uv[1] /= nedges; + } +} + +static void p_flush_uvs(PHandle *handle, PChart *chart) +{ + PEdge *e; + + 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; + } + } +} + +static void p_flush_uvs_blend(PHandle *handle, PChart *chart, float blend) +{ + PEdge *e; + float invblend = 1.0f - blend; + + for (e=chart->edges; e; e=e->nextlink) { + if (e->orig_uv) { + e->orig_uv[0] = blend*e->old_uv[0] + invblend*e->vert->uv[0]/handle->aspx; + e->orig_uv[1] = blend*e->old_uv[1] + invblend*e->vert->uv[1]/handle->aspy; + } + } +} + +static void p_face_backup_uvs(PFace *f) +{ + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + + if (e1->orig_uv && e2->orig_uv && e3->orig_uv) { + e1->old_uv[0] = e1->orig_uv[0]; + e1->old_uv[1] = e1->orig_uv[1]; + e2->old_uv[0] = e2->orig_uv[0]; + e2->old_uv[1] = e2->orig_uv[1]; + e3->old_uv[0] = e3->orig_uv[0]; + e3->old_uv[1] = e3->orig_uv[1]; + } +} + +static void p_face_restore_uvs(PFace *f) +{ + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + + if (e1->orig_uv && e2->orig_uv && e3->orig_uv) { + e1->orig_uv[0] = e1->old_uv[0]; + e1->orig_uv[1] = e1->old_uv[1]; + e2->orig_uv[0] = e2->old_uv[0]; + e2->orig_uv[1] = e2->old_uv[1]; + e3->orig_uv[0] = e3->old_uv[0]; + e3->orig_uv[1] = e3->old_uv[1]; + } +} + +/* Construction (use only during construction, relies on u.key being set */ + +static PVert *p_vert_add(PHandle *handle, PHashKey key, float *co, PEdge *e) +{ + PVert *v = (PVert*)BLI_memarena_alloc(handle->arena, sizeof *v); + v->co = co; + v->u.key = key; + v->edge = e; + v->flag = 0; + + phash_insert(handle->hash_verts, (PHashLink*)v); + + return v; +} + +static PVert *p_vert_lookup(PHandle *handle, PHashKey key, float *co, PEdge *e) +{ + PVert *v = (PVert*)phash_lookup(handle->hash_verts, key); + + if (v) + return v; + else + return p_vert_add(handle, key, co, e); +} + +static PVert *p_vert_copy(PChart *chart, PVert *v) +{ + PVert *nv = (PVert*)BLI_memarena_alloc(chart->handle->arena, sizeof *nv); + + nv->co = v->co; + nv->uv[0] = v->uv[0]; + nv->uv[1] = v->uv[1]; + nv->u.key = v->u.key; + nv->edge = v->edge; + nv->flag = v->flag; + + return nv; +} + +static PEdge *p_edge_lookup(PHandle *handle, PHashKey *vkeys) +{ + PHashKey key = PHASH_edge(vkeys[0], vkeys[1]); + PEdge *e = (PEdge*)phash_lookup(handle->hash_edges, key); + + while (e) { + if ((e->vert->u.key == vkeys[0]) && (e->next->vert->u.key == vkeys[1])) + return e; + else if ((e->vert->u.key == vkeys[1]) && (e->next->vert->u.key == vkeys[0])) + return e; + + e = (PEdge*)phash_next(handle->hash_edges, key, (PHashLink*)e); + } + + return NULL; +} + +static PBool p_face_exists(PHandle *handle, PHashKey *vkeys, int i1, int i2, int i3) +{ + PHashKey key = PHASH_edge(vkeys[i1], vkeys[i2]); + PEdge *e = (PEdge*)phash_lookup(handle->hash_edges, key); + + while (e) { + if ((e->vert->u.key == vkeys[i1]) && (e->next->vert->u.key == vkeys[i2])) { + if (e->next->next->vert->u.key == vkeys[i3]) + return P_TRUE; + } + else if ((e->vert->u.key == vkeys[i2]) && (e->next->vert->u.key == vkeys[i1])) { + if (e->next->next->vert->u.key == vkeys[i3]) + return P_TRUE; + } + + e = (PEdge*)phash_next(handle->hash_edges, key, (PHashLink*)e); + } + + return P_FALSE; +} + +static PChart *p_chart_new(PHandle *handle) +{ + PChart *chart = (PChart*)MEM_callocN(sizeof*chart, "PChart"); + chart->handle = handle; + + return chart; +} + +static void p_chart_delete(PChart *chart) +{ + /* the actual links are free by memarena */ + MEM_freeN(chart); +} + +static PBool p_edge_implicit_seam(PEdge *e, PEdge *ep) +{ + float *uv1, *uv2, *uvp1, *uvp2; + float limit[2]; + + limit[0] = 0.00001; + limit[1] = 0.00001; + + uv1 = e->orig_uv; + uv2 = e->next->orig_uv; + + if (e->vert->u.key == ep->vert->u.key) { + uvp1 = ep->orig_uv; + uvp2 = ep->next->orig_uv; + } + else { + uvp1 = ep->next->orig_uv; + uvp2 = ep->orig_uv; + } + + if((fabs(uv1[0]-uvp1[0]) > limit[0]) || (fabs(uv1[1]-uvp1[1]) > limit[1])) { + e->flag |= PEDGE_SEAM; + ep->flag |= PEDGE_SEAM; + return P_TRUE; + } + if((fabs(uv2[0]-uvp2[0]) > limit[0]) || (fabs(uv2[1]-uvp2[1]) > limit[1])) { + e->flag |= PEDGE_SEAM; + ep->flag |= PEDGE_SEAM; + return P_TRUE; + } + + return P_FALSE; +} + +static PBool p_edge_has_pair(PHandle *handle, PEdge *e, PEdge **pair, PBool impl) +{ + PHashKey key; + PEdge *pe; + PVert *v1, *v2; + PHashKey key1 = e->vert->u.key; + PHashKey key2 = e->next->vert->u.key; + + if (e->flag & PEDGE_SEAM) + return P_FALSE; + + key = PHASH_edge(key1, key2); + pe = (PEdge*)phash_lookup(handle->hash_edges, key); + *pair = NULL; + + while (pe) { + if (pe != e) { + v1 = pe->vert; + v2 = pe->next->vert; + + if (((v1->u.key == key1) && (v2->u.key == key2)) || + ((v1->u.key == key2) && (v2->u.key == key1))) { + + /* don't connect seams and t-junctions */ + if ((pe->flag & PEDGE_SEAM) || *pair || + (impl && p_edge_implicit_seam(e, pe))) { + *pair = NULL; + return P_FALSE; + } + + *pair = pe; + } + } + + pe = (PEdge*)phash_next(handle->hash_edges, key, (PHashLink*)pe); + } + + if (*pair && (e->vert == (*pair)->vert)) { + if ((*pair)->next->pair || (*pair)->next->next->pair) { + /* non unfoldable, maybe mobius ring or klein bottle */ + *pair = NULL; + return P_FALSE; + } + } + + return (*pair != NULL); +} + +static PBool p_edge_connect_pair(PHandle *handle, PEdge *e, PEdge ***stack, PBool impl) +{ + PEdge *pair = NULL; + + if(!e->pair && p_edge_has_pair(handle, e, &pair, impl)) { + if (e->vert == pair->vert) + p_face_flip(pair->face); + + e->pair = pair; + pair->pair = e; + + if (!(pair->face->flag & PFACE_CONNECTED)) { + **stack = pair; + (*stack)++; + } + } + + return (e->pair != NULL); +} + +static int p_connect_pairs(PHandle *handle, PBool impl) +{ + PEdge **stackbase = MEM_mallocN(sizeof*stackbase*phash_size(handle->hash_faces), "Pstackbase"); + PEdge **stack = stackbase; + PFace *f, *first; + PEdge *e, *e1, *e2; + PChart *chart = handle->construction_chart; + int ncharts = 0; + + /* connect pairs, count edges, set vertex-edge pointer to a pairless edge */ + for (first=chart->faces; first; first=first->nextlink) { + if (first->flag & PFACE_CONNECTED) + continue; + + *stack = first->edge; + stack++; + + while (stack != stackbase) { + stack--; + e = *stack; + e1 = e->next; + e2 = e1->next; + + f = e->face; + f->flag |= PFACE_CONNECTED; + + /* assign verts to charts so we can sort them later */ + f->u.chart = ncharts; + + if (!p_edge_connect_pair(handle, e, &stack, impl)) + e->vert->edge = e; + if (!p_edge_connect_pair(handle, e1, &stack, impl)) + e1->vert->edge = e1; + if (!p_edge_connect_pair(handle, e2, &stack, impl)) + e2->vert->edge = e2; + } + + ncharts++; + } + + MEM_freeN(stackbase); + + return ncharts; +} + +static void p_split_vert(PChart *chart, PEdge *e) +{ + PEdge *we, *lastwe = NULL; + PVert *v = e->vert; + PBool copy = P_TRUE; + + if (e->flag & PEDGE_VERTEX_SPLIT) + return; + + /* rewind to start */ + lastwe = e; + for (we = p_wheel_edge_prev(e); we && (we != e); we = p_wheel_edge_prev(we)) + lastwe = we; + + /* go over all edges in wheel */ + for (we = lastwe; we; we = p_wheel_edge_next(we)) { + if (we->flag & PEDGE_VERTEX_SPLIT) + break; + + we->flag |= PEDGE_VERTEX_SPLIT; + + if (we == v->edge) { + /* found it, no need to copy */ + copy = P_FALSE; + v->nextlink = chart->verts; + chart->verts = v; + chart->nverts++; + } + } + + if (copy) { + /* not found, copying */ + v->flag |= PVERT_SPLIT; + v = p_vert_copy(chart, v); + v->flag |= PVERT_SPLIT; + + v->nextlink = chart->verts; + chart->verts = v; + chart->nverts++; + + v->edge = lastwe; + + we = lastwe; + do { + we->vert = v; + we = p_wheel_edge_next(we); + } while (we && (we != lastwe)); + } +} + +static PChart **p_split_charts(PHandle *handle, PChart *chart, int ncharts) +{ + PChart **charts = MEM_mallocN(sizeof*charts * ncharts, "PCharts"), *nchart; + PFace *f, *nextf; + int i; + + for (i = 0; i < ncharts; i++) + charts[i] = p_chart_new(handle); + + f = chart->faces; + while (f) { + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + nextf = f->nextlink; + + nchart = charts[f->u.chart]; + + f->nextlink = nchart->faces; + nchart->faces = f; + e1->nextlink = nchart->edges; + nchart->edges = e1; + e2->nextlink = nchart->edges; + nchart->edges = e2; + e3->nextlink = nchart->edges; + nchart->edges = e3; + + nchart->nfaces++; + nchart->nedges += 3; + + p_split_vert(nchart, e1); + p_split_vert(nchart, e2); + p_split_vert(nchart, e3); + + f = nextf; + } + + return charts; +} + +static PFace *p_face_add(PHandle *handle) +{ + PFace *f; + PEdge *e1, *e2, *e3; + + /* allocate */ + f = (PFace*)BLI_memarena_alloc(handle->arena, sizeof *f); + f->flag=0; // init ! + + e1 = (PEdge*)BLI_memarena_alloc(handle->arena, sizeof *e1); + e2 = (PEdge*)BLI_memarena_alloc(handle->arena, sizeof *e2); + e3 = (PEdge*)BLI_memarena_alloc(handle->arena, sizeof *e3); + + /* set up edges */ + f->edge = e1; + e1->face = e2->face = e3->face = f; + + e1->next = e2; + e2->next = e3; + e3->next = e1; + + e1->pair = NULL; + e2->pair = NULL; + e3->pair = NULL; + + e1->flag =0; + e2->flag =0; + e3->flag =0; + + return f; +} + +static PFace *p_face_add_construct(PHandle *handle, ParamKey key, ParamKey *vkeys, + float *co[3], float *uv[3], int i1, int i2, int i3, + ParamBool *pin, ParamBool *select) +{ + PFace *f = p_face_add(handle); + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + + e1->vert = p_vert_lookup(handle, vkeys[i1], co[i1], e1); + e2->vert = p_vert_lookup(handle, vkeys[i2], co[i2], e2); + e3->vert = p_vert_lookup(handle, vkeys[i3], co[i3], e3); + + e1->orig_uv = uv[i1]; + e2->orig_uv = uv[i2]; + e3->orig_uv = uv[i3]; + + if (pin) { + if (pin[i1]) e1->flag |= PEDGE_PIN; + if (pin[i2]) e2->flag |= PEDGE_PIN; + if (pin[i3]) e3->flag |= PEDGE_PIN; + } + + if (select) { + if (select[i1]) e1->flag |= PEDGE_SELECT; + if (select[i2]) e2->flag |= PEDGE_SELECT; + if (select[i3]) e3->flag |= PEDGE_SELECT; + } + + /* insert into hash */ + f->u.key = key; + phash_insert(handle->hash_faces, (PHashLink*)f); + + e1->u.key = PHASH_edge(vkeys[i1], vkeys[i2]); + e2->u.key = PHASH_edge(vkeys[i2], vkeys[i3]); + e3->u.key = PHASH_edge(vkeys[i3], vkeys[i1]); + + phash_insert(handle->hash_edges, (PHashLink*)e1); + phash_insert(handle->hash_edges, (PHashLink*)e2); + phash_insert(handle->hash_edges, (PHashLink*)e3); + + return f; +} + +static PFace *p_face_add_fill(PChart *chart, PVert *v1, PVert *v2, PVert *v3) +{ + PFace *f = p_face_add(chart->handle); + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + + e1->vert = v1; + e2->vert = v2; + e3->vert = v3; + + e1->orig_uv = e2->orig_uv = e3->orig_uv = NULL; + + f->nextlink = chart->faces; + chart->faces = f; + e1->nextlink = chart->edges; + chart->edges = e1; + e2->nextlink = chart->edges; + chart->edges = e2; + e3->nextlink = chart->edges; + chart->edges = e3; + + chart->nfaces++; + chart->nedges += 3; + + return f; +} + +static PBool p_quad_split_direction(PHandle *handle, float **co, PHashKey *vkeys) +{ + float fac= VecLenf(co[0], co[2]) - VecLenf(co[1], co[3]); + PBool dir = (fac <= 0.0f); + + /* the face exists check is there because of a special case: when + two quads share three vertices, they can each be split into two + triangles, resulting in two identical triangles. for example in + suzanne's nose. */ + if (dir) { + if (p_face_exists(handle,vkeys,0,1,2) || p_face_exists(handle,vkeys,0,2,3)) + return !dir; + } + else { + if (p_face_exists(handle,vkeys,0,1,3) || p_face_exists(handle,vkeys,1,2,3)) + return !dir; + } + + return dir; +} + +/* Construction: boundary filling */ + +static void p_chart_boundaries(PChart *chart, int *nboundaries, PEdge **outer) +{ + PEdge *e, *be; + float len, maxlen = -1.0; + + if (nboundaries) + *nboundaries = 0; + if (outer) + *outer = NULL; + + for (e=chart->edges; e; e=e->nextlink) { + if (e->pair || (e->flag & PEDGE_DONE)) + continue; + + if (nboundaries) + (*nboundaries)++; + + len = 0.0f; + + be = e; + do { + be->flag |= PEDGE_DONE; + len += p_edge_length(be); + be = be->next->vert->edge; + } while(be != e); + + if (outer && (len > maxlen)) { + *outer = e; + maxlen = len; + } + } + + for (e=chart->edges; e; e=e->nextlink) + e->flag &= ~PEDGE_DONE; +} + +static float p_edge_boundary_angle(PEdge *e) +{ + PEdge *we; + PVert *v, *v1, *v2; + float angle; + int n = 0; + + v = e->vert; + + /* concave angle check -- could be better */ + angle = M_PI; + + we = v->edge; + do { + v1 = we->next->vert; + v2 = we->next->next->vert; + angle -= p_vec_angle(v1->co, v->co, v2->co); + + we = we->next->next->pair; + n++; + } while (we && (we != v->edge)); + + return angle; +} + +static void p_chart_fill_boundary(PChart *chart, PEdge *be, int nedges) +{ + PEdge *e, *e1, *e2; + + PFace *f; + struct Heap *heap = BLI_heap_new(); + float angle; + + e = be; + do { + angle = p_edge_boundary_angle(e); + e->u.heaplink = BLI_heap_insert(heap, angle, e); + + e = p_boundary_edge_next(e); + } while(e != be); + + if (nedges == 2) { + /* no real boundary, but an isolated seam */ + e = be->next->vert->edge; + e->pair = be; + be->pair = e; + + BLI_heap_remove(heap, e->u.heaplink); + BLI_heap_remove(heap, be->u.heaplink); + } + else { + while (nedges > 2) { + PEdge *ne, *ne1, *ne2; + + e = (PEdge*)BLI_heap_popmin(heap); + + e1 = p_boundary_edge_prev(e); + e2 = p_boundary_edge_next(e); + + BLI_heap_remove(heap, e1->u.heaplink); + BLI_heap_remove(heap, e2->u.heaplink); + e->u.heaplink = e1->u.heaplink = e2->u.heaplink = NULL; + + e->flag |= PEDGE_FILLED; + e1->flag |= PEDGE_FILLED; + + + + + + f = p_face_add_fill(chart, e->vert, e1->vert, e2->vert); + f->flag |= PFACE_FILLED; + + ne = f->edge->next->next; + ne1 = f->edge; + ne2 = f->edge->next; + + ne->flag = ne1->flag = ne2->flag = PEDGE_FILLED; + + e->pair = ne; + ne->pair = e; + e1->pair = ne1; + ne1->pair = e1; + + ne->vert = e2->vert; + ne1->vert = e->vert; + ne2->vert = e1->vert; + + if (nedges == 3) { + e2->pair = ne2; + ne2->pair = e2; + } + else { + ne2->vert->edge = ne2; + + ne2->u.heaplink = BLI_heap_insert(heap, p_edge_boundary_angle(ne2), ne2); + e2->u.heaplink = BLI_heap_insert(heap, p_edge_boundary_angle(e2), e2); + } + + nedges--; + } + } + + BLI_heap_free(heap, NULL); +} + +static void p_chart_fill_boundaries(PChart *chart, PEdge *outer) +{ + PEdge *e, *be; /* *enext - as yet unused */ + int nedges; + + for (e=chart->edges; e; e=e->nextlink) { + /* enext = e->nextlink; - as yet unused */ + + if (e->pair || (e->flag & PEDGE_FILLED)) + continue; + + nedges = 0; + be = e; + do { + be->flag |= PEDGE_FILLED; + be = be->next->vert->edge; + nedges++; + } while(be != e); + + if (e != outer) + p_chart_fill_boundary(chart, e, nedges); + } +} + +#if 0 +/* Polygon kernel for inserting uv's non overlapping */ + +static int p_polygon_point_in(float *cp1, float *cp2, float *p) +{ + if ((cp1[0] == p[0]) && (cp1[1] == p[1])) + return 2; + else if ((cp2[0] == p[0]) && (cp2[1] == p[1])) + return 3; + else + return (p_area_signed(cp1, cp2, p) >= 0.0f); +} + +static void p_polygon_kernel_clip(float (*oldpoints)[2], int noldpoints, float (*newpoints)[2], int *nnewpoints, float *cp1, float *cp2) +{ + float *p2, *p1, isect[2]; + int i, p2in, p1in; + + p1 = oldpoints[noldpoints-1]; + p1in = p_polygon_point_in(cp1, cp2, p1); + *nnewpoints = 0; + + for (i = 0; i < noldpoints; i++) { + p2 = oldpoints[i]; + p2in = p_polygon_point_in(cp1, cp2, p2); + + if ((p2in >= 2) || (p1in && p2in)) { + newpoints[*nnewpoints][0] = p2[0]; + newpoints[*nnewpoints][1] = p2[1]; + (*nnewpoints)++; + } + else if (p1in && !p2in) { + if (p1in != 3) { + p_intersect_line_2d(p1, p2, cp1, cp2, isect); + newpoints[*nnewpoints][0] = isect[0]; + newpoints[*nnewpoints][1] = isect[1]; + (*nnewpoints)++; + } + } + else if (!p1in && p2in) { + p_intersect_line_2d(p1, p2, cp1, cp2, isect); + newpoints[*nnewpoints][0] = isect[0]; + newpoints[*nnewpoints][1] = isect[1]; + (*nnewpoints)++; + + newpoints[*nnewpoints][0] = p2[0]; + newpoints[*nnewpoints][1] = p2[1]; + (*nnewpoints)++; + } + + p1in = p2in; + p1 = p2; + } +} + +static void p_polygon_kernel_center(float (*points)[2], int npoints, float *center) +{ + int i, size, nnewpoints = npoints; + float (*oldpoints)[2], (*newpoints)[2], *p1, *p2; + + size = npoints*3; + oldpoints = MEM_mallocN(sizeof(float)*2*size, "PPolygonOldPoints"); + newpoints = MEM_mallocN(sizeof(float)*2*size, "PPolygonNewPoints"); + + memcpy(oldpoints, points, sizeof(float)*2*npoints); + + for (i = 0; i < npoints; i++) { + p1 = points[i]; + p2 = points[(i+1)%npoints]; + p_polygon_kernel_clip(oldpoints, nnewpoints, newpoints, &nnewpoints, p1, p2); + + if (nnewpoints == 0) { + /* degenerate case, use center of original polygon */ + memcpy(oldpoints, points, sizeof(float)*2*npoints); + nnewpoints = npoints; + break; + } + else if (nnewpoints == 1) { + /* degenerate case, use remaining point */ + center[0] = newpoints[0][0]; + center[1] = newpoints[0][1]; + + MEM_freeN(oldpoints); + MEM_freeN(newpoints); + + return; + } + + if (nnewpoints*2 > size) { + size *= 2; + free(oldpoints); + oldpoints = malloc(sizeof(float)*2*size); + memcpy(oldpoints, newpoints, sizeof(float)*2*nnewpoints); + free(newpoints); + newpoints = malloc(sizeof(float)*2*size); + } + else { + float (*sw_points)[2] = oldpoints; + oldpoints = newpoints; + newpoints = sw_points; + } + } + + center[0] = center[1] = 0.0f; + + for (i = 0; i < nnewpoints; i++) { + center[0] += oldpoints[i][0]; + center[1] += oldpoints[i][1]; + } + + center[0] /= nnewpoints; + center[1] /= nnewpoints; + + MEM_freeN(oldpoints); + MEM_freeN(newpoints); +} +#endif + +#if 0 +/* Edge Collapser */ + +int NCOLLAPSE = 1; +int NCOLLAPSEX = 0; + +static float p_vert_cotan(float *v1, float *v2, float *v3) +{ + float a[3], b[3], c[3], clen; + + VecSubf(a, v2, v1); + VecSubf(b, v3, v1); + Crossf(c, a, b); + + clen = VecLength(c); + + if (clen == 0.0f) + return 0.0f; + + return Inpf(a, b)/clen; +} + +static PBool p_vert_flipped_wheel_triangle(PVert *v) +{ + PEdge *e = v->edge; + + do { + if (p_face_uv_area_signed(e->face) < 0.0f) + return P_TRUE; + + e = p_wheel_edge_next(e); + } while (e && (e != v->edge)); + + return P_FALSE; +} + +static PBool p_vert_map_harmonic_weights(PVert *v) +{ + float weightsum, positionsum[2], olduv[2]; + + weightsum = 0.0f; + positionsum[0] = positionsum[1] = 0.0f; + + if (p_vert_interior(v)) { + PEdge *e = v->edge; + + do { + float t1, t2, weight; + PVert *v1, *v2; + + v1 = e->next->vert; + v2 = e->next->next->vert; + t1 = p_vert_cotan(v2->co, e->vert->co, v1->co); + + v1 = e->pair->next->vert; + v2 = e->pair->next->next->vert; + t2 = p_vert_cotan(v2->co, e->pair->vert->co, v1->co); + + weight = 0.5f*(t1 + t2); + weightsum += weight; + positionsum[0] += weight*e->pair->vert->uv[0]; + positionsum[1] += weight*e->pair->vert->uv[1]; + + e = p_wheel_edge_next(e); + } while (e && (e != v->edge)); + } + else { + PEdge *e = v->edge; + + do { + float t1, t2; + PVert *v1, *v2; + + v2 = e->next->vert; + v1 = e->next->next->vert; + + t1 = p_vert_cotan(v1->co, v->co, v2->co); + t2 = p_vert_cotan(v2->co, v->co, v1->co); + + weightsum += t1 + t2; + positionsum[0] += (v2->uv[1] - v1->uv[1]) + (t1*v2->uv[0] + t2*v1->uv[0]); + positionsum[1] += (v1->uv[0] - v2->uv[0]) + (t1*v2->uv[1] + t2*v1->uv[1]); + + e = p_wheel_edge_next(e); + } while (e && (e != v->edge)); + } + + if (weightsum != 0.0f) { + weightsum = 1.0f/weightsum; + positionsum[0] *= weightsum; + positionsum[1] *= weightsum; + } + + olduv[0] = v->uv[0]; + olduv[1] = v->uv[1]; + v->uv[0] = positionsum[0]; + v->uv[1] = positionsum[1]; + + if (p_vert_flipped_wheel_triangle(v)) { + v->uv[0] = olduv[0]; + v->uv[1] = olduv[1]; + + return P_FALSE; + } + + return P_TRUE; +} + +static void p_vert_harmonic_insert(PVert *v) +{ + PEdge *e; + + if (!p_vert_map_harmonic_weights(v)) { + /* do polygon kernel center insertion: this is quite slow, but should + only be needed for 0.01 % of verts or so, when insert with harmonic + weights fails */ + + int npoints = 0, i; + float (*points)[2]; + + e = v->edge; + do { + npoints++; + e = p_wheel_edge_next(e); + } while (e && (e != v->edge)); + + if (e == NULL) + npoints++; + + points = MEM_mallocN(sizeof(float)*2*npoints, "PHarmonicPoints"); + + e = v->edge; + i = 0; + do { + PEdge *nexte = p_wheel_edge_next(e); + + points[i][0] = e->next->vert->uv[0]; + points[i][1] = e->next->vert->uv[1]; + + if (nexte == NULL) { + i++; + points[i][0] = e->next->next->vert->uv[0]; + points[i][1] = e->next->next->vert->uv[1]; + break; + } + + e = nexte; + i++; + } while (e != v->edge); + + p_polygon_kernel_center(points, npoints, v->uv); + + MEM_freeN(points); + } + + e = v->edge; + do { + if (!(e->next->vert->flag & PVERT_PIN)) + p_vert_map_harmonic_weights(e->next->vert); + e = p_wheel_edge_next(e); + } while (e && (e != v->edge)); + + p_vert_map_harmonic_weights(v); +} + +static void p_vert_fix_edge_pointer(PVert *v) +{ + PEdge *start = v->edge; + + /* set v->edge pointer to the edge with no pair, if there is one */ + while (v->edge->pair) { + v->edge = p_wheel_edge_prev(v->edge); + + if (v->edge == start) + break; + } +} + +static void p_collapsing_verts(PEdge *edge, PEdge *pair, PVert **newv, PVert **keepv) +{ + /* the two vertices that are involved in the collapse */ + if (edge) { + *newv = edge->vert; + *keepv = edge->next->vert; + } + else { + *newv = pair->next->vert; + *keepv = pair->vert; + } +} + +static void p_collapse_edge(PEdge *edge, PEdge *pair) +{ + PVert *oldv, *keepv; + PEdge *e; + + p_collapsing_verts(edge, pair, &oldv, &keepv); + + /* change e->vert pointers from old vertex to the target vertex */ + e = oldv->edge; + do { + if ((e != edge) && !(pair && pair->next == e)) + e->vert = keepv; + + e = p_wheel_edge_next(e); + } while (e && (e != oldv->edge)); + + /* set keepv->edge pointer */ + if ((edge && (keepv->edge == edge->next)) || (keepv->edge == pair)) { + if (edge && edge->next->pair) + keepv->edge = edge->next->pair->next; + else if (pair && pair->next->next->pair) + keepv->edge = pair->next->next->pair; + else if (edge && edge->next->next->pair) + keepv->edge = edge->next->next->pair; + else + keepv->edge = pair->next->pair->next; + } + + /* update pairs and v->edge pointers */ + if (edge) { + PEdge *e1 = edge->next, *e2 = e1->next; + + if (e1->pair) + e1->pair->pair = e2->pair; + + if (e2->pair) { + e2->pair->pair = e1->pair; + e2->vert->edge = p_wheel_edge_prev(e2); + } + else + e2->vert->edge = p_wheel_edge_next(e2); + + p_vert_fix_edge_pointer(e2->vert); + } + + if (pair) { + PEdge *e1 = pair->next, *e2 = e1->next; + + if (e1->pair) + e1->pair->pair = e2->pair; + + if (e2->pair) { + e2->pair->pair = e1->pair; + e2->vert->edge = p_wheel_edge_prev(e2); + } + else + e2->vert->edge = p_wheel_edge_next(e2); + + p_vert_fix_edge_pointer(e2->vert); + } + + p_vert_fix_edge_pointer(keepv); + + /* mark for move to collapsed list later */ + oldv->flag |= PVERT_COLLAPSE; + + if (edge) { + PFace *f = edge->face; + PEdge *e1 = edge->next, *e2 = e1->next; + + f->flag |= PFACE_COLLAPSE; + edge->flag |= PEDGE_COLLAPSE; + e1->flag |= PEDGE_COLLAPSE; + e2->flag |= PEDGE_COLLAPSE; + } + + if (pair) { + PFace *f = pair->face; + PEdge *e1 = pair->next, *e2 = e1->next; + + f->flag |= PFACE_COLLAPSE; + pair->flag |= PEDGE_COLLAPSE; + e1->flag |= PEDGE_COLLAPSE; + e2->flag |= PEDGE_COLLAPSE; + } +} + +static void p_split_vertex(PEdge *edge, PEdge *pair) +{ + PVert *newv, *keepv; + PEdge *e; + + p_collapsing_verts(edge, pair, &newv, &keepv); + + /* update edge pairs */ + if (edge) { + PEdge *e1 = edge->next, *e2 = e1->next; + + if (e1->pair) + e1->pair->pair = e1; + if (e2->pair) + e2->pair->pair = e2; + + e2->vert->edge = e2; + p_vert_fix_edge_pointer(e2->vert); + keepv->edge = e1; + } + + if (pair) { + PEdge *e1 = pair->next, *e2 = e1->next; + + if (e1->pair) + e1->pair->pair = e1; + if (e2->pair) + e2->pair->pair = e2; + + e2->vert->edge = e2; + p_vert_fix_edge_pointer(e2->vert); + keepv->edge = pair; + } + + p_vert_fix_edge_pointer(keepv); + + /* set e->vert pointers to restored vertex */ + e = newv->edge; + do { + e->vert = newv; + e = p_wheel_edge_next(e); + } while (e && (e != newv->edge)); +} + +static PBool p_collapse_allowed_topologic(PEdge *edge, PEdge *pair) +{ + PVert *oldv, *keepv; + + p_collapsing_verts(edge, pair, &oldv, &keepv); + + /* boundary edges */ + if (!edge || !pair) { + /* avoid collapsing chart into an edge */ + if (edge && !edge->next->pair && !edge->next->next->pair) + return P_FALSE; + else if (pair && !pair->next->pair && !pair->next->next->pair) + return P_FALSE; + } + /* avoid merging two boundaries (oldv and keepv are on the 'other side' of + the chart) */ + else if (!p_vert_interior(oldv) && !p_vert_interior(keepv)) + return P_FALSE; + + return P_TRUE; +} + +static PBool p_collapse_normal_flipped(float *v1, float *v2, float *vold, float *vnew) +{ + float nold[3], nnew[3], sub1[3], sub2[3]; + + VecSubf(sub1, vold, v1); + VecSubf(sub2, vold, v2); + Crossf(nold, sub1, sub2); + + VecSubf(sub1, vnew, v1); + VecSubf(sub2, vnew, v2); + Crossf(nnew, sub1, sub2); + + return (Inpf(nold, nnew) <= 0.0f); +} + +static PBool p_collapse_allowed_geometric(PEdge *edge, PEdge *pair) +{ + PVert *oldv, *keepv; + PEdge *e; + float angulardefect, angle; + + p_collapsing_verts(edge, pair, &oldv, &keepv); + + angulardefect = 2*M_PI; + + e = oldv->edge; + do { + float a[3], b[3], minangle, maxangle; + PEdge *e1 = e->next, *e2 = e1->next; + PVert *v1 = e1->vert, *v2 = e2->vert; + int i; + + angle = p_vec_angle(v1->co, oldv->co, v2->co); + angulardefect -= angle; + + /* skip collapsing faces */ + if (v1 == keepv || v2 == keepv) { + e = p_wheel_edge_next(e); + continue; + } + + if (p_collapse_normal_flipped(v1->co, v2->co, oldv->co, keepv->co)) + return P_FALSE; + + a[0] = angle; + a[1] = p_vec_angle(v2->co, v1->co, oldv->co); + a[2] = M_PI - a[0] - a[1]; + + b[0] = p_vec_angle(v1->co, keepv->co, v2->co); + b[1] = p_vec_angle(v2->co, v1->co, keepv->co); + b[2] = M_PI - b[0] - b[1]; + + /* abf criterion 1: avoid sharp and obtuse angles */ + minangle = 15.0f*M_PI/180.0f; + maxangle = M_PI - minangle; + + for (i = 0; i < 3; i++) { + if ((b[i] < a[i]) && (b[i] < minangle)) + return P_FALSE; + else if ((b[i] > a[i]) && (b[i] > maxangle)) + return P_FALSE; + } + + e = p_wheel_edge_next(e); + } while (e && (e != oldv->edge)); + + if (p_vert_interior(oldv)) { + /* hlscm criterion: angular defect smaller than threshold */ + if (fabs(angulardefect) > (M_PI*30.0/180.0)) + return P_FALSE; + } + else { + PVert *v1 = p_boundary_edge_next(oldv->edge)->vert; + PVert *v2 = p_boundary_edge_prev(oldv->edge)->vert; + + /* abf++ criterion 2: avoid collapsing verts inwards */ + if (p_vert_interior(keepv)) + return P_FALSE; + + /* don't collapse significant boundary changes */ + angle = p_vec_angle(v1->co, oldv->co, v2->co); + if (angle < (M_PI*160.0/180.0)) + return P_FALSE; + } + + return P_TRUE; +} + +static PBool p_collapse_allowed(PEdge *edge, PEdge *pair) +{ + PVert *oldv, *keepv; + + p_collapsing_verts(edge, pair, &oldv, &keepv); + + if (oldv->flag & PVERT_PIN) + return P_FALSE; + + return (p_collapse_allowed_topologic(edge, pair) && + p_collapse_allowed_geometric(edge, pair)); +} + +static float p_collapse_cost(PEdge *edge, PEdge *pair) +{ + /* based on volume and boundary optimization from: + "Fast and Memory Efficient Polygonal Simplification" P. Lindstrom, G. Turk */ + + PVert *oldv, *keepv; + PEdge *e; + PFace *oldf1, *oldf2; + float volumecost = 0.0f, areacost = 0.0f, edgevec[3], cost, weight, elen; + float shapecost = 0.0f; + float shapeold = 0.0f, shapenew = 0.0f; + int nshapeold = 0, nshapenew = 0; + + p_collapsing_verts(edge, pair, &oldv, &keepv); + oldf1 = (edge)? edge->face: NULL; + oldf2 = (pair)? pair->face: NULL; + + VecSubf(edgevec, keepv->co, oldv->co); + + e = oldv->edge; + do { + float a1, a2, a3; + float *co1 = e->next->vert->co; + float *co2 = e->next->next->vert->co; + + if ((e->face != oldf1) && (e->face != oldf2)) { + float tetrav2[3], tetrav3[3], c[3]; + + /* tetrahedron volume = (1/3!)*|a.(b x c)| */ + VecSubf(tetrav2, co1, oldv->co); + VecSubf(tetrav3, co2, oldv->co); + Crossf(c, tetrav2, tetrav3); + + volumecost += fabs(Inpf(edgevec, c)/6.0f); +#if 0 + shapecost += Inpf(co1, keepv->co); + + if (p_wheel_edge_next(e) == NULL) + shapecost += Inpf(co2, keepv->co); +#endif + + p_triangle_angles(oldv->co, co1, co2, &a1, &a2, &a3); + a1 = a1 - M_PI/3.0; + a2 = a2 - M_PI/3.0; + a3 = a3 - M_PI/3.0; + shapeold = (a1*a1 + a2*a2 + a3*a3)/((M_PI/2)*(M_PI/2)); + + nshapeold++; + } + else { + p_triangle_angles(keepv->co, co1, co2, &a1, &a2, &a3); + a1 = a1 - M_PI/3.0; + a2 = a2 - M_PI/3.0; + a3 = a3 - M_PI/3.0; + shapenew = (a1*a1 + a2*a2 + a3*a3)/((M_PI/2)*(M_PI/2)); + + nshapenew++; + } + + e = p_wheel_edge_next(e); + } while (e && (e != oldv->edge)); + + if (!p_vert_interior(oldv)) { + PVert *v1 = p_boundary_edge_prev(oldv->edge)->vert; + PVert *v2 = p_boundary_edge_next(oldv->edge)->vert; + + areacost = AreaT3Dfl(oldv->co, v1->co, v2->co); + } + + elen = VecLength(edgevec); + weight = 1.0f; /* 0.2f */ + cost = weight*volumecost*volumecost + elen*elen*areacost*areacost; +#if 0 + cost += shapecost; +#else + shapeold /= nshapeold; + shapenew /= nshapenew; + shapecost = (shapeold + 0.00001)/(shapenew + 0.00001); + + cost *= shapecost; +#endif + + return cost; +} + +static void p_collapse_cost_vertex(PVert *vert, float *mincost, PEdge **mine) +{ + PEdge *e, *enext, *pair; + + *mine = NULL; + *mincost = 0.0f; + e = vert->edge; + do { + if (p_collapse_allowed(e, e->pair)) { + float cost = p_collapse_cost(e, e->pair); + + if ((*mine == NULL) || (cost < *mincost)) { + *mincost = cost; + *mine = e; + } + } + + enext = p_wheel_edge_next(e); + + if (enext == NULL) { + /* the other boundary edge, where we only have the pair halfedge */ + pair = e->next->next; + + if (p_collapse_allowed(NULL, pair)) { + float cost = p_collapse_cost(NULL, pair); + + if ((*mine == NULL) || (cost < *mincost)) { + *mincost = cost; + *mine = pair; + } + } + + break; + } + + e = enext; + } while (e != vert->edge); +} + +static void p_chart_post_collapse_flush(PChart *chart, PEdge *collapsed) +{ + /* move to collapsed_ */ + + PVert *v, *nextv = NULL, *verts = chart->verts; + PEdge *e, *nexte = NULL, *edges = chart->edges, *laste = NULL; + PFace *f, *nextf = NULL, *faces = chart->faces; + + chart->verts = chart->collapsed_verts = NULL; + chart->edges = chart->collapsed_edges = NULL; + chart->faces = chart->collapsed_faces = NULL; + + chart->nverts = chart->nedges = chart->nfaces = 0; + + for (v=verts; v; v=nextv) { + nextv = v->nextlink; + + if (v->flag & PVERT_COLLAPSE) { + v->nextlink = chart->collapsed_verts; + chart->collapsed_verts = v; + } + else { + v->nextlink = chart->verts; + chart->verts = v; + chart->nverts++; + } + } + + for (e=edges; e; e=nexte) { + nexte = e->nextlink; + + if (!collapsed || !(e->flag & PEDGE_COLLAPSE_EDGE)) { + if (e->flag & PEDGE_COLLAPSE) { + e->nextlink = chart->collapsed_edges; + chart->collapsed_edges = e; + } + else { + e->nextlink = chart->edges; + chart->edges = e; + chart->nedges++; + } + } + } + + /* these are added last so they can be popped of in the right order + for splitting */ + for (e=collapsed; e; e=e->nextlink) { + e->nextlink = e->u.nextcollapse; + laste = e; + } + if (laste) { + laste->nextlink = chart->collapsed_edges; + chart->collapsed_edges = collapsed; + } + + for (f=faces; f; f=nextf) { + nextf = f->nextlink; + + if (f->flag & PFACE_COLLAPSE) { + f->nextlink = chart->collapsed_faces; + chart->collapsed_faces = f; + } + else { + f->nextlink = chart->faces; + chart->faces = f; + chart->nfaces++; + } + } +} + +static void p_chart_post_split_flush(PChart *chart) +{ + /* move from collapsed_ */ + + PVert *v, *nextv = NULL; + PEdge *e, *nexte = NULL; + PFace *f, *nextf = NULL; + + for (v=chart->collapsed_verts; v; v=nextv) { + nextv = v->nextlink; + v->nextlink = chart->verts; + chart->verts = v; + chart->nverts++; + } + + for (e=chart->collapsed_edges; e; e=nexte) { + nexte = e->nextlink; + e->nextlink = chart->edges; + chart->edges = e; + chart->nedges++; + } + + for (f=chart->collapsed_faces; f; f=nextf) { + nextf = f->nextlink; + f->nextlink = chart->faces; + chart->faces = f; + chart->nfaces++; + } + + chart->collapsed_verts = NULL; + chart->collapsed_edges = NULL; + chart->collapsed_faces = NULL; +} + +static void p_chart_simplify_compute(PChart *chart) +{ + /* Computes a list of edge collapses / vertex splits. The collapsed + simplices go in the chart->collapsed_* lists, The original and + collapsed may then be view as stacks, where the next collapse/split + is at the top of the respective lists. */ + + Heap *heap = BLI_heap_new(); + PVert *v, **wheelverts; + PEdge *collapsededges = NULL, *e; + int nwheelverts, i, ncollapsed = 0; + + wheelverts = MEM_mallocN(sizeof(PVert*)*chart->nverts, "PChartWheelVerts"); + + /* insert all potential collapses into heap */ + for (v=chart->verts; v; v=v->nextlink) { + float cost; + PEdge *e = NULL; + + p_collapse_cost_vertex(v, &cost, &e); + + if (e) + v->u.heaplink = BLI_heap_insert(heap, cost, e); + else + v->u.heaplink = NULL; + } + + for (e=chart->edges; e; e=e->nextlink) + e->u.nextcollapse = NULL; + + /* pop edge collapse out of heap one by one */ + while (!BLI_heap_empty(heap)) { + if (ncollapsed == NCOLLAPSE) + break; + + HeapNode *link = BLI_heap_top(heap); + PEdge *edge = (PEdge*)BLI_heap_popmin(heap), *pair = edge->pair; + PVert *oldv, *keepv; + PEdge *wheele, *nexte; + + /* remember the edges we collapsed */ + edge->u.nextcollapse = collapsededges; + collapsededges = edge; + + if (edge->vert->u.heaplink != link) { + edge->flag |= (PEDGE_COLLAPSE_EDGE|PEDGE_COLLAPSE_PAIR); + edge->next->vert->u.heaplink = NULL; + SWAP(PEdge*, edge, pair); + } + else { + edge->flag |= PEDGE_COLLAPSE_EDGE; + edge->vert->u.heaplink = NULL; + } + + p_collapsing_verts(edge, pair, &oldv, &keepv); + + /* gather all wheel verts and remember them before collapse */ + nwheelverts = 0; + wheele = oldv->edge; + + do { + wheelverts[nwheelverts++] = wheele->next->vert; + nexte = p_wheel_edge_next(wheele); + + if (nexte == NULL) + wheelverts[nwheelverts++] = wheele->next->next->vert; + + wheele = nexte; + } while (wheele && (wheele != oldv->edge)); + + /* collapse */ + p_collapse_edge(edge, pair); + + for (i = 0; i < nwheelverts; i++) { + float cost; + PEdge *collapse = NULL; + + v = wheelverts[i]; + + if (v->u.heaplink) { + BLI_heap_remove(heap, v->u.heaplink); + v->u.heaplink = NULL; + } + + p_collapse_cost_vertex(v, &cost, &collapse); + + if (collapse) + v->u.heaplink = BLI_heap_insert(heap, cost, collapse); + } + + ncollapsed++; + } + + MEM_freeN(wheelverts); + BLI_heap_free(heap, NULL); + + p_chart_post_collapse_flush(chart, collapsededges); +} + +static void p_chart_complexify(PChart *chart) +{ + PEdge *e, *pair, *edge; + PVert *newv, *keepv; + int x = 0; + + for (e=chart->collapsed_edges; e; e=e->nextlink) { + if (!(e->flag & PEDGE_COLLAPSE_EDGE)) + break; + + edge = e; + pair = e->pair; + + if (edge->flag & PEDGE_COLLAPSE_PAIR) { + SWAP(PEdge*, edge, pair); + } + + p_split_vertex(edge, pair); + p_collapsing_verts(edge, pair, &newv, &keepv); + + if (x >= NCOLLAPSEX) { + newv->uv[0] = keepv->uv[0]; + newv->uv[1] = keepv->uv[1]; + } + else { + p_vert_harmonic_insert(newv); + x++; + } + } + + p_chart_post_split_flush(chart); +} + +#if 0 +static void p_chart_simplify(PChart *chart) +{ + /* Not implemented, needs proper reordering in split_flush. */ +} +#endif +#endif + +/* ABF */ + +#define ABF_MAX_ITER 20 + +typedef struct PAbfSystem { + int ninterior, nfaces, nangles; + float *alpha, *beta, *sine, *cosine, *weight; + float *bAlpha, *bTriangle, *bInterior; + float *lambdaTriangle, *lambdaPlanar, *lambdaLength; + float (*J2dt)[3], *bstar, *dstar; + float minangle, maxangle; +} PAbfSystem; + +static void p_abf_setup_system(PAbfSystem *sys) +{ + int i; + + sys->alpha = (float*)MEM_mallocN(sizeof(float)*sys->nangles, "ABFalpha"); + sys->beta = (float*)MEM_mallocN(sizeof(float)*sys->nangles, "ABFbeta"); + sys->sine = (float*)MEM_mallocN(sizeof(float)*sys->nangles, "ABFsine"); + sys->cosine = (float*)MEM_mallocN(sizeof(float)*sys->nangles, "ABFcosine"); + sys->weight = (float*)MEM_mallocN(sizeof(float)*sys->nangles, "ABFweight"); + + sys->bAlpha = (float*)MEM_mallocN(sizeof(float)*sys->nangles, "ABFbalpha"); + sys->bTriangle = (float*)MEM_mallocN(sizeof(float)*sys->nfaces, "ABFbtriangle"); + sys->bInterior = (float*)MEM_mallocN(sizeof(float)*2*sys->ninterior, "ABFbinterior"); + + sys->lambdaTriangle = (float*)MEM_callocN(sizeof(float)*sys->nfaces, "ABFlambdatri"); + sys->lambdaPlanar = (float*)MEM_callocN(sizeof(float)*sys->ninterior, "ABFlamdaplane"); + sys->lambdaLength = (float*)MEM_mallocN(sizeof(float)*sys->ninterior, "ABFlambdalen"); + + sys->J2dt = MEM_mallocN(sizeof(float)*sys->nangles*3, "ABFj2dt"); + sys->bstar = (float*)MEM_mallocN(sizeof(float)*sys->nfaces, "ABFbstar"); + sys->dstar = (float*)MEM_mallocN(sizeof(float)*sys->nfaces, "ABFdstar"); + + for (i = 0; i < sys->ninterior; i++) + sys->lambdaLength[i] = 1.0; + + sys->minangle = 7.5f*M_PI/180.0f; + sys->maxangle = M_PI - sys->minangle; +} + +static void p_abf_free_system(PAbfSystem *sys) +{ + MEM_freeN(sys->alpha); + MEM_freeN(sys->beta); + MEM_freeN(sys->sine); + MEM_freeN(sys->cosine); + MEM_freeN(sys->weight); + MEM_freeN(sys->bAlpha); + MEM_freeN(sys->bTriangle); + MEM_freeN(sys->bInterior); + MEM_freeN(sys->lambdaTriangle); + MEM_freeN(sys->lambdaPlanar); + MEM_freeN(sys->lambdaLength); + MEM_freeN(sys->J2dt); + MEM_freeN(sys->bstar); + MEM_freeN(sys->dstar); +} + +static void p_abf_compute_sines(PAbfSystem *sys) +{ + int i; + float *sine = sys->sine, *cosine = sys->cosine, *alpha = sys->alpha; + + for (i = 0; i < sys->nangles; i++, sine++, cosine++, alpha++) { + *sine = sin(*alpha); + *cosine = cos(*alpha); + } +} + +static float p_abf_compute_sin_product(PAbfSystem *sys, PVert *v, int aid) +{ + PEdge *e, *e1, *e2; + float sin1, sin2; + + sin1 = sin2 = 1.0; + + e = v->edge; + do { + e1 = e->next; + e2 = e->next->next; + + if (aid == e1->u.id) { + /* we are computing a derivative for this angle, + so we use cos and drop the other part */ + sin1 *= sys->cosine[e1->u.id]; + sin2 = 0.0; + } + else + sin1 *= sys->sine[e1->u.id]; + + if (aid == e2->u.id) { + /* see above */ + sin1 = 0.0; + sin2 *= sys->cosine[e2->u.id]; + } + else + sin2 *= sys->sine[e2->u.id]; + + e = e->next->next->pair; + } while (e && (e != v->edge)); + + return (sin1 - sin2); +} + +static float p_abf_compute_grad_alpha(PAbfSystem *sys, PFace *f, PEdge *e) +{ + PVert *v = e->vert, *v1 = e->next->vert, *v2 = e->next->next->vert; + float deriv; + + deriv = (sys->alpha[e->u.id] - sys->beta[e->u.id])*sys->weight[e->u.id]; + deriv += sys->lambdaTriangle[f->u.id]; + + if (v->flag & PVERT_INTERIOR) { + deriv += sys->lambdaPlanar[v->u.id]; + } + + if (v1->flag & PVERT_INTERIOR) { + float product = p_abf_compute_sin_product(sys, v1, e->u.id); + deriv += sys->lambdaLength[v1->u.id]*product; + } + + if (v2->flag & PVERT_INTERIOR) { + float product = p_abf_compute_sin_product(sys, v2, e->u.id); + deriv += sys->lambdaLength[v2->u.id]*product; + } + + return deriv; +} + +static float p_abf_compute_gradient(PAbfSystem *sys, PChart *chart) +{ + PFace *f; + PEdge *e; + PVert *v; + float norm = 0.0; + + for (f=chart->faces; f; f=f->nextlink) { + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + float gtriangle, galpha1, galpha2, galpha3; + + galpha1 = p_abf_compute_grad_alpha(sys, f, e1); + galpha2 = p_abf_compute_grad_alpha(sys, f, e2); + galpha3 = p_abf_compute_grad_alpha(sys, f, e3); + + sys->bAlpha[e1->u.id] = -galpha1; + sys->bAlpha[e2->u.id] = -galpha2; + sys->bAlpha[e3->u.id] = -galpha3; + + norm += galpha1*galpha1 + galpha2*galpha2 + galpha3*galpha3; + + gtriangle = sys->alpha[e1->u.id] + sys->alpha[e2->u.id] + sys->alpha[e3->u.id] - M_PI; + sys->bTriangle[f->u.id] = -gtriangle; + norm += gtriangle*gtriangle; + } + + for (v=chart->verts; v; v=v->nextlink) { + if (v->flag & PVERT_INTERIOR) { + float gplanar = -2*M_PI, glength; + + e = v->edge; + do { + gplanar += sys->alpha[e->u.id]; + e = e->next->next->pair; + } while (e && (e != v->edge)); + + sys->bInterior[v->u.id] = -gplanar; + norm += gplanar*gplanar; + + glength = p_abf_compute_sin_product(sys, v, -1); + sys->bInterior[sys->ninterior + v->u.id] = -glength; + norm += glength*glength; + } + } + + return norm; +} + +static PBool p_abf_matrix_invert(PAbfSystem *sys, PChart *chart) +{ + PFace *f; + PEdge *e; + int i, j, ninterior = sys->ninterior, nvar = 2*sys->ninterior; + PBool success; + + nlNewContext(); + nlSolverParameteri(NL_NB_VARIABLES, nvar); + + nlBegin(NL_SYSTEM); + + nlBegin(NL_MATRIX); + + for (i = 0; i < nvar; i++) + nlRightHandSideAdd(0, i, sys->bInterior[i]); + + for (f=chart->faces; f; f=f->nextlink) { + float wi1, wi2, wi3, b, si, beta[3], j2[3][3], W[3][3]; + float row1[6], row2[6], row3[6]; + int vid[6]; + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert; + + wi1 = 1.0/sys->weight[e1->u.id]; + wi2 = 1.0/sys->weight[e2->u.id]; + wi3 = 1.0/sys->weight[e3->u.id]; + + /* bstar1 = (J1*dInv*bAlpha - bTriangle) */ + b = sys->bAlpha[e1->u.id]*wi1; + b += sys->bAlpha[e2->u.id]*wi2; + b += sys->bAlpha[e3->u.id]*wi3; + b -= sys->bTriangle[f->u.id]; + + /* si = J1*d*J1t */ + si = 1.0/(wi1 + wi2 + wi3); + + /* J1t*si*bstar1 - bAlpha */ + beta[0] = b*si - sys->bAlpha[e1->u.id]; + beta[1] = b*si - sys->bAlpha[e2->u.id]; + beta[2] = b*si - sys->bAlpha[e3->u.id]; + + /* use this later for computing other lambda's */ + sys->bstar[f->u.id] = b; + sys->dstar[f->u.id] = si; + + /* set matrix */ + W[0][0] = si - sys->weight[e1->u.id]; W[0][1] = si; W[0][2] = si; + W[1][0] = si; W[1][1] = si - sys->weight[e2->u.id]; W[1][2] = si; + W[2][0] = si; W[2][1] = si; W[2][2] = si - sys->weight[e3->u.id]; + + vid[0] = vid[1] = vid[2] = vid[3] = vid[4] = vid[5] = -1; + + if (v1->flag & PVERT_INTERIOR) { + vid[0] = v1->u.id; + vid[3] = ninterior + v1->u.id; + + sys->J2dt[e1->u.id][0] = j2[0][0] = 1.0*wi1; + sys->J2dt[e2->u.id][0] = j2[1][0] = p_abf_compute_sin_product(sys, v1, e2->u.id)*wi2; + sys->J2dt[e3->u.id][0] = j2[2][0] = p_abf_compute_sin_product(sys, v1, e3->u.id)*wi3; + + nlRightHandSideAdd(0, v1->u.id, j2[0][0]*beta[0]); + nlRightHandSideAdd(0, ninterior + v1->u.id, j2[1][0]*beta[1] + j2[2][0]*beta[2]); + + row1[0] = j2[0][0]*W[0][0]; + row2[0] = j2[0][0]*W[1][0]; + row3[0] = j2[0][0]*W[2][0]; + + row1[3] = j2[1][0]*W[0][1] + j2[2][0]*W[0][2]; + row2[3] = j2[1][0]*W[1][1] + j2[2][0]*W[1][2]; + row3[3] = j2[1][0]*W[2][1] + j2[2][0]*W[2][2]; + } + + if (v2->flag & PVERT_INTERIOR) { + vid[1] = v2->u.id; + vid[4] = ninterior + v2->u.id; + + sys->J2dt[e1->u.id][1] = j2[0][1] = p_abf_compute_sin_product(sys, v2, e1->u.id)*wi1; + sys->J2dt[e2->u.id][1] = j2[1][1] = 1.0*wi2; + sys->J2dt[e3->u.id][1] = j2[2][1] = p_abf_compute_sin_product(sys, v2, e3->u.id)*wi3; + + nlRightHandSideAdd(0, v2->u.id, j2[1][1]*beta[1]); + nlRightHandSideAdd(0, ninterior + v2->u.id, j2[0][1]*beta[0] + j2[2][1]*beta[2]); + + row1[1] = j2[1][1]*W[0][1]; + row2[1] = j2[1][1]*W[1][1]; + row3[1] = j2[1][1]*W[2][1]; + + row1[4] = j2[0][1]*W[0][0] + j2[2][1]*W[0][2]; + row2[4] = j2[0][1]*W[1][0] + j2[2][1]*W[1][2]; + row3[4] = j2[0][1]*W[2][0] + j2[2][1]*W[2][2]; + } + + if (v3->flag & PVERT_INTERIOR) { + vid[2] = v3->u.id; + vid[5] = ninterior + v3->u.id; + + sys->J2dt[e1->u.id][2] = j2[0][2] = p_abf_compute_sin_product(sys, v3, e1->u.id)*wi1; + sys->J2dt[e2->u.id][2] = j2[1][2] = p_abf_compute_sin_product(sys, v3, e2->u.id)*wi2; + sys->J2dt[e3->u.id][2] = j2[2][2] = 1.0*wi3; + + nlRightHandSideAdd(0, v3->u.id, j2[2][2]*beta[2]); + nlRightHandSideAdd(0, ninterior + v3->u.id, j2[0][2]*beta[0] + j2[1][2]*beta[1]); + + row1[2] = j2[2][2]*W[0][2]; + row2[2] = j2[2][2]*W[1][2]; + row3[2] = j2[2][2]*W[2][2]; + + row1[5] = j2[0][2]*W[0][0] + j2[1][2]*W[0][1]; + row2[5] = j2[0][2]*W[1][0] + j2[1][2]*W[1][1]; + row3[5] = j2[0][2]*W[2][0] + j2[1][2]*W[2][1]; + } + + for (i = 0; i < 3; i++) { + int r = vid[i]; + + if (r == -1) + continue; + + for (j = 0; j < 6; j++) { + int c = vid[j]; + + if (c == -1) + continue; + + if (i == 0) + nlMatrixAdd(r, c, j2[0][i]*row1[j]); + else + nlMatrixAdd(r + ninterior, c, j2[0][i]*row1[j]); + + if (i == 1) + nlMatrixAdd(r, c, j2[1][i]*row2[j]); + else + nlMatrixAdd(r + ninterior, c, j2[1][i]*row2[j]); + + + if (i == 2) + nlMatrixAdd(r, c, j2[2][i]*row3[j]); + else + nlMatrixAdd(r + ninterior, c, j2[2][i]*row3[j]); + } + } + } + + nlEnd(NL_MATRIX); + + nlEnd(NL_SYSTEM); + + success = nlSolve(); + + if (success) { + for (f=chart->faces; f; f=f->nextlink) { + float dlambda1, pre[3], dalpha; + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert; + + pre[0] = pre[1] = pre[2] = 0.0; + + if (v1->flag & PVERT_INTERIOR) { + float x = nlGetVariable(0, v1->u.id); + float x2 = nlGetVariable(0, ninterior + v1->u.id); + pre[0] += sys->J2dt[e1->u.id][0]*x; + pre[1] += sys->J2dt[e2->u.id][0]*x2; + pre[2] += sys->J2dt[e3->u.id][0]*x2; + } + + if (v2->flag & PVERT_INTERIOR) { + float x = nlGetVariable(0, v2->u.id); + float x2 = nlGetVariable(0, ninterior + v2->u.id); + pre[0] += sys->J2dt[e1->u.id][1]*x2; + pre[1] += sys->J2dt[e2->u.id][1]*x; + pre[2] += sys->J2dt[e3->u.id][1]*x2; + } + + if (v3->flag & PVERT_INTERIOR) { + float x = nlGetVariable(0, v3->u.id); + float x2 = nlGetVariable(0, ninterior + v3->u.id); + pre[0] += sys->J2dt[e1->u.id][2]*x2; + pre[1] += sys->J2dt[e2->u.id][2]*x2; + pre[2] += sys->J2dt[e3->u.id][2]*x; + } + + dlambda1 = pre[0] + pre[1] + pre[2]; + dlambda1 = sys->dstar[f->u.id]*(sys->bstar[f->u.id] - dlambda1); + + sys->lambdaTriangle[f->u.id] += dlambda1; + + dalpha = (sys->bAlpha[e1->u.id] - dlambda1); + sys->alpha[e1->u.id] += dalpha/sys->weight[e1->u.id] - pre[0]; + + dalpha = (sys->bAlpha[e2->u.id] - dlambda1); + sys->alpha[e2->u.id] += dalpha/sys->weight[e2->u.id] - pre[1]; + + dalpha = (sys->bAlpha[e3->u.id] - dlambda1); + sys->alpha[e3->u.id] += dalpha/sys->weight[e3->u.id] - pre[2]; + + /* clamp */ + e = f->edge; + do { + if (sys->alpha[e->u.id] > M_PI) + sys->alpha[e->u.id] = M_PI; + else if (sys->alpha[e->u.id] < 0.0f) + sys->alpha[e->u.id] = 0.0f; + } while (e != f->edge); + } + + for (i = 0; i < ninterior; i++) { + sys->lambdaPlanar[i] += nlGetVariable(0, i); + sys->lambdaLength[i] += nlGetVariable(0, ninterior + i); + } + } + + nlDeleteContext(nlGetCurrent()); + + return success; +} + +static PBool p_chart_abf_solve(PChart *chart) +{ + PVert *v; + PFace *f; + PEdge *e, *e1, *e2, *e3; + PAbfSystem sys; + int i; + float lastnorm, limit = (chart->nfaces > 100)? 1.0f: 0.001f; + + /* setup id's */ + sys.ninterior = sys.nfaces = sys.nangles = 0; + + for (v=chart->verts; v; v=v->nextlink) { + if (p_vert_interior(v)) { + v->flag |= PVERT_INTERIOR; + v->u.id = sys.ninterior++; + } + else + v->flag &= ~PVERT_INTERIOR; + } + + for (f=chart->faces; f; f=f->nextlink) { + e1 = f->edge; e2 = e1->next; e3 = e2->next; + f->u.id = sys.nfaces++; + + /* angle id's are conveniently stored in half edges */ + e1->u.id = sys.nangles++; + e2->u.id = sys.nangles++; + e3->u.id = sys.nangles++; + } + + p_abf_setup_system(&sys); + + /* compute initial angles */ + for (f=chart->faces; f; f=f->nextlink) { + float a1, a2, a3; + + e1 = f->edge; e2 = e1->next; e3 = e2->next; + p_face_angles(f, &a1, &a2, &a3); + + if (a1 < sys.minangle) + a1 = sys.minangle; + else if (a1 > sys.maxangle) + a1 = sys.maxangle; + if (a2 < sys.minangle) + a2 = sys.minangle; + else if (a2 > sys.maxangle) + a2 = sys.maxangle; + if (a3 < sys.minangle) + a3 = sys.minangle; + else if (a3 > sys.maxangle) + a3 = sys.maxangle; + + sys.alpha[e1->u.id] = sys.beta[e1->u.id] = a1; + sys.alpha[e2->u.id] = sys.beta[e2->u.id] = a2; + sys.alpha[e3->u.id] = sys.beta[e3->u.id] = a3; + + sys.weight[e1->u.id] = 2.0/(a1*a1); + sys.weight[e2->u.id] = 2.0/(a2*a2); + sys.weight[e3->u.id] = 2.0/(a3*a3); + } + + for (v=chart->verts; v; v=v->nextlink) { + if (v->flag & PVERT_INTERIOR) { + float anglesum = 0.0, scale; + + e = v->edge; + do { + anglesum += sys.beta[e->u.id]; + e = e->next->next->pair; + } while (e && (e != v->edge)); + + scale = (anglesum == 0.0f)? 0.0f: 2*M_PI/anglesum; + + e = v->edge; + do { + sys.beta[e->u.id] = sys.alpha[e->u.id] = sys.beta[e->u.id]*scale; + e = e->next->next->pair; + } while (e && (e != v->edge)); + } + } + + if (sys.ninterior > 0) { + p_abf_compute_sines(&sys); + + /* iteration */ + lastnorm = 1e10; + + for (i = 0; i < ABF_MAX_ITER; i++) { + float norm = p_abf_compute_gradient(&sys, chart); + + lastnorm = norm; + + if (norm < limit) + break; + + if (!p_abf_matrix_invert(&sys, chart)) { + param_warning("ABF failed to invert matrix."); + p_abf_free_system(&sys); + return P_FALSE; + } + + p_abf_compute_sines(&sys); + } + + if (i == ABF_MAX_ITER) { + param_warning("ABF maximum iterations reached."); + p_abf_free_system(&sys); + return P_FALSE; + } + } + + chart->u.lscm.abf_alpha = MEM_dupallocN(sys.alpha); + p_abf_free_system(&sys); + + return P_TRUE; +} + +/* Least Squares Conformal Maps */ + +static void p_chart_pin_positions(PChart *chart, PVert **pin1, PVert **pin2) +{ + if (pin1 == pin2) { + /* degenerate case */ + PFace *f = chart->faces; + *pin1 = f->edge->vert; + *pin2 = f->edge->next->vert; + + (*pin1)->uv[0] = 0.0f; + (*pin1)->uv[1] = 0.5f; + (*pin2)->uv[0] = 1.0f; + (*pin2)->uv[1] = 0.5f; + } + else { + int diru, dirv, dirx, diry; + float sub[3]; + + VecSubf(sub, (*pin1)->co, (*pin2)->co); + sub[0] = fabs(sub[0]); + sub[1] = fabs(sub[1]); + sub[2] = fabs(sub[2]); + + if ((sub[0] > sub[1]) && (sub[0] > sub[2])) { + dirx = 0; + diry = (sub[1] > sub[2])? 1: 2; + } + else if ((sub[1] > sub[0]) && (sub[1] > sub[2])) { + dirx = 1; + diry = (sub[0] > sub[2])? 0: 2; + } + else { + dirx = 2; + diry = (sub[0] > sub[1])? 0: 1; + } + + if (dirx == 2) { + diru = 1; + dirv = 0; + } + else { + diru = 0; + dirv = 1; + } + + (*pin1)->uv[diru] = (*pin1)->co[dirx]; + (*pin1)->uv[dirv] = (*pin1)->co[diry]; + (*pin2)->uv[diru] = (*pin2)->co[dirx]; + (*pin2)->uv[dirv] = (*pin2)->co[diry]; + } +} + +static PBool p_chart_symmetry_pins(PChart *chart, PEdge *outer, PVert **pin1, PVert **pin2) +{ + PEdge *be, *lastbe = NULL, *maxe1 = NULL, *maxe2 = NULL, *be1, *be2; + PEdge *cure = NULL, *firste1 = NULL, *firste2 = NULL, *nextbe; + float maxlen = 0.0f, curlen = 0.0f, totlen = 0.0f, firstlen = 0.0f; + float len1, len2; + + /* find longest series of verts split in the chart itself, these are + marked during construction */ + be = outer; + lastbe = p_boundary_edge_prev(be); + do { + float len = p_edge_length(be); + totlen += len; + + nextbe = p_boundary_edge_next(be); + + if ((be->vert->flag & PVERT_SPLIT) || + (lastbe->vert->flag & nextbe->vert->flag & PVERT_SPLIT)) { + if (!cure) { + if (be == outer) + firste1 = be; + cure = be; + } + else + curlen += p_edge_length(lastbe); + } + else if (cure) { + if (curlen > maxlen) { + maxlen = curlen; + maxe1 = cure; + maxe2 = lastbe; + } + + if (firste1 == cure) { + firstlen = curlen; + firste2 = lastbe; + } + + curlen = 0.0f; + cure = NULL; + } + + lastbe = be; + be = nextbe; + } while(be != outer); + + /* make sure we also count a series of splits over the starting point */ + if (cure && (cure != outer)) { + firstlen += curlen + p_edge_length(be); + + if (firstlen > maxlen) { + maxlen = firstlen; + maxe1 = cure; + maxe2 = firste2; + } + } + + if (!maxe1 || !maxe2 || (maxlen < 0.5f*totlen)) + return P_FALSE; + + /* find pin1 in the split vertices */ + be1 = maxe1; + be2 = maxe2; + len1 = 0.0f; + len2 = 0.0f; + + do { + if (len1 < len2) { + len1 += p_edge_length(be1); + be1 = p_boundary_edge_next(be1); + } + else { + be2 = p_boundary_edge_prev(be2); + len2 += p_edge_length(be2); + } + } while (be1 != be2); + + *pin1 = be1->vert; + + /* find pin2 outside the split vertices */ + be1 = maxe1; + be2 = maxe2; + len1 = 0.0f; + len2 = 0.0f; + + do { + if (len1 < len2) { + be1 = p_boundary_edge_prev(be1); + len1 += p_edge_length(be1); + } + else { + len2 += p_edge_length(be2); + be2 = p_boundary_edge_next(be2); + } + } while (be1 != be2); + + *pin2 = be1->vert; + + p_chart_pin_positions(chart, pin1, pin2); + + return P_TRUE; +} + +static void p_chart_extrema_verts(PChart *chart, PVert **pin1, PVert **pin2) +{ + float minv[3], maxv[3], dirlen; + PVert *v, *minvert[3], *maxvert[3]; + int i, dir; + + /* find minimum and maximum verts over x/y/z axes */ + minv[0] = minv[1] = minv[2] = 1e20; + maxv[0] = maxv[1] = maxv[2] = -1e20; + + minvert[0] = minvert[1] = minvert[2] = NULL; + maxvert[0] = maxvert[1] = maxvert[2] = NULL; + + for (v = chart->verts; v; v=v->nextlink) { + for (i = 0; i < 3; i++) { + if (v->co[i] < minv[i]) { + minv[i] = v->co[i]; + minvert[i] = v; + } + if (v->co[i] > maxv[i]) { + maxv[i] = v->co[i]; + maxvert[i] = v; + } + } + } + + /* find axes with longest distance */ + dir = 0; + dirlen = -1.0; + + for (i = 0; i < 3; i++) { + if (maxv[i] - minv[i] > dirlen) { + dir = i; + dirlen = maxv[i] - minv[i]; + } + } + + *pin1 = minvert[dir]; + *pin2 = maxvert[dir]; + + p_chart_pin_positions(chart, pin1, pin2); +} + +static void p_chart_lscm_load_solution(PChart *chart) +{ + PVert *v; + + for (v=chart->verts; v; v=v->nextlink) { + v->uv[0] = nlGetVariable(0, 2*v->u.id); + v->uv[1] = nlGetVariable(0, 2*v->u.id + 1); + } +} + +static void p_chart_lscm_begin(PChart *chart, PBool live, PBool abf) +{ + PVert *v, *pin1, *pin2; + PBool select = P_FALSE, deselect = P_FALSE; + int npins = 0, id = 0; + + /* give vertices matrix indices and count pins */ + for (v=chart->verts; v; v=v->nextlink) { + if (v->flag & PVERT_PIN) { + npins++; + if (v->flag & PVERT_SELECT) + select = P_TRUE; + } + + if (!(v->flag & PVERT_SELECT)) + deselect = P_TRUE; + } + + if ((live && (!select || !deselect)) || (npins == 1)) { + chart->u.lscm.context = NULL; + } + else { +#if 0 + p_chart_simplify_compute(chart); + p_chart_topological_sanity_check(chart); +#endif + + if (abf) { + if (!p_chart_abf_solve(chart)) + param_warning("ABF solving failed: falling back to LSCM.\n"); + } + + if (npins <= 1) { + /* not enough pins, lets find some ourself */ + PEdge *outer; + + p_chart_boundaries(chart, NULL, &outer); + + if (!p_chart_symmetry_pins(chart, outer, &pin1, &pin2)) + p_chart_extrema_verts(chart, &pin1, &pin2); + + chart->u.lscm.pin1 = pin1; + chart->u.lscm.pin2 = pin2; + } + else { + chart->flag |= PCHART_NOPACK; + } + + for (v=chart->verts; v; v=v->nextlink) + v->u.id = id++; + + nlNewContext(); + nlSolverParameteri(NL_NB_VARIABLES, 2*chart->nverts); + nlSolverParameteri(NL_NB_ROWS, 2*chart->nfaces); + nlSolverParameteri(NL_LEAST_SQUARES, NL_TRUE); + + chart->u.lscm.context = nlGetCurrent(); + } +} + +static PBool p_chart_lscm_solve(PHandle *handle, PChart *chart) +{ + PVert *v, *pin1 = chart->u.lscm.pin1, *pin2 = chart->u.lscm.pin2; + PFace *f; + float *alpha = chart->u.lscm.abf_alpha; + int row; + + nlMakeCurrent(chart->u.lscm.context); + + nlBegin(NL_SYSTEM); + +#if 0 + /* TODO: make loading pins work for simplify/complexify. */ +#endif + + for (v=chart->verts; v; v=v->nextlink) + if (v->flag & PVERT_PIN) + p_vert_load_pin_select_uvs(handle, v); /* reload for live */ + + if (chart->u.lscm.pin1) { + nlLockVariable(2*pin1->u.id); + nlLockVariable(2*pin1->u.id + 1); + nlLockVariable(2*pin2->u.id); + nlLockVariable(2*pin2->u.id + 1); + + nlSetVariable(0, 2*pin1->u.id, pin1->uv[0]); + nlSetVariable(0, 2*pin1->u.id + 1, pin1->uv[1]); + nlSetVariable(0, 2*pin2->u.id, pin2->uv[0]); + nlSetVariable(0, 2*pin2->u.id + 1, pin2->uv[1]); + } + else { + /* set and lock the pins */ + for (v=chart->verts; v; v=v->nextlink) { + if (v->flag & PVERT_PIN) { + nlLockVariable(2*v->u.id); + nlLockVariable(2*v->u.id + 1); + + nlSetVariable(0, 2*v->u.id, v->uv[0]); + nlSetVariable(0, 2*v->u.id + 1, v->uv[1]); + } + } + } + + /* construct matrix */ + + nlBegin(NL_MATRIX); + + row = 0; + for (f=chart->faces; f; f=f->nextlink) { + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert; + float a1, a2, a3, ratio, cosine, sine; + float sina1, sina2, sina3, sinmax; + + if (alpha) { + /* use abf angles if passed on */ + a1 = *(alpha++); + a2 = *(alpha++); + a3 = *(alpha++); + } + else + p_face_angles(f, &a1, &a2, &a3); + + sina1 = sin(a1); + sina2 = sin(a2); + sina3 = sin(a3); + + sinmax = MAX3(sina1, sina2, sina3); + + /* shift vertices to find most stable order */ + if (sina3 != sinmax) { + SHIFT3(PVert*, v1, v2, v3); + SHIFT3(float, a1, a2, a3); + SHIFT3(float, sina1, sina2, sina3); + + if (sina2 == sinmax) { + SHIFT3(PVert*, v1, v2, v3); + SHIFT3(float, a1, a2, a3); + SHIFT3(float, sina1, sina2, sina3); + } + } + + /* angle based lscm formulation */ + ratio = (sina3 == 0.0f)? 1.0f: sina2/sina3; + cosine = cos(a1)*ratio; + sine = sina1*ratio; + +#if 0 + nlBegin(NL_ROW); + nlCoefficient(2*v1->u.id, cosine - 1.0); + nlCoefficient(2*v1->u.id+1, -sine); + nlCoefficient(2*v2->u.id, -cosine); + nlCoefficient(2*v2->u.id+1, sine); + nlCoefficient(2*v3->u.id, 1.0); + nlEnd(NL_ROW); + + nlBegin(NL_ROW); + nlCoefficient(2*v1->u.id, sine); + nlCoefficient(2*v1->u.id+1, cosine - 1.0); + nlCoefficient(2*v2->u.id, -sine); + nlCoefficient(2*v2->u.id+1, -cosine); + nlCoefficient(2*v3->u.id+1, 1.0); + nlEnd(NL_ROW); +#else + nlMatrixAdd(row, 2*v1->u.id, cosine - 1.0); + nlMatrixAdd(row, 2*v1->u.id+1, -sine); + nlMatrixAdd(row, 2*v2->u.id, -cosine); + nlMatrixAdd(row, 2*v2->u.id+1, sine); + nlMatrixAdd(row, 2*v3->u.id, 1.0); + row++; + + nlMatrixAdd(row, 2*v1->u.id, sine); + nlMatrixAdd(row, 2*v1->u.id+1, cosine - 1.0); + nlMatrixAdd(row, 2*v2->u.id, -sine); + nlMatrixAdd(row, 2*v2->u.id+1, -cosine); + nlMatrixAdd(row, 2*v3->u.id+1, 1.0); + row++; +#endif + } + + nlEnd(NL_MATRIX); + + nlEnd(NL_SYSTEM); + + if (nlSolveAdvanced(NULL, NL_TRUE)) { + p_chart_lscm_load_solution(chart); + return P_TRUE; + } + else { + for (v=chart->verts; v; v=v->nextlink) { + v->uv[0] = 0.0f; + v->uv[1] = 0.0f; + } + } + + return P_FALSE; +} + +static void p_chart_lscm_end(PChart *chart) +{ + if (chart->u.lscm.context) + nlDeleteContext(chart->u.lscm.context); + + if (chart->u.lscm.abf_alpha) { + MEM_freeN(chart->u.lscm.abf_alpha); + chart->u.lscm.abf_alpha = NULL; + } + + chart->u.lscm.context = NULL; + chart->u.lscm.pin1 = NULL; + chart->u.lscm.pin2 = NULL; +} + +/* Stretch */ + +#define P_STRETCH_ITER 20 + +static void p_stretch_pin_boundary(PChart *chart) +{ + PVert *v; + + for(v=chart->verts; v; v=v->nextlink) + if (v->edge->pair == NULL) + v->flag |= PVERT_PIN; + else + v->flag &= ~PVERT_PIN; +} + +static float p_face_stretch(PFace *f) +{ + float T, w, tmp[3]; + float Ps[3], Pt[3]; + float a, c, area; + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + PVert *v1 = e1->vert, *v2 = e2->vert, *v3 = e3->vert; + + area = p_face_uv_area_signed(f); + + if (area <= 0.0f) /* flipped face -> infinite stretch */ + return 1e10f; + + w= 1.0f/(2.0f*area); + + /* compute derivatives */ + VecCopyf(Ps, v1->co); + VecMulf(Ps, (v2->uv[1] - v3->uv[1])); + + VecCopyf(tmp, v2->co); + VecMulf(tmp, (v3->uv[1] - v1->uv[1])); + VecAddf(Ps, Ps, tmp); + + VecCopyf(tmp, v3->co); + VecMulf(tmp, (v1->uv[1] - v2->uv[1])); + VecAddf(Ps, Ps, tmp); + + VecMulf(Ps, w); + + VecCopyf(Pt, v1->co); + VecMulf(Pt, (v3->uv[0] - v2->uv[0])); + + VecCopyf(tmp, v2->co); + VecMulf(tmp, (v1->uv[0] - v3->uv[0])); + VecAddf(Pt, Pt, tmp); + + VecCopyf(tmp, v3->co); + VecMulf(tmp, (v2->uv[0] - v1->uv[0])); + VecAddf(Pt, Pt, tmp); + + VecMulf(Pt, w); + + /* Sander Tensor */ + a= Inpf(Ps, Ps); + c= Inpf(Pt, Pt); + + T = sqrt(0.5f*(a + c)); + if (f->flag & PFACE_FILLED) + T *= 0.2; + + return T; +} + +static float p_stretch_compute_vertex(PVert *v) +{ + PEdge *e = v->edge; + float sum = 0.0f; + + do { + sum += p_face_stretch(e->face); + e = p_wheel_edge_next(e); + } while (e && e != (v->edge)); + + return sum; +} + +static void p_chart_stretch_minimize(PChart *chart, RNG *rng) +{ + PVert *v; + PEdge *e; + int j, nedges; + float orig_stretch, low, stretch_low, high, stretch_high, mid, stretch; + float orig_uv[2], dir[2], random_angle, trusted_radius; + + for(v=chart->verts; v; v=v->nextlink) { + if((v->flag & PVERT_PIN) || !(v->flag & PVERT_SELECT)) + continue; + + orig_stretch = p_stretch_compute_vertex(v); + orig_uv[0] = v->uv[0]; + orig_uv[1] = v->uv[1]; + + /* move vertex in a random direction */ + trusted_radius = 0.0f; + nedges = 0; + e = v->edge; + + do { + trusted_radius += p_edge_uv_length(e); + nedges++; + + e = p_wheel_edge_next(e); + } while (e && e != (v->edge)); + + trusted_radius /= 2 * nedges; + + random_angle = rng_getFloat(rng) * 2.0 * M_PI; + dir[0] = trusted_radius * cos(random_angle); + dir[1] = trusted_radius * sin(random_angle); + + /* calculate old and new stretch */ + low = 0; + stretch_low = orig_stretch; + + Vec2Addf(v->uv, orig_uv, dir); + high = 1; + stretch = stretch_high = p_stretch_compute_vertex(v); + + /* binary search for lowest stretch position */ + for (j = 0; j < P_STRETCH_ITER; j++) { + mid = 0.5 * (low + high); + v->uv[0]= orig_uv[0] + mid*dir[0]; + v->uv[1]= orig_uv[1] + mid*dir[1]; + stretch = p_stretch_compute_vertex(v); + + if (stretch_low < stretch_high) { + high = mid; + stretch_high = stretch; + } + else { + low = mid; + stretch_low = stretch; + } + } + + /* no luck, stretch has increased, reset to old values */ + if(stretch >= orig_stretch) + Vec2Copyf(v->uv, orig_uv); + } +} + +/* Minimum area enclosing rectangle for packing */ + +static int p_compare_geometric_uv(const void *a, const void *b) +{ + PVert *v1 = *(PVert**)a; + PVert *v2 = *(PVert**)b; + + if (v1->uv[0] < v2->uv[0]) + return -1; + else if (v1->uv[0] == v2->uv[0]) { + if (v1->uv[1] < v2->uv[1]) + return -1; + else if (v1->uv[1] == v2->uv[1]) + return 0; + else + return 1; + } + else + return 1; +} + +static PBool p_chart_convex_hull(PChart *chart, PVert ***verts, int *nverts, int *right) +{ + /* Graham algorithm, taken from: + * http://aspn.activestate.com/ASPN/Cookbook/Python/Recipe/117225 */ + + PEdge *be, *e; + int npoints = 0, i, ulen, llen; + PVert **U, **L, **points, **p; + + p_chart_boundaries(chart, NULL, &be); + + if (!be) + return P_FALSE; + + e = be; + do { + npoints++; + e = p_boundary_edge_next(e); + } while(e != be); + + p = points = (PVert**)MEM_mallocN(sizeof(PVert*)*npoints*2, "PCHullpoints"); + U = (PVert**)MEM_mallocN(sizeof(PVert*)*npoints, "PCHullU"); + L = (PVert**)MEM_mallocN(sizeof(PVert*)*npoints, "PCHullL"); + + e = be; + do { + *p = e->vert; + p++; + e = p_boundary_edge_next(e); + } while(e != be); + + qsort(points, npoints, sizeof(PVert*), p_compare_geometric_uv); + + ulen = llen = 0; + for (p=points, i = 0; i < npoints; i++, p++) { + while ((ulen > 1) && (p_area_signed(U[ulen-2]->uv, (*p)->uv, U[ulen-1]->uv) <= 0)) + ulen--; + while ((llen > 1) && (p_area_signed(L[llen-2]->uv, (*p)->uv, L[llen-1]->uv) >= 0)) + llen--; + + U[ulen] = *p; + ulen++; + L[llen] = *p; + llen++; + } + + npoints = 0; + for (p=points, i = 0; i < ulen; i++, p++, npoints++) + *p = U[i]; + + /* the first and last point in L are left out, since they are also in U */ + for (i = llen-2; i > 0; i--, p++, npoints++) + *p = L[i]; + + *verts = points; + *nverts = npoints; + *right = ulen - 1; + + MEM_freeN(U); + MEM_freeN(L); + + return P_TRUE; +} + +static float p_rectangle_area(float *p1, float *dir, float *p2, float *p3, float *p4) +{ + /* given 4 points on the rectangle edges and the direction of on edge, + compute the area of the rectangle */ + + float orthodir[2], corner1[2], corner2[2], corner3[2]; + + orthodir[0] = dir[1]; + orthodir[1] = -dir[0]; + + if (!p_intersect_line_2d_dir(p1, dir, p2, orthodir, corner1)) + return 1e10; + + if (!p_intersect_line_2d_dir(p1, dir, p4, orthodir, corner2)) + return 1e10; + + if (!p_intersect_line_2d_dir(p3, dir, p4, orthodir, corner3)) + return 1e10; + + return Vec2Lenf(corner1, corner2)*Vec2Lenf(corner2, corner3); +} + +static float p_chart_minimum_area_angle(PChart *chart) +{ + /* minimum area enclosing rectangle with rotating calipers, info: + * http://cgm.cs.mcgill.ca/~orm/maer.html */ + + float rotated, minarea, minangle, area, len; + float *angles, miny, maxy, v[2], a[4], mina; + int npoints, right, mini, maxi, i, idx[4], nextidx; + PVert **points, *p1, *p2, *p3, *p4, *p1n; + + /* compute convex hull */ + if (!p_chart_convex_hull(chart, &points, &npoints, &right)) + return 0.0; + + /* find left/top/right/bottom points, and compute angle for each point */ + angles = MEM_mallocN(sizeof(float)*npoints, "PMinAreaAngles"); + + mini = maxi = 0; + miny = 1e10; + maxy = -1e10; + + for (i = 0; i < npoints; i++) { + p1 = (i == 0)? points[npoints-1]: points[i-1]; + p2 = points[i]; + p3 = (i == npoints-1)? points[0]: points[i+1]; + + angles[i] = M_PI - p_vec2_angle(p1->uv, p2->uv, p3->uv); + + if (points[i]->uv[1] < miny) { + miny = points[i]->uv[1]; + mini = i; + } + if (points[i]->uv[1] > maxy) { + maxy = points[i]->uv[1]; + maxi = i; + } + } + + /* left, top, right, bottom */ + idx[0] = 0; + idx[1] = maxi; + idx[2] = right; + idx[3] = mini; + + v[0] = points[idx[0]]->uv[0]; + v[1] = points[idx[0]]->uv[1] + 1.0f; + a[0] = p_vec2_angle(points[(idx[0]+1)%npoints]->uv, points[idx[0]]->uv, v); + + v[0] = points[idx[1]]->uv[0] + 1.0f; + v[1] = points[idx[1]]->uv[1]; + a[1] = p_vec2_angle(points[(idx[1]+1)%npoints]->uv, points[idx[1]]->uv, v); + + v[0] = points[idx[2]]->uv[0]; + v[1] = points[idx[2]]->uv[1] - 1.0f; + a[2] = p_vec2_angle(points[(idx[2]+1)%npoints]->uv, points[idx[2]]->uv, v); + + v[0] = points[idx[3]]->uv[0] - 1.0f; + v[1] = points[idx[3]]->uv[1]; + a[3] = p_vec2_angle(points[(idx[3]+1)%npoints]->uv, points[idx[3]]->uv, v); + + /* 4 rotating calipers */ + + rotated = 0.0; + minarea = 1e10; + minangle = 0.0; + + while (rotated <= M_PI/2) { /* INVESTIGATE: how far to rotate? */ + /* rotate with the smallest angle */ + mini = 0; + mina = 1e10; + + for (i = 0; i < 4; i++) + if (a[i] < mina) { + mina = a[i]; + mini = i; + } + + rotated += mina; + nextidx = (idx[mini]+1)%npoints; + + a[mini] = angles[nextidx]; + a[(mini+1)%4] = a[(mini+1)%4] - mina; + a[(mini+2)%4] = a[(mini+2)%4] - mina; + a[(mini+3)%4] = a[(mini+3)%4] - mina; + + /* compute area */ + p1 = points[idx[mini]]; + p1n = points[nextidx]; + p2 = points[idx[(mini+1)%4]]; + p3 = points[idx[(mini+2)%4]]; + p4 = points[idx[(mini+3)%4]]; + + len = Vec2Lenf(p1->uv, p1n->uv); + + if (len > 0.0f) { + len = 1.0/len; + v[0] = (p1n->uv[0] - p1->uv[0])*len; + v[1] = (p1n->uv[1] - p1->uv[1])*len; + + area = p_rectangle_area(p1->uv, v, p2->uv, p3->uv, p4->uv); + + /* remember smallest area */ + if (area < minarea) { + minarea = area; + minangle = rotated; + } + } + + idx[mini] = nextidx; + } + + /* try keeping rotation as small as possible */ + if (minangle > M_PI/4) + minangle -= M_PI/2; + + MEM_freeN(angles); + MEM_freeN(points); + + return minangle; +} + +static void p_chart_rotate_minimum_area(PChart *chart) +{ + float angle = p_chart_minimum_area_angle(chart); + float sine = sin(angle); + float cosine = cos(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; + } +} + +/* Area Smoothing */ + +/* 2d bsp tree for inverse mapping - that's a bit silly */ + +typedef struct SmoothTriangle { + float co1[2], co2[2], co3[2]; + float oco1[2], oco2[2], oco3[2]; +} SmoothTriangle; + +typedef struct SmoothNode { + struct SmoothNode *c1, *c2; + SmoothTriangle **tri; + float split; + int axis, ntri; +} SmoothNode; + +static void p_barycentric_2d(float *v1, float *v2, float *v3, float *p, float *b) +{ + float a[2], c[2], h[2], div; + + a[0] = v2[0] - v1[0]; + a[1] = v2[1] - v1[1]; + c[0] = v3[0] - v1[0]; + c[1] = v3[1] - v1[1]; + + div = a[0]*c[1] - a[1]*c[0]; + + if (div == 0.0f) { + b[0] = 1.0f/3.0f; + b[1] = 1.0f/3.0f; + b[2] = 1.0f/3.0f; + } + else { + h[0] = p[0] - v1[0]; + h[1] = p[1] - v1[1]; + + div = 1.0f/div; + + b[1] = (h[0]*c[1] - h[1]*c[0])*div; + b[2] = (a[0]*h[1] - a[1]*h[0])*div; + b[0] = 1.0 - b[1] - b[2]; + } +} + +static PBool p_triangle_inside(SmoothTriangle *t, float *co) +{ + float b[3]; + + p_barycentric_2d(t->co1, t->co2, t->co3, co, b); + + if ((b[0] >= 0.0) && (b[1] >= 0.0) && (b[2] >= 0.0f)) { + co[0] = t->oco1[0]*b[0] + t->oco2[0]*b[1] + t->oco3[0]*b[2]; + co[1] = t->oco1[1]*b[0] + t->oco2[1]*b[1] + t->oco3[1]*b[2]; + 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); + int axis, i, t1size = 0, t2size = 0; + float split, mi, mx; + SmoothTriangle **t1, **t2, *t; + + node->tri = tri; + node->ntri = ntri; + + if (ntri <= 10 || depth >= 15) + return node; + + t1 = MEM_mallocN(sizeof(SmoothTriangle)*ntri, "PNodeTri1"); + t2 = MEM_mallocN(sizeof(SmoothTriangle)*ntri, "PNodeTri1"); + + axis = (bmax[0] - bmin[0] > bmax[1] - bmin[1])? 0: 1; + split = 0.5f*(bmin[axis] + bmax[axis]); + + for (i = 0; i < ntri; i++) { + t = tri[i]; + + if ((t->co1[axis] <= split) || (t->co2[axis] <= split) || (t->co3[axis] <= split)) { + t1[t1size] = t; + t1size++; + } + if ((t->co1[axis] >= split) || (t->co2[axis] >= split) || (t->co3[axis] >= split)) { + t2[t2size] = t; + t2size++; + } + } + + if ((t1size == t2size) && (t1size == ntri)) { + MEM_freeN(t1); + MEM_freeN(t2); + return node; + } + + node->tri = NULL; + node->ntri = 0; + MEM_freeN(tri); + + node->axis = axis; + node->split = split; + + mi = bmin[axis]; + mx = bmax[axis]; + bmax[axis] = split; + node->c1 = p_node_new(arena, t1, t1size, bmin, bmax, depth+1); + + bmin[axis] = bmax[axis]; + bmax[axis] = mx; + node->c2 = p_node_new(arena, t2, t2size, bmin, bmax, depth+1); + + return node; +} + +static void p_node_delete(SmoothNode *node) +{ + if (node->c1) + p_node_delete(node->c1); + if (node->c2) + p_node_delete(node->c2); + if (node->tri) + MEM_freeN(node->tri); +} + +static PBool p_node_intersect(SmoothNode *node, float *co) +{ + int i; + + if (node->tri) { + for (i = 0; i < node->ntri; i++) + if (p_triangle_inside(node->tri[i], co)) + return P_TRUE; + + return P_FALSE; + } + else { + if (co[node->axis] < node->split) + return p_node_intersect(node->c1, co); + else + return p_node_intersect(node->c2, co); + } + +} + +/* smooothing */ + +static int p_compare_float(const void *a, const void *b) +{ + if (*((float*)a) < *((float*)b)) + return -1; + else if (*((float*)a) == *((float*)b)) + return 0; + else + return 1; +} + +static float p_smooth_median_edge_length(PChart *chart) +{ + PEdge *e; + float *lengths = MEM_mallocN(sizeof(chart->edges)*chart->nedges, "PMedianLength"); + float median; + int i; + + /* ok, so i'm lazy */ + for (i=0, e=chart->edges; e; e=e->nextlink, i++) + lengths[i] = p_edge_length(e); + + qsort(lengths, i, sizeof(float), p_compare_float); + + median = lengths[i/2]; + MEM_freeN(lengths); + + return median; +} + +static float p_smooth_distortion(PEdge *e, float avg2d, float avg3d) +{ + float len2d = p_edge_uv_length(e)*avg3d; + float len3d = p_edge_length(e)*avg2d; + + return (len3d == 0.0f)? 0.0f: len2d/len3d; +} + +static void p_smooth(PChart *chart) +{ + PEdge *e; + PVert *v; + PFace *f; + int j, it2, maxiter2, it; + int nedges = chart->nedges, nwheel, gridx, gridy; + int edgesx, edgesy, nsize, esize, i, x, y, maxiter, totiter; + float minv[2], maxv[2], median, invmedian, distortion, avglen2d, avglen3d; + float center[2], dx, dy, *nodes, dlimit, d, *oldnodesx, *oldnodesy; + float *nodesx, *nodesy, *hedges, *vedges, climit, moved, padding; + SmoothTriangle *triangles, *t, *t2, **tri, **trip; + SmoothNode *root; + MemArena *arena; + + if (nedges == 0) + return; + + p_chart_uv_bbox(chart, minv, maxv); + median = p_smooth_median_edge_length(chart)*0.10f; + + if (median == 0.0) + return; + + invmedian = 1.0/median; + + /* compute edge distortion */ + distortion = 0.0; + avglen2d = avglen3d = 0.0; + + for (e=chart->edges; e; e=e->nextlink) { + avglen2d += p_edge_uv_length(e); + avglen3d += p_edge_length(e); + } + + avglen2d /= nedges; + avglen3d /= nedges; + + for (v=chart->verts; v; v=v->nextlink) { + v->u.distortion = 0.0; + nwheel = 0; + + e = v->edge; + do { + v->u.distortion += p_smooth_distortion(e, avglen2d, avglen3d); + nwheel++; + + e = e->next->next->pair; + } while(e && (e != v->edge)); + + v->u.distortion /= nwheel; + } + + /* need to do excessive grid size checking still */ + center[0] = 0.5f*(minv[0] + maxv[0]); + center[1] = 0.5f*(minv[1] + maxv[1]); + + dx = 0.5f*(maxv[0] - minv[0]); + dy = 0.5f*(maxv[1] - minv[1]); + + padding = 0.15f; + dx += padding*dx + 2.0f*median; + dy += padding*dy + 2.0f*median; + + gridx = (int)(dx*invmedian); + gridy = (int)(dy*invmedian); + + minv[0] = center[0] - median*gridx; + minv[1] = center[1] - median*gridy; + maxv[0] = center[0] + median*gridx; + maxv[1] = center[1] + median*gridy; + + /* create grid */ + gridx = gridx*2 + 1; + gridy = gridy*2 + 1; + + if ((gridx <= 2) || (gridy <= 2)) + return; + + edgesx = gridx-1; + edgesy = gridy-1; + nsize = gridx*gridy; + esize = edgesx*edgesy; + + nodes = MEM_mallocN(sizeof(float)*nsize, "PSmoothNodes"); + nodesx = MEM_mallocN(sizeof(float)*nsize, "PSmoothNodesX"); + nodesy = MEM_mallocN(sizeof(float)*nsize, "PSmoothNodesY"); + oldnodesx = MEM_mallocN(sizeof(float)*nsize, "PSmoothOldNodesX"); + oldnodesy = MEM_mallocN(sizeof(float)*nsize, "PSmoothOldNodesY"); + hedges = MEM_mallocN(sizeof(float)*esize, "PSmoothHEdges"); + vedges = MEM_mallocN(sizeof(float)*esize, "PSmoothVEdges"); + + if (!nodes || !nodesx || !nodesy || !oldnodesx || !oldnodesy || !hedges || !vedges) { + if (nodes) MEM_freeN(nodes); + if (nodesx) MEM_freeN(nodesx); + if (nodesy) MEM_freeN(nodesy); + if (oldnodesx) MEM_freeN(oldnodesx); + if (oldnodesy) MEM_freeN(oldnodesy); + if (hedges) MEM_freeN(hedges); + if (vedges) MEM_freeN(vedges); + + // XXX error("Not enough memory for area smoothing grid."); + return; + } + + for (x = 0; x < gridx; x++) { + for (y = 0; y < gridy; y++) { + i = x + y*gridx; + + nodesx[i] = minv[0] + median*x; + nodesy[i] = minv[1] + median*y; + + nodes[i] = 1.0f; + } + } + + /* embed in grid */ + for (f=chart->faces; f; f=f->nextlink) { + PEdge *e1 = f->edge, *e2 = e1->next, *e3 = e2->next; + float fmin[2], fmax[2]; + int bx1, by1, bx2, by2; + + INIT_MINMAX2(fmin, fmax); + + DO_MINMAX2(e1->vert->uv, fmin, fmax); + DO_MINMAX2(e2->vert->uv, fmin, fmax); + DO_MINMAX2(e3->vert->uv, fmin, fmax); + + bx1 = (int)((fmin[0] - minv[0])*invmedian); + by1 = (int)((fmin[1] - minv[1])*invmedian); + bx2 = (int)((fmax[0] - minv[0])*invmedian + 2); + by2 = (int)((fmax[1] - minv[1])*invmedian + 2); + + for (x = bx1; x < bx2; x++) { + for (y = by1; y < by2; y++) { + float p[2], b[3]; + + i = x + y*gridx; + + p[0] = nodesx[i]; + p[1] = nodesy[i]; + + p_barycentric_2d(e1->vert->uv, e2->vert->uv, e3->vert->uv, p, b); + + if ((b[0] > 0.0) && (b[1] > 0.0) && (b[2] > 0.0)) { + nodes[i] = e1->vert->u.distortion*b[0]; + nodes[i] += e2->vert->u.distortion*b[1]; + nodes[i] += e3->vert->u.distortion*b[2]; + } + } + } + } + + /* smooth the grid */ + maxiter = 10; + totiter = 0; + climit = 0.00001f*nsize; + + for (it = 0; it < maxiter; it++) { + moved = 0.0f; + + for (x = 0; x < edgesx; x++) { + for (y = 0; y < edgesy; y++) { + i = x + y*gridx; + j = x + y*edgesx; + + hedges[j] = (nodes[i] + nodes[i+1])*0.5f; + vedges[j] = (nodes[i] + nodes[i+gridx])*0.5f; + + /* we do *inverse* mapping */ + hedges[j] = 1.0f/hedges[j]; + vedges[j] = 1.0f/vedges[j]; + } + } + + maxiter2 = 50; + dlimit = 0.0001f; + + for (it2 = 0; it2 < maxiter2; it2++) { + d = 0.0f; + totiter += 1; + + memcpy(oldnodesx, nodesx, sizeof(float)*nsize); + memcpy(oldnodesy, nodesy, sizeof(float)*nsize); + + for (x=1; x < gridx-1; x++) { + for (y=1; y < gridy-1; y++) { + float p[2], oldp[2], sum1, sum2, diff[2], length; + + i = x + gridx*y; + j = x + edgesx*y; + + oldp[0] = oldnodesx[i]; + oldp[1] = oldnodesy[i]; + + sum1 = hedges[j-1]*oldnodesx[i-1]; + sum1 += hedges[j]*oldnodesx[i+1]; + sum1 += vedges[j-edgesx]*oldnodesx[i-gridx]; + sum1 += vedges[j]*oldnodesx[i+gridx]; + + sum2 = hedges[j-1]; + sum2 += hedges[j]; + sum2 += vedges[j-edgesx]; + sum2 += vedges[j]; + + nodesx[i] = sum1/sum2; + + sum1 = hedges[j-1]*oldnodesy[i-1]; + sum1 += hedges[j]*oldnodesy[i+1]; + sum1 += vedges[j-edgesx]*oldnodesy[i-gridx]; + sum1 += vedges[j]*oldnodesy[i+gridx]; + + nodesy[i] = sum1/sum2; + + p[0] = nodesx[i]; + p[1] = nodesy[i]; + + diff[0] = p[0] - oldp[0]; + diff[1] = p[1] - oldp[1]; + + length = sqrt(diff[0]*diff[0] + diff[1]*diff[1]); + d = MAX2(d, length); + moved += length; + } + } + + if (d < dlimit) + break; + } + + if (moved < climit) + break; + } + + MEM_freeN(oldnodesx); + MEM_freeN(oldnodesy); + MEM_freeN(hedges); + MEM_freeN(vedges); + + /* create bsp */ + t = triangles = MEM_mallocN(sizeof(SmoothTriangle)*esize*2, "PSmoothTris"); + trip = tri = MEM_mallocN(sizeof(SmoothTriangle*)*esize*2, "PSmoothTriP"); + + if (!triangles || !tri) { + MEM_freeN(nodes); + MEM_freeN(nodesx); + MEM_freeN(nodesy); + + if (triangles) MEM_freeN(triangles); + if (tri) MEM_freeN(tri); + + // XXX error("Not enough memory for area smoothing grid."); + return; + } + + for (x = 0; x < edgesx; x++) { + for (y = 0; y < edgesy; y++) { + i = x + y*gridx; + + t->co1[0] = nodesx[i]; + t->co1[1] = nodesy[i]; + + t->co2[0] = nodesx[i+1]; + t->co2[1] = nodesy[i+1]; + + t->co3[0] = nodesx[i+gridx]; + t->co3[1] = nodesy[i+gridx]; + + t->oco1[0] = minv[0] + x*median; + t->oco1[1] = minv[1] + y*median; + + t->oco2[0] = minv[0] + (x+1)*median; + t->oco2[1] = minv[1] + y*median; + + t->oco3[0] = minv[0] + x*median; + t->oco3[1] = minv[1] + (y+1)*median; + + t2 = t+1; + + t2->co1[0] = nodesx[i+gridx+1]; + t2->co1[1] = nodesy[i+gridx+1]; + + t2->oco1[0] = minv[0] + (x+1)*median; + t2->oco1[1] = minv[1] + (y+1)*median; + + t2->co2[0] = t->co2[0]; t2->co2[1] = t->co2[1]; + t2->oco2[0] = t->oco2[0]; t2->oco2[1] = t->oco2[1]; + + t2->co3[0] = t->co3[0]; t2->co3[1] = t->co3[1]; + t2->oco3[0] = t->oco3[0]; t2->oco3[1] = t->oco3[1]; + + *trip = t; trip++; t++; + *trip = t; trip++; t++; + } + } + + MEM_freeN(nodes); + MEM_freeN(nodesx); + MEM_freeN(nodesy); + + arena = BLI_memarena_new(1<<16); + root = p_node_new(arena, tri, esize*2, minv, maxv, 0); + + for (v=chart->verts; v; v=v->nextlink) + if (!p_node_intersect(root, v->uv)) + param_warning("area smoothing error: couldn't find mapping triangle\n"); + + p_node_delete(root); + BLI_memarena_free(arena); + + MEM_freeN(triangles); +} + +/* Exported */ + +ParamHandle *param_construct_begin() +{ + PHandle *handle = MEM_callocN(sizeof*handle, "PHandle"); + handle->construction_chart = p_chart_new(handle); + handle->state = PHANDLE_STATE_ALLOCATED; + handle->arena = BLI_memarena_new((1<<16)); + handle->aspx = 1.0f; + handle->aspy = 1.0f; + + handle->hash_verts = phash_new((PHashLink**)&handle->construction_chart->verts, 1); + handle->hash_edges = phash_new((PHashLink**)&handle->construction_chart->edges, 1); + handle->hash_faces = phash_new((PHashLink**)&handle->construction_chart->faces, 1); + + return (ParamHandle*)handle; +} + +void param_aspect_ratio(ParamHandle *handle, float aspx, float aspy) +{ + PHandle *phandle = (PHandle*)handle; + + phandle->aspx = aspx; + phandle->aspy = aspy; +} + +void param_delete(ParamHandle *handle) +{ + PHandle *phandle = (PHandle*)handle; + int i; + + param_assert((phandle->state == PHANDLE_STATE_ALLOCATED) || + (phandle->state == PHANDLE_STATE_CONSTRUCTED)); + + for (i = 0; i < phandle->ncharts; i++) + p_chart_delete(phandle->charts[i]); + + if (phandle->charts) + MEM_freeN(phandle->charts); + + if (phandle->construction_chart) { + p_chart_delete(phandle->construction_chart); + + phash_delete(phandle->hash_verts); + phash_delete(phandle->hash_edges); + phash_delete(phandle->hash_faces); + } + + BLI_memarena_free(phandle->arena); + MEM_freeN(phandle); +} + +void param_face_add(ParamHandle *handle, ParamKey key, int nverts, + ParamKey *vkeys, float **co, float **uv, + ParamBool *pin, ParamBool *select) +{ + PHandle *phandle = (PHandle*)handle; + + param_assert(phash_lookup(phandle->hash_faces, key) == NULL); + param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); + param_assert((nverts == 3) || (nverts == 4)); + + if (nverts == 4) { + 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); + } + 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); + } + } + else + p_face_add_construct(phandle, key, vkeys, co, uv, 0, 1, 2, pin, select); +} + +void param_edge_set_seam(ParamHandle *handle, ParamKey *vkeys) +{ + PHandle *phandle = (PHandle*)handle; + PEdge *e; + + param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); + + e = p_edge_lookup(phandle, vkeys); + if (e) + e->flag |= PEDGE_SEAM; +} + +void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool impl) +{ + PHandle *phandle = (PHandle*)handle; + PChart *chart = phandle->construction_chart; + int i, j, nboundaries = 0; + PEdge *outer; + + param_assert(phandle->state == PHANDLE_STATE_ALLOCATED); + + phandle->ncharts = p_connect_pairs(phandle, impl); + phandle->charts = p_split_charts(phandle, chart, phandle->ncharts); + + p_chart_delete(phandle->construction_chart); + phandle->construction_chart = NULL; + + phash_delete(phandle->hash_verts); + phash_delete(phandle->hash_edges); + phash_delete(phandle->hash_faces); + phandle->hash_verts = phandle->hash_edges = phandle->hash_faces = NULL; + + for (i = j = 0; i < phandle->ncharts; i++) { + PVert *v; + chart = phandle->charts[i]; + + p_chart_boundaries(chart, &nboundaries, &outer); + + if (!impl && nboundaries == 0) { + p_chart_delete(chart); + continue; + } + + phandle->charts[j] = chart; + j++; + + if (fill && (nboundaries > 1)) + p_chart_fill_boundaries(chart, outer); + + for (v=chart->verts; v; v=v->nextlink) + p_vert_load_pin_select_uvs(handle, v); + } + + phandle->ncharts = j; + + phandle->state = PHANDLE_STATE_CONSTRUCTED; +} + +void param_lscm_begin(ParamHandle *handle, ParamBool live, ParamBool abf) +{ + PHandle *phandle = (PHandle*)handle; + PFace *f; + int i; + + param_assert(phandle->state == PHANDLE_STATE_CONSTRUCTED); + phandle->state = PHANDLE_STATE_LSCM; + + for (i = 0; i < phandle->ncharts; i++) { + for (f=phandle->charts[i]->faces; f; f=f->nextlink) + p_face_backup_uvs(f); + p_chart_lscm_begin(phandle->charts[i], live, abf); + } +} + +void param_lscm_solve(ParamHandle *handle) +{ + PHandle *phandle = (PHandle*)handle; + PChart *chart; + int i; + PBool result; + + param_assert(phandle->state == PHANDLE_STATE_LSCM); + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + if (chart->u.lscm.context) { + result = p_chart_lscm_solve(phandle, chart); + + if (result && !(chart->flag & PCHART_NOPACK)) + p_chart_rotate_minimum_area(chart); + + if (!result || (chart->u.lscm.pin1)) + p_chart_lscm_end(chart); + } + } +} + +void param_lscm_end(ParamHandle *handle) +{ + PHandle *phandle = (PHandle*)handle; + int i; + + param_assert(phandle->state == PHANDLE_STATE_LSCM); + + for (i = 0; i < phandle->ncharts; i++) { + p_chart_lscm_end(phandle->charts[i]); +#if 0 + p_chart_complexify(phandle->charts[i]); +#endif + } + + phandle->state = PHANDLE_STATE_CONSTRUCTED; +} + +void param_stretch_begin(ParamHandle *handle) +{ + PHandle *phandle = (PHandle*)handle; + PChart *chart; + PVert *v; + PFace *f; + int i; + + param_assert(phandle->state == PHANDLE_STATE_CONSTRUCTED); + phandle->state = PHANDLE_STATE_STRETCH; + + phandle->rng = rng_new(31415926); + phandle->blend = 0.0f; + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + for (v=chart->verts; v; v=v->nextlink) + v->flag &= ~PVERT_PIN; /* don't use user-defined pins */ + + p_stretch_pin_boundary(chart); + + for (f=chart->faces; f; f=f->nextlink) { + p_face_backup_uvs(f); + f->u.area3d = p_face_area(f); + } + } +} + +void param_stretch_blend(ParamHandle *handle, float blend) +{ + PHandle *phandle = (PHandle*)handle; + + param_assert(phandle->state == PHANDLE_STATE_STRETCH); + phandle->blend = blend; +} + +void param_stretch_iter(ParamHandle *handle) +{ + PHandle *phandle = (PHandle*)handle; + PChart *chart; + int i; + + param_assert(phandle->state == PHANDLE_STATE_STRETCH); + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + p_chart_stretch_minimize(chart, phandle->rng); + } +} + +void param_stretch_end(ParamHandle *handle) +{ + PHandle *phandle = (PHandle*)handle; + + param_assert(phandle->state == PHANDLE_STATE_STRETCH); + phandle->state = PHANDLE_STATE_CONSTRUCTED; + + rng_free(phandle->rng); + phandle->rng = NULL; +} + +void param_smooth_area(ParamHandle *handle) +{ + PHandle *phandle = (PHandle*)handle; + int i; + + param_assert(phandle->state == PHANDLE_STATE_CONSTRUCTED); + + for (i = 0; i < phandle->ncharts; i++) { + PChart *chart = phandle->charts[i]; + PVert *v; + + for (v=chart->verts; v; v=v->nextlink) + v->flag &= ~PVERT_PIN; + + p_smooth(chart); + } +} + +void param_pack(ParamHandle *handle) +{ + /* box packing variables */ + boxPack *boxarray, *box; + float tot_width, tot_height, scale; + + PChart *chart; + int i, unpacked=0; + float trans[2]; + + PHandle *phandle = (PHandle*)handle; + + if (phandle->ncharts == 0) + return; + + if(phandle->aspx != phandle->aspy) + param_scale(handle, 1.0f/phandle->aspx, 1.0f/phandle->aspy); + + /* we may not use all these boxes */ + boxarray = MEM_mallocN( phandle->ncharts*sizeof(boxPack), "boxPack box"); + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + if (chart->flag & PCHART_NOPACK) { + unpacked++; + continue; + } + + box = boxarray+(i-unpacked); + + p_chart_uv_bbox(chart, trans, chart->u.pack.size); + + trans[0] = -trans[0]; + trans[1] = -trans[1]; + + p_chart_uv_translate(chart, trans); + + box->w = chart->u.pack.size[0] + trans[0]; + box->h = chart->u.pack.size[1] + trans[1]; + box->index = i; /* warning this index skips PCHART_NOPACK boxes */ + } + + boxPack2D(boxarray, phandle->ncharts-unpacked, &tot_width, &tot_height); + + if (tot_height>tot_width) + scale = 1.0/tot_height; + else + scale = 1.0/tot_width; + + for (i = 0; i < phandle->ncharts-unpacked; i++) { + box = boxarray+i; + trans[0] = box->x; + trans[1] = box->y; + + chart = phandle->charts[box->index]; + p_chart_uv_translate(chart, trans); + p_chart_uv_scale(chart, scale); + } + MEM_freeN(boxarray); + + if(phandle->aspx != phandle->aspy) + param_scale(handle, phandle->aspx, phandle->aspy); +} + +void param_average(ParamHandle *handle) +{ + PChart *chart; + int i; + float tot_uvarea = 0.0f, tot_facearea = 0.0f; + float tot_fac, fac; + float minv[2], maxv[2], trans[2]; + PHandle *phandle = (PHandle*)handle; + + if (phandle->ncharts == 0) + return; + + for (i = 0; i < phandle->ncharts; i++) { + PFace *f; + chart = phandle->charts[i]; + + chart->u.pack.area = 0.0f; /* 3d area */ + chart->u.pack.rescale = 0.0f; /* UV area, abusing rescale for tmp storage, oh well :/ */ + + for (f=chart->faces; f; f=f->nextlink) { + chart->u.pack.area += p_face_area(f); + chart->u.pack.rescale += fabs(p_face_uv_area_signed(f)); + } + + tot_facearea += chart->u.pack.area; + tot_uvarea += chart->u.pack.rescale; + } + + if (tot_facearea == tot_uvarea || tot_facearea==0.0f || tot_uvarea==0.0f) { + /* nothing to do */ + return; + } + + tot_fac = tot_facearea/tot_uvarea; + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + if (chart->u.pack.area != 0.0f && chart->u.pack.rescale != 0.0f) { + fac = chart->u.pack.area / chart->u.pack.rescale; + + /* 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, sqrt(fac / tot_fac)); + + /* Move to original center */ + trans[0] = -trans[0]; + trans[1] = -trans[1]; + p_chart_uv_translate(chart, trans); + } + } +} + +void param_scale(ParamHandle *handle, float x, float y) +{ + PHandle *phandle = (PHandle*)handle; + PChart *chart; + int i; + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + p_chart_uv_scale_xy(chart, x, y); + } +} + +void param_flush(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(phandle, chart); + else + p_flush_uvs_blend(phandle, chart, phandle->blend); + } +} + +void param_flush_restore(ParamHandle *handle) +{ + PHandle *phandle = (PHandle*)handle; + PChart *chart; + PFace *f; + int i; + + for (i = 0; i < phandle->ncharts; i++) { + chart = phandle->charts[i]; + + for (f=chart->faces; f; f=f->nextlink) + p_face_restore_uvs(f); + } +} + diff --git a/source/blender/editors/uvedit/uvedit_parametrizer.h b/source/blender/editors/uvedit/uvedit_parametrizer.h new file mode 100644 index 00000000000..c468b8d62c5 --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_parametrizer.h @@ -0,0 +1,97 @@ + +#ifndef __PARAMETRIZER_H__ +#define __PARAMETRIZER_H__ + +#ifdef __cplusplus +extern "C" { +#endif + +#include "BLO_sys_types.h" // for intptr_t support + +typedef void ParamHandle; /* handle to a set of charts */ +typedef intptr_t ParamKey; /* (hash) key for identifying verts and faces */ +typedef enum ParamBool { + PARAM_TRUE = 1, + PARAM_FALSE = 0 +} ParamBool; + +/* Chart construction: + ------------------- + - faces and seams may only be added between construct_{begin|end} + - the pointers to co and uv are stored, rather than being copied + - vertices are implicitly created + - in construct_end the mesh will be split up according to the seams + - the resulting charts must be: + - manifold, connected, open (at least one boundary loop) + - output will be written to the uv pointers +*/ + +ParamHandle *param_construct_begin(); + +void param_aspect_ratio(ParamHandle *handle, float aspx, float aspy); + +void param_face_add(ParamHandle *handle, + ParamKey key, + int nverts, + ParamKey *vkeys, + float **co, + float **uv, + ParamBool *pin, + ParamBool *select); + +void param_edge_set_seam(ParamHandle *handle, + ParamKey *vkeys); + +void param_construct_end(ParamHandle *handle, ParamBool fill, ParamBool impl); +void param_delete(ParamHandle *chart); + +/* Least Squares Conformal Maps: + ----------------------------- + - charts with less than two pinned vertices are assigned 2 pins + - lscm is divided in three steps: + - begin: compute matrix and it's factorization (expensive) + - solve using pinned coordinates (cheap) + - end: clean up + - uv coordinates are allowed to change within begin/end, for + quick re-solving +*/ + +void param_lscm_begin(ParamHandle *handle, ParamBool live, ParamBool abf); +void param_lscm_solve(ParamHandle *handle); +void param_lscm_end(ParamHandle *handle); + +/* Stretch */ + +void param_stretch_begin(ParamHandle *handle); +void param_stretch_blend(ParamHandle *handle, float blend); +void param_stretch_iter(ParamHandle *handle); +void param_stretch_end(ParamHandle *handle); + +/* Area Smooth */ + +void param_smooth_area(ParamHandle *handle); + +/* Packing */ + +void param_pack(ParamHandle *handle); + +/* Average area for all charts */ + +void param_average(ParamHandle *handle); + +/* Simple x,y scale */ + +void param_scale(ParamHandle *handle, float x, float y); + +/* Flushing */ + +void param_flush(ParamHandle *handle); +void param_flush_restore(ParamHandle *handle); + + +#ifdef __cplusplus +} +#endif + +#endif /*__PARAMETRIZER_H__*/ + diff --git a/source/blender/editors/uvedit/uvedit_unwrap_ops.c b/source/blender/editors/uvedit/uvedit_unwrap_ops.c new file mode 100644 index 00000000000..0fe907677f3 --- /dev/null +++ b/source/blender/editors/uvedit/uvedit_unwrap_ops.c @@ -0,0 +1,383 @@ +/** + * $Id$ + * + * ***** BEGIN GPL LICENSE BLOCK ***** + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software Foundation, + * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. + * + * The Original Code is Copyright (C) 2001-2002 by NaN Holding BV. + * All rights reserved. + * + * The Original Code is: all of this file. + * + * Contributor(s): none yet. + * + * ***** END GPL LICENSE BLOCK ***** + */ + +#include <string.h> +#include <stdlib.h> +#include <math.h> + +#include "MEM_guardedalloc.h" + +#include "DNA_mesh_types.h" +#include "DNA_meshdata_types.h" +#include "DNA_object_types.h" +#include "DNA_scene_types.h" +#include "DNA_screen_types.h" +#include "DNA_space_types.h" + +#include "BKE_customdata.h" +#include "BKE_depsgraph.h" +#include "BKE_global.h" +#include "BKE_mesh.h" +#include "BKE_utildefines.h" + +#include "BLI_arithb.h" +#include "BLI_edgehash.h" +#include "BLI_editVert.h" + +#include "PIL_time.h" + +#include "ED_mesh.h" + +#include "uvedit_intern.h" +#include "uvedit_parametrizer.h" + +/* Parametrizer */ +ParamHandle *construct_param_handle(Scene *scene, EditMesh *em, short implicit, short fill, short sel) +{ + ParamHandle *handle; + EditFace *efa; + EditEdge *eed; + EditVert *ev; + MTFace *tf; + int a; + + handle = param_construct_begin(); + + if ((scene->toolsettings->uvcalc_flag & UVCALC_NO_ASPECT_CORRECT)==0) { + efa = EM_get_actFace(em, 1); + + if (efa) { + float aspx = 1.0f, aspy= 1.0f; + // XXX MTFace *tf = CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + // XXX image_final_aspect(tf->tpage, &aspx, &aspy); + + if (aspx!=aspy) + param_aspect_ratio(handle, aspx, aspy); + } + } + + /* we need the vert indicies */ + for (ev= em->verts.first, a=0; ev; ev= ev->next, a++) + ev->tmp.l = a; + + for (efa= em->faces.first; efa; efa= efa->next) { + ParamKey key, vkeys[4]; + ParamBool pin[4], select[4]; + float *co[4]; + float *uv[4]; + int nverts; + + if ((efa->h) || (sel && (efa->f & SELECT)==0)) + continue; + + tf= (MTFace *)CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + + if (implicit && + !( uvedit_uv_selected(scene, efa, tf, 0) || + uvedit_uv_selected(scene, efa, tf, 1) || + uvedit_uv_selected(scene, efa, tf, 2) || + (efa->v4 && uvedit_uv_selected(scene, efa, tf, 3)) ) + ) { + continue; + } + + key = (ParamKey)efa; + vkeys[0] = (ParamKey)efa->v1->tmp.l; + vkeys[1] = (ParamKey)efa->v2->tmp.l; + vkeys[2] = (ParamKey)efa->v3->tmp.l; + + co[0] = efa->v1->co; + co[1] = efa->v2->co; + co[2] = efa->v3->co; + + uv[0] = tf->uv[0]; + uv[1] = tf->uv[1]; + uv[2] = tf->uv[2]; + + pin[0] = ((tf->unwrap & TF_PIN1) != 0); + pin[1] = ((tf->unwrap & TF_PIN2) != 0); + pin[2] = ((tf->unwrap & TF_PIN3) != 0); + + select[0] = ((uvedit_uv_selected(scene, efa, tf, 0)) != 0); + select[1] = ((uvedit_uv_selected(scene, efa, tf, 1)) != 0); + select[2] = ((uvedit_uv_selected(scene, efa, tf, 2)) != 0); + + if (efa->v4) { + vkeys[3] = (ParamKey)efa->v4->tmp.l; + co[3] = efa->v4->co; + uv[3] = tf->uv[3]; + pin[3] = ((tf->unwrap & TF_PIN4) != 0); + select[3] = (uvedit_uv_selected(scene, efa, tf, 3) != 0); + nverts = 4; + } + else + nverts = 3; + + param_face_add(handle, key, nverts, vkeys, co, uv, pin, select); + } + + if (!implicit) { + for (eed= em->edges.first; eed; eed= eed->next) { + if(eed->seam) { + ParamKey vkeys[2]; + vkeys[0] = (ParamKey)eed->v1->tmp.l; + vkeys[1] = (ParamKey)eed->v2->tmp.l; + param_edge_set_seam(handle, vkeys); + } + } + } + + param_construct_end(handle, fill, implicit); + + return handle; +} + +void unwrap_lscm(Scene *scene, Object *obedit, short seamcut) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + ParamHandle *handle; + short abf = scene->toolsettings->unwrapper == 1; + short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES; + + /* add uvs if there not here */ + if (!uvedit_test(obedit)) { +#if 0 + if (em && em->faces.first) + EM_add_data_layer(&em->fdata, CD_MTFACE); + + if (!uvedit_test(obedit)) + return; + + if (G.sima && G.sima->image) /* this is a bit of a kludge, but assume they want the image on their mesh when UVs are added */ + image_changed(G.sima, G.sima->image); + + /* select new UV's */ + if ((G.sima==0 || G.sima->flag & SI_SYNC_UVSEL)==0) { + EditFace *efa; + MTFace *tf; + for(efa=em->faces.first; efa; efa=efa->next) { + tf= (MTFace *)CustomData_em_get(&em->fdata, efa->data, CD_MTFACE); + simaFaceSel_Set(efa, tf); + } + } +#endif + } + + handle = construct_param_handle(scene, em, 0, fillholes, seamcut == 0); + + param_lscm_begin(handle, PARAM_FALSE, abf); + param_lscm_solve(handle); + param_lscm_end(handle); + + param_pack(handle); + + param_flush(handle); + + param_delete(handle); + + if (!seamcut) + ; // XXX BIF_undo_push("UV unwrap"); + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + // XXX allqueue(REDRAWVIEW3D, 0); + // XXX allqueue(REDRAWIMAGE, 0); +} + +void minimize_stretch_tface_uv(Scene *scene, Object *obedit) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + ParamHandle *handle; + double lasttime; + short doit = 1, escape = 0, val, blend = 0; + unsigned short event = 0; + short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES; + + if(!uvedit_test(obedit)) return; + + handle = construct_param_handle(scene, em, 1, fillholes, 1); + + lasttime = PIL_check_seconds_timer(); + + param_stretch_begin(handle); + + while (doit) { + param_stretch_iter(handle); + + while (0) { // XXX qtest()) { + event= 0; // XXX extern_qread(&val); + + if (val) { +#if 0 + switch (event) { + case ESCKEY: + escape = 1; + case RETKEY: + case PADENTER: + doit = 0; + break; + case PADPLUSKEY: + case WHEELUPMOUSE: + if (blend < 10) { + blend++; + param_stretch_blend(handle, blend*0.1f); + param_flush(handle); + lasttime = 0.0f; + } + break; + case PADMINUS: + case WHEELDOWNMOUSE: + if (blend > 0) { + blend--; + param_stretch_blend(handle, blend*0.1f); + param_flush(handle); + lasttime = 0.0f; + } + break; + } +#endif + } + else if (0) { // XXX (event == LEFTMOUSE) || (event == RIGHTMOUSE)) { + escape = 0; // XXX (event == RIGHTMOUSE); + doit = 0; + } + } + + if (!doit) + break; + + if (PIL_check_seconds_timer() - lasttime > 0.5) { + char str[100]; + + param_flush(handle); + + sprintf(str, "Stretch minimize. Blend %.2f.", blend*0.1f); + // XXX headerprint(str); + + lasttime = PIL_check_seconds_timer(); + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + // XXX if(G.sima->lock) force_draw_plus(SPACE_VIEW3D, 0); + // XXX else force_draw(0); + } + } + + if (escape) + param_flush_restore(handle); + else + param_flush(handle); + + param_stretch_end(handle); + + param_delete(handle); + + // XXX BIF_undo_push("UV stretch minimize"); + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + // XXX allqueue(REDRAWVIEW3D, 0); + // XXX allqueue(REDRAWIMAGE, 0); +} + +void pack_charts_tface_uv(Scene *scene, Object *obedit) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + ParamHandle *handle; + + if(!uvedit_test(obedit)) return; + + handle = construct_param_handle(scene, em, 1, 0, 1); + param_pack(handle); + param_flush(handle); + param_delete(handle); + + // XXX BIF_undo_push("UV pack islands"); + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + // XXX allqueue(REDRAWVIEW3D, 0); + // XXX allqueue(REDRAWIMAGE, 0); +} + + +void average_charts_tface_uv(Scene *scene, Object *obedit) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + ParamHandle *handle; + + if(!uvedit_test(obedit)) return; + + handle = construct_param_handle(scene, em, 1, 0, 1); + param_average(handle); + param_flush(handle); + param_delete(handle); + + // XXX BIF_undo_push("UV average island scale"); + + DAG_object_flush_update(scene, obedit, OB_RECALC_DATA); + + // XXX allqueue(REDRAWVIEW3D, 0); + // XXX allqueue(REDRAWIMAGE, 0); +} + +/* LSCM live mode */ + +static ParamHandle *liveHandle = NULL; + +void unwrap_lscm_live_begin(Scene *scene, Object *obedit) +{ + EditMesh *em= ((Mesh*)obedit->data)->edit_mesh; + short abf = scene->toolsettings->unwrapper == 1; + short fillholes = scene->toolsettings->uvcalc_flag & UVCALC_FILLHOLES; + + if(!uvedit_test(obedit)) return; + + liveHandle = construct_param_handle(scene, em, 0, fillholes, 1); + + param_lscm_begin(liveHandle, PARAM_TRUE, abf); +} + +void unwrap_lscm_live_re_solve(void) +{ + if (liveHandle) { + param_lscm_solve(liveHandle); + param_flush(liveHandle); + } +} + +void unwrap_lscm_live_end(short cancel) +{ + if (liveHandle) { + param_lscm_end(liveHandle); + if (cancel) + param_flush_restore(liveHandle); + param_delete(liveHandle); + liveHandle = NULL; + } +} + |