Welcome to mirror list, hosted at ThFree Co, Russian Federation.

git.blender.org/blender.git - Unnamed repository; edit this file 'description' to name the repository.
summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--source/blender/blenkernel/BKE_context.h5
-rw-r--r--source/blender/blenkernel/intern/context.c10
-rw-r--r--source/blender/editors/Makefile2
-rw-r--r--source/blender/editors/SConscript3
-rw-r--r--source/blender/editors/include/ED_armature.h2
-rw-r--r--source/blender/editors/include/ED_mesh.h21
-rw-r--r--source/blender/editors/include/ED_object.h4
-rw-r--r--source/blender/editors/include/ED_screen.h1
-rw-r--r--source/blender/editors/include/ED_uvedit.h45
-rw-r--r--source/blender/editors/mesh/editface.c2
-rw-r--r--source/blender/editors/mesh/editmesh_lib.c6
-rw-r--r--source/blender/editors/mesh/mesh_intern.h5
-rw-r--r--source/blender/editors/screen/screen_ops.c18
-rw-r--r--source/blender/editors/space_api/spacetypes.c8
-rw-r--r--source/blender/editors/space_image/Makefile4
-rw-r--r--source/blender/editors/space_image/SConscript1
-rw-r--r--source/blender/editors/space_image/image_draw.c722
-rw-r--r--source/blender/editors/space_image/image_header.c1230
-rw-r--r--source/blender/editors/space_image/image_intern.h38
-rw-r--r--source/blender/editors/space_image/image_ops.c1178
-rw-r--r--source/blender/editors/space_image/image_panels.c631
-rw-r--r--source/blender/editors/space_image/image_render.c335
-rw-r--r--source/blender/editors/space_image/space_image.c387
-rw-r--r--source/blender/editors/uvedit/Makefile54
-rw-r--r--source/blender/editors/uvedit/SConscript10
-rw-r--r--source/blender/editors/uvedit/uvedit_draw.c808
-rw-r--r--source/blender/editors/uvedit/uvedit_intern.h72
-rw-r--r--source/blender/editors/uvedit/uvedit_ops.c2401
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.c4481
-rw-r--r--source/blender/editors/uvedit/uvedit_parametrizer.h97
-rw-r--r--source/blender/editors/uvedit/uvedit_unwrap_ops.c383
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;
+ }
+}
+