[gegl/npd-squashed] libs, operations: Add n-point deformation
- From: Michael Henning <mhenning src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [gegl/npd-squashed] libs, operations: Add n-point deformation
- Date: Sun, 17 Nov 2013 20:43:55 +0000 (UTC)
commit 7bd297888509d38889424393caaf0a31e2e2ca15
Author: Marek Dvoroznak <dvoromar gmail com>
Date: Wed Jun 26 23:32:22 2013 +0200
libs, operations: Add n-point deformation
.gitignore | 1 +
Makefile.am | 1 +
configure.ac | 9 +
gegl.pc.in | 2 +-
libs/npd/.gitignore | 6 +
libs/npd/Makefile.am | 40 +++
libs/npd/deformation.c | 207 +++++++++++++
libs/npd/deformation.h | 48 +++
libs/npd/graphics.c | 556 +++++++++++++++++++++++++++++++++++
libs/npd/graphics.h | 128 ++++++++
libs/npd/lattice_cut.c | 278 ++++++++++++++++++
libs/npd/lattice_cut.h | 40 +++
libs/npd/npd.h | 32 ++
libs/npd/npd_common.c | 619 +++++++++++++++++++++++++++++++++++++++
libs/npd/npd_common.h | 157 ++++++++++
libs/npd/npd_gegl.c | 68 +++++
libs/npd/npd_gegl.h | 33 ++
libs/npd/npd_math.c | 47 +++
libs/npd/npd_math.h | 58 ++++
operations/external/Makefile.am | 8 +
operations/external/npd.c | 329 +++++++++++++++++++++
po/POTFILES.in | 1 +
22 files changed, 2667 insertions(+), 1 deletions(-)
---
diff --git a/.gitignore b/.gitignore
index 69c9757..d411137 100644
--- a/.gitignore
+++ b/.gitignore
@@ -47,3 +47,4 @@
cscope.files
cscope.out
/*.project
+/nbproject
diff --git a/Makefile.am b/Makefile.am
index 31f43be..588a324 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -7,6 +7,7 @@ SUBDIRS=\
libs \
opencl \
gegl \
+ libs/npd \
seamless-clone \
operations \
bin \
diff --git a/configure.ac b/configure.ac
index f567090..a6f780b 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1074,6 +1074,14 @@ if test "x$have_p2tc" != "xyes"; then
AC_SUBST(P2TC_LIBS, "-L\$(top_builddir)/libs/poly2tri-c/poly2tri-c -lpoly2tri-c")
fi
+##################
+# Check for libnpd
+##################
+have_libnpd="yes (internal)"
+
+AC_SUBST(NPD_CFLAGS, "-I\$(top_srcdir)/libs")
+AC_SUBST(NPD_LIBS, "-L\$(top_builddir)/libs/npd -lgegl-npd-\$(GEGL_API_VERSION)")
+
#######################
# Check for other items
#######################
@@ -1139,6 +1147,7 @@ gegl/property-types/Makefile
gegl/opencl/Makefile
libs/Makefile
libs/rgbe/Makefile
+libs/npd/Makefile
libs/poly2tri-c/Makefile
libs/poly2tri-c/poly2tri-c/Makefile
libs/poly2tri-c/poly2tri-c/render/Makefile
diff --git a/gegl.pc.in b/gegl.pc.in
index 614bb78..f4b70fc 100644
--- a/gegl.pc.in
+++ b/gegl.pc.in
@@ -8,5 +8,5 @@ Name: GEGL
Description: Generic Graphics Library
Version: @GEGL_REAL_VERSION@
Requires: @GLIB_PACKAGES@ babl
-Libs: -L${libdir} -l PACKAGE_NAME@- GEGL_API_VERSION@
+Libs: -L${libdir} -l PACKAGE_NAME@- GEGL_API_VERSION@ -l PACKAGE_NAME@-npd- GEGL_API_VERSION@
Cflags: -I${includedir}/@PACKAGE_NAME -@GEGL_API_VERSION@
diff --git a/libs/npd/.gitignore b/libs/npd/.gitignore
new file mode 100644
index 0000000..4e0b01c
--- /dev/null
+++ b/libs/npd/.gitignore
@@ -0,0 +1,6 @@
+/Makefile
+/Makefile.in
+/.deps/
+/.libs/
+/*.lo
+/*.la
\ No newline at end of file
diff --git a/libs/npd/Makefile.am b/libs/npd/Makefile.am
new file mode 100644
index 0000000..080bb9d
--- /dev/null
+++ b/libs/npd/Makefile.am
@@ -0,0 +1,40 @@
+include $(top_srcdir)/operations/Makefile-common.am
+
+######################################################
+# A shared library for n-point image deformation API #
+######################################################
+
+GEGL_NPD_publicdir = $(includedir)/gegl-$(GEGL_API_VERSION)/npd
+
+GEGL_NPD_public_HEADERS = \
+ npd_common.h \
+ deformation.h \
+ npd_math.h \
+ graphics.h \
+ lattice_cut.h \
+ npd_gegl.h \
+ npd.h
+
+GEGL_NPD_SOURCES = \
+ $(GEGL_NPD_public_HEADERS) \
+ npd_common.c \
+ deformation.c \
+ npd_math.c \
+ graphics.c \
+ lattice_cut.c \
+ npd_gegl.c
+
+libgegl_npd_ GEGL_API_VERSION@_la_SOURCES = \
+ $(GEGL_NPD_public_HEADERS) \
+ $(GEGL_NPD_SOURCES)
+
+libgegl_npd_ GEGL_API_VERSION@_la_LDFLAGS = \
+ -avoid-version -export-dynamic $(no_undefined)
+
+libgegl_npd_ GEGL_API_VERSION@_la_CFLAGS = \
+ $(AM_CFLAGS)
+
+lib_LTLIBRARIES = libgegl-npd- GEGL_API_VERSION@.la
+
+libgegl_npd_ GEGL_API_VERSION@_la_LIBADD = \
+ $(libgegl)
diff --git a/libs/npd/deformation.c b/libs/npd/deformation.c
new file mode 100644
index 0000000..87f4362
--- /dev/null
+++ b/libs/npd/deformation.c
@@ -0,0 +1,207 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#include "deformation.h"
+#include <math.h>
+
+#define NPD_COMPUTE_CENTROID(suffix, arg1, arg2, WEIGHTS, accessor) \
+void \
+npd_compute_centroid_##suffix (gint num_of_points, \
+ arg1, \
+ arg2, \
+ NPDPoint *centroid) \
+{ \
+ gfloat x_sum = 0, y_sum = 0, weights_sum = 0; \
+ gint i; \
+ \
+ /* first compute sum of all values for each coordinate */ \
+ for (i = 0; i < num_of_points; ++i) \
+ { \
+ x_sum += (WEIGHTS) * points[i] accessor x; \
+ y_sum += (WEIGHTS) * points[i] accessor y; \
+ weights_sum += (WEIGHTS); \
+ } \
+ \
+ /* then compute mean */ \
+ centroid->x = x_sum / weights_sum; \
+ centroid->y = y_sum / weights_sum; \
+}
+
+NPD_COMPUTE_CENTROID (of_overlapping_points,
+ NPDPoint *points[],
+ gfloat weights[],
+ 1,
+ ->)
+NPD_COMPUTE_CENTROID (from_weighted_points,
+ NPDPoint points[],
+ gfloat weights[],
+ weights[i],
+ .)
+#undef NPD_COMPUTE_CENTROID
+
+void
+npd_compute_ARSAP_transformation (gint num_of_points,
+ NPDPoint reference_points[],
+ NPDPoint current_points[],
+ gfloat weights[],
+ gboolean ASAP)
+{
+ NPDPoint pc = {0, 0}, qc = {0, 0};
+ gfloat a = 0, b = 0, mu_part = 0, mu, r1, r2, x0, y0;
+ gint i;
+
+ /* p - points of reference pose */
+ npd_compute_centroid_from_weighted_points (num_of_points,
+ reference_points,
+ weights,
+ &pc);
+ /* q - points of current pose */
+ npd_compute_centroid_from_weighted_points (num_of_points,
+ current_points,
+ weights,
+ &qc);
+
+ /* get rotation */
+ for (i = 0; i < num_of_points; ++i)
+ {
+ gfloat px_minus_pcx = reference_points[i].x - pc.x;
+ gfloat py_minus_pcy = reference_points[i].y - pc.y;
+ gfloat qx_minus_qcx = current_points[i].x - qc.x;
+ gfloat qy_minus_qcy = current_points[i].y - qc.y;
+
+ a += weights[i]
+ * ((px_minus_pcx)
+ * (qx_minus_qcx)
+ + (py_minus_pcy)
+ * (qy_minus_qcy));
+ b += weights[i]
+ * ((px_minus_pcx)
+ * (qy_minus_qcy)
+ - (py_minus_pcy)
+ * (qx_minus_qcx));
+
+ mu_part += weights[i]
+ * ((px_minus_pcx)
+ * (px_minus_pcx)
+ + (py_minus_pcy)
+ * (py_minus_pcy));
+ }
+
+ mu = 1;
+ if (ASAP) mu = mu_part;
+ else mu = sqrt(a * a + b * b);
+
+ r1 = a / mu;
+ r2 = -b / mu;
+
+ /* get translation */
+ x0 = qc.x - ( r1 * pc.x + r2 * pc.y);
+ y0 = qc.y - (-r2 * pc.x + r1 * pc.y);
+
+ /* transform points */
+ for (i = 0; i < num_of_points; ++i)
+ {
+ if (!current_points[i].fixed)
+ {
+ current_points[i].x = r1 * reference_points[i].x
+ + r2 * reference_points[i].y + x0;
+ current_points[i].y = -r2 * reference_points[i].x
+ + r1 * reference_points[i].y + y0;
+ }
+ }
+}
+
+void
+npd_compute_ARSAP_transformations (NPDHiddenModel *hidden_model)
+{
+ gint i;
+
+ for (i = 0; i < hidden_model->num_of_bones; ++i)
+ {
+ NPDBone *reference_bones = &hidden_model->reference_bones[i];
+ NPDBone *current_bones = &hidden_model->current_bones[i];
+ npd_compute_ARSAP_transformation (reference_bones->num_of_points,
+ reference_bones->points,
+ current_bones->points,
+ current_bones->weights,
+ hidden_model->ASAP);
+ }
+}
+
+void
+npd_deform_model (NPDModel *model,
+ gint rigidity)
+{
+ gint i;
+ for (i = 0; i < rigidity; ++i)
+ {
+ npd_deform_model_once (model);
+ }
+}
+
+void
+npd_deform_model_once (NPDModel *model)
+{
+ gint i, j;
+
+ /* updates associated overlapping points according to this control point */
+ for (i = 0; i < model->control_points->len; ++i)
+ {
+ NPDControlPoint *cp = &g_array_index (model->control_points,
+ NPDControlPoint,
+ i);
+
+ for (j = 0; j < cp->overlapping_points->num_of_points; ++j)
+ {
+ npd_set_point_coordinates (cp->overlapping_points->points[j],
+ &cp->point);
+ }
+ }
+
+ npd_deform_hidden_model_once (model->hidden_model);
+}
+
+void
+npd_deform_hidden_model_once (NPDHiddenModel *hidden_model)
+{
+ gint i, j;
+ npd_compute_ARSAP_transformations (hidden_model);
+
+ /* overlapping points are not overlapping after the deformation,
+ so we have to move them to their centroid */
+ for (i = 0; i < hidden_model->num_of_overlapping_points; ++i)
+ {
+ NPDOverlappingPoints *list_of_ops
+ = &hidden_model->list_of_overlapping_points[i];
+ NPDPoint centroid;
+
+ npd_compute_centroid_of_overlapping_points (list_of_ops->num_of_points,
+ list_of_ops->points,
+ NULL,
+ ¢roid);
+
+ for (j = 0; j < list_of_ops->num_of_points; ++j)
+ {
+ list_of_ops->points[j]->x = centroid.x;
+ list_of_ops->points[j]->y = centroid.y;
+ }
+ }
+}
diff --git a/libs/npd/deformation.h b/libs/npd/deformation.h
new file mode 100644
index 0000000..c363e1d
--- /dev/null
+++ b/libs/npd/deformation.h
@@ -0,0 +1,48 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#ifndef __NPD_DEFORMATION_H__
+#define __NPD_DEFORMATION_H__
+
+#include "npd_common.h"
+
+void npd_compute_centroid_from_weighted_points
+ (gint num_of_points,
+ NPDPoint points[],
+ gfloat weights[],
+ NPDPoint *centroid);
+void npd_compute_centroid_of_overlapping_points
+ (gint num_of_points,
+ NPDPoint *points[],
+ gfloat weights[],
+ NPDPoint *centroid);
+void npd_compute_ARSAP_transformation (gint num_of_points,
+ NPDPoint reference_shape[],
+ NPDPoint current_shape[],
+ gfloat weights[],
+ gboolean ASAP);
+void npd_compute_ARSAP_transformations (NPDHiddenModel *model);
+void npd_deform_model (NPDModel *model,
+ gint rigidity);
+void npd_deform_model_once (NPDModel *model);
+void npd_deform_hidden_model_once (NPDHiddenModel *hidden_model);
+
+#endif /* __NPD_DEFORMATION_H__ */
diff --git a/libs/npd/graphics.c b/libs/npd/graphics.c
new file mode 100644
index 0000000..e5c5e5f
--- /dev/null
+++ b/libs/npd/graphics.c
@@ -0,0 +1,556 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#include "graphics.h"
+#include "glib.h"
+#include <math.h>
+#include "npd_math.h"
+#include "lattice_cut.h"
+#include <stdio.h>
+#include <string.h>
+
+void
+npd_create_mesh_from_image (NPDModel *model,
+ gint width,
+ gint height,
+ gint position_x,
+ gint position_y)
+{
+ gint square_size = model->mesh_square_size;
+ NPDImage *image = model->reference_image;
+ gint i, cy, cx, y, x, r, c, ow, oh;
+ NPDColor pixel_color = { 0, 0, 0, 0 };
+ GArray *squares;
+ gint *sq2id;
+ gboolean *empty_squares;
+ GList *tmp_ops = NULL;
+ GArray *ops = g_array_new (FALSE, FALSE, sizeof (NPDOverlappingPoints));
+ GList **edges;
+ NPDHiddenModel *hm = model->hidden_model;
+
+ /* create squares above the image */
+ width = ceil ((gfloat) width / square_size);
+ height = ceil ((gfloat) height / square_size);
+
+ squares = g_array_new (FALSE, FALSE, sizeof (NPDBone));
+ empty_squares = g_new0 (gboolean, width * height);
+ sq2id = g_new0 (gint, width * height);
+
+ i = 0;
+ for (cy = 0; cy < height; cy++)
+ for (cx = 0; cx < width; cx++)
+ {
+ gboolean is_empty = TRUE;
+
+ for (y = cy * square_size; y < (cy + 1) * square_size; y++)
+ for (x = cx * square_size; x < (cx + 1) * square_size; x++)
+ {
+ /* test of emptiness */
+ npd_get_pixel_color (image, x, y, &pixel_color);
+ if (!npd_is_color_transparent (&pixel_color))
+ {
+ is_empty = FALSE;
+ goto not_empty;
+ }
+ }
+
+#define NPD_SQ_ID cy * width + cx
+
+ not_empty:
+ if (!is_empty)
+ {
+ /* create a square */
+ NPDBone square;
+ npd_create_square (&square,
+ position_x + cx * square_size,
+ position_y + cy * square_size,
+ square_size, square_size);
+ g_array_append_val (squares, square);
+ sq2id[NPD_SQ_ID] = i;
+ i++;
+ }
+ else
+ empty_squares[NPD_SQ_ID] = TRUE;
+ }
+
+ /* find empty edges */
+ edges = npd_find_edges (model->reference_image, width, height, square_size);
+
+ /* create provisional overlapping points */
+#define NPD_ADD_P(op,r,c,point) \
+ if ((r) > -1 && (r) < (oh - 1) && (c) > -1 && (c) < (ow - 1) && \
+ edges[op] == NULL && !empty_squares[(r) * width + (c)]) \
+ { \
+ tmp_ops = g_list_append (tmp_ops, GINT_TO_POINTER ((r) * width + (c)));\
+ tmp_ops = g_list_append (tmp_ops, GINT_TO_POINTER (point)); \
+ num++; \
+ }
+
+ ow = width + 1; oh = height + 1;
+
+ for (r = 0; r < oh; r++)
+ for (c = 0; c < ow; c++)
+ {
+ gint index = r * ow + c, num = 0;
+ NPD_ADD_P (index, r - 1, c - 1, 2);
+ NPD_ADD_P (index, r - 1, c, 3);
+ NPD_ADD_P (index, r, c, 0);
+ NPD_ADD_P (index, r, c - 1, 1);
+ if (num > 0)
+ tmp_ops = g_list_insert_before (tmp_ops,
+ g_list_nth_prev (g_list_last (tmp_ops), 2 * num - 1),
+ GINT_TO_POINTER (num));
+ }
+#undef NPD_ADD_P
+
+ /* cut lattice's edges and continue with creating of provisional overlapping points */
+ tmp_ops = g_list_concat (tmp_ops, npd_cut_edges (edges, ow, oh));
+ for (i = 0; i < ow * oh; i++) g_list_free (edges[i]);
+ g_free (edges);
+
+ /* create model's bones */
+ hm->num_of_bones = squares->len;
+ hm->current_bones = (NPDBone*) (gpointer) squares->data;
+ g_array_free (squares, FALSE);
+
+ /* create model's overlapping points */
+ while (g_list_next (tmp_ops))
+ {
+ GPtrArray *ppts = g_ptr_array_new ();
+ gint count = GPOINTER_TO_INT (tmp_ops->data);
+ gint j = 0;
+
+ for (i = 0; i < count; i++)
+ {
+ gint sq, p;
+ tmp_ops = g_list_next (tmp_ops);
+ sq = GPOINTER_TO_INT (tmp_ops->data);
+ tmp_ops = g_list_next (tmp_ops);
+ p = GPOINTER_TO_INT (tmp_ops->data);
+
+ if (!empty_squares[sq])
+ {
+ g_ptr_array_add (ppts, &hm->current_bones[sq2id[sq]].points[p]);
+ j++;
+ }
+ }
+
+ if (j > 0)
+ {
+ NPDOverlappingPoints op;
+ op.num_of_points = j;
+ op.points = (NPDPoint**) ppts->pdata;
+ g_ptr_array_free (ppts, FALSE);
+ op.representative = op.points[0];
+ g_array_append_val (ops, op);
+ }
+ tmp_ops = g_list_next (tmp_ops);
+ }
+
+ g_list_free (tmp_ops);
+ g_free (empty_squares);
+ g_free (sq2id);
+
+ hm->num_of_overlapping_points = ops->len;
+ hm->list_of_overlapping_points = (NPDOverlappingPoints*) (gpointer) ops->data;
+ g_array_free (ops, FALSE);
+
+ /* create reference bones according to current bones */
+ hm->reference_bones = g_new (NPDBone, hm->num_of_bones);
+ for (i = 0; i < hm->num_of_bones; i++)
+ {
+ NPDBone *current_bone = &hm->current_bones[i];
+ NPDBone *reference_bone = &hm->reference_bones[i];
+ NPDPoint *current_points, *ref_points;
+ gint j, n = current_bone->num_of_points;
+
+ reference_bone->num_of_points = n;
+ reference_bone->points = g_new (NPDPoint, n);
+ memcpy (reference_bone->points, current_bone->points, n * sizeof (NPDPoint));
+ reference_bone->weights = current_bone->weights;
+
+ current_points = current_bone->points;
+ ref_points = reference_bone->points;
+
+ for (j = 0; j < n; j++)
+ {
+ current_points[j].current_bone = current_bone;
+ current_points[j].reference_bone = reference_bone;
+ ref_points[j].current_bone = current_bone;
+ ref_points[j].reference_bone = reference_bone;
+ ref_points[j].x = current_points[j].x - position_x;
+ ref_points[j].y = current_points[j].y - position_y;
+ current_points[j].counterpart = &ref_points[j];
+ ref_points[j].counterpart = ¤t_points[j];
+ }
+ }
+
+/*
+// could be useful later
+ gint j;
+ for (i = 0; i < hm->num_of_overlapping_points; i++)
+ for (j = 0; j < hm->list_of_overlapping_points[i].num_of_points; j++)
+ {
+ NPDPoint *p = hm->list_of_overlapping_points[i].points[j];
+ p->overlapping_points = &hm->list_of_overlapping_points[i];
+ p->counterpart->overlapping_points = &hm->list_of_overlapping_points[i];
+ }
+*/
+}
+
+void
+npd_draw_mesh (NPDModel *model,
+ NPDDisplay *display)
+{
+ NPDHiddenModel *hm = model->hidden_model;
+ gint i, j;
+
+ for (i = 0; i < hm->num_of_bones; i++)
+ {
+ NPDBone *bone = &hm->current_bones[i];
+ NPDPoint *first = &bone->points[0];
+ NPDPoint *p0, *p1 = NULL;
+
+ for (j = 1; j < bone->num_of_points; j++)
+ {
+ p0 = &bone->points[j - 1];
+ p1 = &bone->points[j];
+ npd_draw_line (display, p0->x, p0->y, p1->x, p1->y);
+ }
+ npd_draw_line (display, p1->x, p1->y, first->x, first->y);
+ }
+}
+
+gfloat
+npd_bilinear_interpolation (gfloat I0,
+ gfloat I1,
+ gfloat I2,
+ gfloat I3,
+ gfloat dx,
+ gfloat dy)
+{
+ return (I0 * (1 - dx) + I1 * dx) * (1 - dy) +
+ (I2 * (1 - dx) + I3 * dx) * dy;
+}
+
+void
+npd_bilinear_color_interpolation (NPDColor *I0,
+ NPDColor *I1,
+ NPDColor *I2,
+ NPDColor *I3,
+ gfloat dx,
+ gfloat dy,
+ NPDColor *out)
+{
+ out->r = npd_bilinear_interpolation (I0->r, I1->r, I2->r, I3->r, dx, dy);
+ out->g = npd_bilinear_interpolation (I0->g, I1->g, I2->g, I3->g, dx, dy);
+ out->b = npd_bilinear_interpolation (I0->b, I1->b, I2->b, I3->b, dx, dy);
+ out->a = npd_bilinear_interpolation (I0->a, I1->a, I2->a, I3->a, dx, dy);
+}
+
+gfloat
+npd_blend_band (gfloat src,
+ gfloat dst,
+ gfloat src_alpha,
+ gfloat dst_alpha,
+ gfloat out_alpha_recip)
+{
+ return (src * src_alpha +
+ dst * dst_alpha * (1 - src_alpha)) * out_alpha_recip;
+}
+
+void
+npd_blend_colors (NPDColor *src,
+ NPDColor *dst,
+ NPDColor *out_color)
+{
+#ifdef NPD_RGBA_FLOAT
+ gfloat src_A = src->a,
+ dst_A = dst->a;
+#else
+ gfloat src_A = src->a / 255.0,
+ dst_A = dst->a / 255.0;
+#endif
+ gfloat out_alpha = src_A + dst_A * (1 - src_A);
+ gfloat out_alpha_recip = 1 / out_alpha;
+
+ out_color->r = npd_blend_band (src->r, dst->r, src_A, dst_A, out_alpha_recip);
+ out_color->g = npd_blend_band (src->g, dst->g, src_A, dst_A, out_alpha_recip);
+ out_color->b = npd_blend_band (src->b, dst->b, src_A, dst_A, out_alpha_recip);
+#ifdef NPD_RGBA_FLOAT
+ out_color->a = out_alpha;
+#else
+ out_color->a = out_alpha * 255;
+#endif
+}
+
+void
+npd_texture_fill_triangle (gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gint x3,
+ gint y3,
+ NPDMatrix *A,
+ NPDImage *input_image,
+ NPDImage *output_image,
+ NPDSettings settings)
+{
+ gint yA, yB, yC, xA, xB, xC;
+ gint tmp, y;
+ gint deltaXAB, deltaYAB;
+ gint deltaXBC, deltaYBC;
+ gint deltaXAC, deltaYAC;
+
+ gfloat slopeAB;
+ gfloat slopeBC;
+ gfloat slopeAC;
+
+ gfloat k, l;
+ gfloat slope1, slope2, slope3, slope4;
+
+ if (y1 == y2 && x1 > x2)
+ {
+ tmp = y1; y1 = y2; y2 = tmp;
+ tmp = x1; x1 = x2; x2 = tmp;
+ }
+ if (y1 == y3 && x1 > x3)
+ {
+ tmp = y1; y1 = y3; y3 = tmp;
+ tmp = x1; x1 = x3; x3 = tmp;
+ }
+ if (y2 == y3 && x2 > x3)
+ {
+ tmp = y2; y2 = y3; y3 = tmp;
+ tmp = x2; x2 = x3; x3 = tmp;
+ }
+
+ if (y1 <= y2)
+ {
+ if (y2 <= y3)
+ {
+ /* y1 <= y2 <= y3 */
+ yA = y1; yB = y2; yC = y3;
+ xA = x1; xB = x2; xC = x3;
+ }
+ else if (y1 <= y3)
+ {
+ /* y1 <= y3 < y2 */
+ yA = y1; yB = y3; yC = y2;
+ xA = x1; xB = x3; xC = x2;
+ }
+ else
+ {
+ /* y3 < y1 < y2 */
+ yA = y3; yB = y1; yC = y2;
+ xA = x3; xB = x1; xC = x2;
+ }
+ }
+ else
+ {
+ if (y1 <= y3)
+ {
+ /* y2 < y1 <= y3 */
+ yA = y2; yB = y1; yC = y3;
+ xA = x2; xB = x1; xC = x3;
+ }
+ else if (y2 <= y3)
+ {
+ /* y2 <= y3 < y1 */
+ yA = y2; yB = y3; yC = y1;
+ xA = x2; xB = x3; xC = x1;
+ }
+ else
+ {
+ /* y3 < y2 < y1 */
+ yA = y3; yB = y2; yC = y1;
+ xA = x3; xB = x2; xC = x1;
+ }
+ }
+
+ deltaXAB = xB - xA, deltaYAB = yB - yA;
+ deltaXBC = xC - xB, deltaYBC = yC - yB;
+ deltaXAC = xC - xA, deltaYAC = yC - yA;
+
+ slopeBC = (deltaYBC == 0 ? 0 : (gfloat) deltaXBC / deltaYBC);
+ slopeAC = (deltaYAC == 0 ? 0 : (gfloat) deltaXAC / deltaYAC);
+
+ if (deltaYAB == 0)
+ {
+ slopeAB = 0;
+ k = xA;
+ l = xB;
+
+ slope1 = slopeAB;
+ slope2 = slopeAC;
+ slope3 = slopeAC;
+ slope4 = slopeBC;
+ }
+ else
+ {
+ slopeAB = (gfloat) deltaXAB / deltaYAB;
+ k = xA;
+ l = xA;
+
+ if (slopeAB > slopeAC)
+ {
+ slope1 = slopeAC;
+ slope2 = slopeAB;
+ slope3 = slopeAC;
+ slope4 = slopeBC;
+ }
+ else
+ {
+ slope1 = slopeAB;
+ slope2 = slopeAC;
+ slope3 = slopeBC;
+ slope4 = slopeAC;
+ }
+ }
+
+ for (y = yA; y < yB; y++)
+ {
+ npd_draw_texture_line ((gint) round (k), (gint) round (l),
+ y, A,
+ input_image, output_image,
+ settings);
+ k += slope1;
+ l += slope2;
+ }
+
+ for (y = yB; y <= yC; y++)
+ {
+ npd_draw_texture_line ((gint) round (k), (gint) round (l),
+ y, A,
+ input_image, output_image,
+ settings);
+ k += slope3;
+ l += slope4;
+ }
+}
+
+void
+npd_texture_quadrilateral (NPDBone *reference_bone,
+ NPDBone *current_bone,
+ NPDImage *input_image,
+ NPDImage *output_image,
+ NPDSettings settings)
+{
+ /* p1 are points of domain, p2 are points of codomain */
+ NPDPoint *p1 = current_bone->points;
+ NPDPoint *p2 = reference_bone->points;
+
+ NPDMatrix *A = NULL;
+ npd_new_matrix (&A);
+
+ npd_compute_affinity (&p1[0], &p1[1], &p1[2],
+ &p2[0], &p2[1], &p2[2], A);
+ npd_texture_fill_triangle ((gint) p1[0].x, (gint) p1[0].y,
+ (gint) p1[1].x, (gint) p1[1].y,
+ (gint) p1[2].x, (gint) p1[2].y,
+ A, input_image, output_image,
+ settings);
+
+ npd_compute_affinity (&p1[0], &p1[2], &p1[3],
+ &p2[0], &p2[2], &p2[3], A);
+ npd_texture_fill_triangle ((gint) p1[0].x, (gint) p1[0].y,
+ (gint) p1[2].x, (gint) p1[2].y,
+ (gint) p1[3].x, (gint) p1[3].y,
+ A, input_image, output_image,
+ settings);
+
+ npd_destroy_matrix (&A);
+}
+
+void
+npd_draw_texture_line (gint x1,
+ gint x2,
+ gint y,
+ NPDMatrix *A,
+ NPDImage *input_image,
+ NPDImage *output_image,
+ NPDSettings settings)
+{
+ gint x, fx, fy;
+ gfloat dx, dy;
+
+ for (x = x1; x <= x2; x++)
+ {
+ NPDPoint p, q;
+ NPDColor I0, interpolated, *final;
+
+ q.x = x; q.y = y;
+ npd_apply_transformation (A, &q, &p);
+
+ fx = floor (p.x);
+ fy = floor (p.y);
+
+ npd_get_pixel_color (input_image, fx, fy, &I0);
+ final = &I0;
+
+ /* bilinear interpolation */
+ if (settings & NPD_BILINEAR_INTERPOLATION)
+ {
+ NPDColor I1, I2, I3;
+
+ dx = p.x - fx;
+ dy = p.y - fy;
+
+ npd_get_pixel_color (input_image, fx + 1, fy, &I1);
+ npd_get_pixel_color (input_image, fx, fy + 1, &I2);
+ npd_get_pixel_color (input_image, fx + 1, fy + 1, &I3);
+ npd_bilinear_color_interpolation (&I0, &I1, &I2, &I3, dx, dy, &interpolated);
+ final = &interpolated;
+ }
+
+ /* alpha blending */
+ if (settings & NPD_ALPHA_BLENDING)
+ {
+ NPDColor dest;
+ npd_get_pixel_color (output_image, x, y, &dest);
+ npd_blend_colors (final, &dest, final);
+ }
+
+ npd_set_pixel_color (output_image, x, y, final);
+ }
+}
+
+gboolean
+npd_compare_colors (NPDColor *c1,
+ NPDColor *c2)
+{
+ if (npd_equal_floats (c1->r, c2->r) &&
+ npd_equal_floats (c1->g, c2->g) &&
+ npd_equal_floats (c1->b, c2->b) &&
+ npd_equal_floats (c1->a, c2->a))
+ return TRUE;
+
+ return FALSE;
+}
+
+gboolean
+npd_is_color_transparent (NPDColor *color)
+{
+ if (npd_equal_floats (color->a, 0.0))
+ return TRUE;
+
+ return FALSE;
+}
diff --git a/libs/npd/graphics.h b/libs/npd/graphics.h
new file mode 100644
index 0000000..db174fc
--- /dev/null
+++ b/libs/npd/graphics.h
@@ -0,0 +1,128 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#ifndef __NPD_GRAPHICS_H__
+#define __NPD_GRAPHICS_H__
+
+#include "npd_common.h"
+
+//#define NPD_RGBA_FLOAT
+
+struct _NPDColor {
+#ifdef NPD_RGBA_FLOAT
+ gfloat r;
+ gfloat g;
+ gfloat b;
+ gfloat a;
+#else
+ guint8 r;
+ guint8 g;
+ guint8 b;
+ guint8 a;
+#endif
+};
+
+typedef enum
+{
+ NPD_BILINEAR_INTERPOLATION = 1,
+ NPD_ALPHA_BLENDING = 1 << 1
+} NPDSettings;
+
+void npd_create_model_from_image (NPDModel *model,
+ NPDImage *image,
+ gint square_size);
+void npd_create_mesh_from_image (NPDModel *model,
+ gint width,
+ gint height,
+ gint position_x,
+ gint position_y);
+void npd_draw_model (NPDModel *model,
+ NPDDisplay *display);
+void npd_draw_mesh (NPDModel *model,
+ NPDDisplay *display);
+
+gboolean npd_load_image (NPDImage *image,
+ const char *path);
+void npd_destroy_image (NPDImage *image);
+//void npd_draw_image (NPDImage *image);
+void npd_texture_fill_triangle (gint x1,
+ gint y1,
+ gint x2,
+ gint y2,
+ gint x3,
+ gint y3,
+ NPDMatrix *A,
+ NPDImage *input_image,
+ NPDImage *output_image,
+ NPDSettings settings);
+void npd_texture_quadrilateral (NPDBone *reference_bone,
+ NPDBone *current_bone,
+ NPDImage *input_image,
+ NPDImage *output_image,
+ NPDSettings settings);
+void npd_draw_texture_line (gint x1,
+ gint x2,
+ gint y,
+ NPDMatrix *A,
+ NPDImage *input_image,
+ NPDImage *output_image,
+ NPDSettings settings);
+void (*npd_draw_line) (NPDDisplay *display,
+ gfloat x0,
+ gfloat y0,
+ gfloat x1,
+ gfloat y1);
+gfloat npd_bilinear_interpolation (gfloat I0,
+ gfloat I1,
+ gfloat I2,
+ gfloat I3,
+ gfloat dx,
+ gfloat dy);
+void npd_bilinear_color_interpolation (NPDColor *I0,
+ NPDColor *I1,
+ NPDColor *I2,
+ NPDColor *I3,
+ gfloat dx,
+ gfloat dy,
+ NPDColor *out);
+gfloat npd_blend_band (gfloat src,
+ gfloat dst,
+ gfloat src_alpha,
+ gfloat dest_alpha,
+ gfloat out_alpha);
+void npd_blend_colors (NPDColor *src,
+ NPDColor *dst,
+ NPDColor *out_color);
+void (*npd_get_pixel_color) (NPDImage *image,
+ gint x,
+ gint y,
+ NPDColor *color);
+void (*npd_set_pixel_color) (NPDImage *image,
+ gint x,
+ gint y,
+ NPDColor *color);
+gboolean npd_compare_colors (NPDColor *c1,
+ NPDColor *c2);
+gboolean npd_is_color_transparent (NPDColor *color);
+gboolean npd_init_display (NPDDisplay *display);
+void npd_destroy_display (NPDDisplay *display);
+
+#endif /*__NPD_GRAPHICS_H__ */
diff --git a/libs/npd/lattice_cut.c b/libs/npd/lattice_cut.c
new file mode 100644
index 0000000..d8e7f72
--- /dev/null
+++ b/libs/npd/lattice_cut.c
@@ -0,0 +1,278 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#include "lattice_cut.h"
+#include "npd_common.h"
+#include "graphics.h"
+#include <glib.h>
+#include "npd_math.h"
+
+#define NPD_SWAP_INTS(i,j) { gint tmp = i; i = j; j = tmp; }
+
+/* only works for straight lines */
+gboolean
+npd_is_edge_empty (NPDImage *image,
+ gint X1,
+ gint Y1,
+ gint X2,
+ gint Y2)
+{
+ gint x, y;
+ NPDColor color;
+
+ if (Y1 > Y2) NPD_SWAP_INTS (Y1, Y2);
+ if (X1 > X2) NPD_SWAP_INTS (X1, X2);
+
+ for (y = Y1; y <= Y2; y++)
+ for (x = X1; x <= X2; x++)
+ {
+ npd_get_pixel_color (image, x, y, &color);
+ if (!npd_is_color_transparent (&color))
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+GList**
+npd_find_edges (NPDImage *image,
+ gint count_x,
+ gint count_y,
+ gint square_size)
+{
+ gint i, j;
+ gint ow = count_x + 1,
+ oh = count_y + 1;
+ GList **empty_edges = g_new0 (GList*, ow * oh);
+
+ for (j = 1; j <= count_y; j++)
+ for (i = 1; i <= count_x; i++)
+ {
+#define NPD_TEST_EMPTY(from_op_x,from_op_y,to_op_x,to_op_y) \
+ if (npd_is_edge_empty (image, \
+ (from_op_x) * square_size, (from_op_y) * square_size, \
+ (to_op_x) * square_size, (to_op_y) * square_size)) \
+ { \
+ gint from_op_id = (from_op_y) * ow + (from_op_x), \
+ to_op_id = (to_op_y) * ow + (to_op_x); \
+ empty_edges[from_op_id] = g_list_append (empty_edges[from_op_id], \
+ GINT_TO_POINTER (to_op_id)); \
+ empty_edges[to_op_id] = g_list_append (empty_edges[to_op_id], \
+ GINT_TO_POINTER (from_op_id)); \
+ }
+
+ if (j != count_y) NPD_TEST_EMPTY (i, j, i - 1, j);
+ if (i != count_x) NPD_TEST_EMPTY (i, j, i, j - 1);
+#undef NPD_TEST_EMPTY
+ }
+
+ return empty_edges;
+}
+
+GList*
+npd_cut_edges (GList **edges,
+ gint ow,
+ gint oh)
+{
+ gint i, r, col, width = ow - 1;
+ GList *ops = NULL;
+ gint neighbors[4];
+
+ for (r = 0; r < oh; r++)
+ for (col = 0; col < ow; col++)
+ {
+ gint index = r * ow + col;
+ GList *op = edges[index];
+ gint num_of_neighbors = g_list_length (op);
+
+ if (num_of_neighbors == 0) continue;
+
+#define NPD_ADD_COUNT(count) ops = g_list_append (ops, GINT_TO_POINTER (count))
+#define NPD_ADD_P(r,col,point) \
+ if ((r) > -1 && (r) < (oh - 1) && (col) > -1 && (col) < (ow - 1)) \
+ { \
+ ops = g_list_append (ops, GINT_TO_POINTER ((r) * width + (col))); \
+ ops = g_list_append (ops, GINT_TO_POINTER (point)); \
+ }
+
+ for (i = 0; i < num_of_neighbors; i++)
+ neighbors[i] = GPOINTER_TO_INT (g_list_nth_data (op, i));
+
+ if (num_of_neighbors == 1)
+ {
+ gboolean border = FALSE;
+ if (r == 0 || col == 0 || r == (oh - 1) || col == (ow - 1))
+ border = TRUE;
+
+ if (border) NPD_ADD_COUNT (1);
+ else NPD_ADD_COUNT (4);
+ NPD_ADD_P (r - 1, col - 1, 2);
+ NPD_ADD_P (r - 1, col, 3);
+ NPD_ADD_P (r, col, 0);
+ NPD_ADD_P (r, col - 1, 1);
+ if (border)
+ ops = g_list_insert_before (ops,
+ g_list_nth_prev (g_list_last (ops), 1),
+ GINT_TO_POINTER (1));
+#undef NPD_ADD_P
+ }
+ else
+ if (num_of_neighbors == 2)
+ {
+ gboolean x_differs = FALSE, y_differs = FALSE;
+ gint a, b, c, d;
+
+#define NPD_OP_X(op) ((op) % ow)
+#define NPD_OP_Y(op) ((op) / ow)
+
+ if (NPD_OP_X (neighbors[0]) != NPD_OP_X (neighbors[1])) x_differs = TRUE;
+ if (NPD_OP_Y (neighbors[0]) != NPD_OP_Y (neighbors[1])) y_differs = TRUE;
+
+ if (x_differs && y_differs)
+ {
+ /* corner */
+ gint B = neighbors[0], C = neighbors[1];
+
+ if (NPD_OP_X (index) == NPD_OP_X (neighbors[0]))
+ { B = neighbors[1]; C = neighbors[0]; }
+
+ if (NPD_OP_Y (index) < NPD_OP_Y (C))
+ {
+ if (NPD_OP_X (index) < NPD_OP_X (B))
+ { /* IV. quadrant */ a = 2; b = 3; c = 1; d = 0; }
+ else
+ { /* III. quadrant */ a = 2; b = 3; c = 0; d = 1; }
+ }
+ else
+ {
+ if (NPD_OP_X (index) < NPD_OP_X (B))
+ { /* I. quadrant */ a = 2; b = 0; c = 1; d = 3; }
+ else
+ { /* II. quadrant */ a = 0; b = 3; c = 1; d = 2; }
+ }
+
+#define NPD_OP2SQ(op) (op == 0 ? ( r * width + col) : \
+ (op == 1 ? ( r * width + col - 1) : \
+ (op == 2 ? ((r - 1) * width + col - 1) : \
+ ((r - 1) * width + col))))
+#define NPD_ADD_P(square,point) \
+ ops = g_list_append (ops, GINT_TO_POINTER (square)); \
+ ops = g_list_append (ops, GINT_TO_POINTER (point));
+
+ NPD_ADD_COUNT (3);
+ NPD_ADD_P (NPD_OP2SQ (a), a);
+ NPD_ADD_P (NPD_OP2SQ (b), b);
+ NPD_ADD_P (NPD_OP2SQ (c), c);
+ NPD_ADD_COUNT (1);
+ NPD_ADD_P (NPD_OP2SQ (d), d);
+ }
+ else
+ {
+ /* segment */
+ a = 0; b = 1; c = 2; d = 3;
+ if (y_differs) { a = 1; b = 2; c = 0; d = 3; }
+
+ NPD_ADD_COUNT (2);
+ NPD_ADD_P (NPD_OP2SQ (a), a);
+ NPD_ADD_P (NPD_OP2SQ (b), b);
+ NPD_ADD_COUNT (2);
+ NPD_ADD_P (NPD_OP2SQ (c), c);
+ NPD_ADD_P (NPD_OP2SQ (d), d);
+ }
+ }
+ else
+ if (num_of_neighbors == 3)
+ {
+ gint B = neighbors[0], C = neighbors[1], D = neighbors[2];
+ gint a = 2, b = 1, c = 3, d = 0;
+
+ if ((NPD_OP_X (B) != NPD_OP_X (C)) && (NPD_OP_Y (B) != NPD_OP_Y (C)))
+ {
+ /* B and C form corner */
+ B = neighbors[2]; D = neighbors[0]; /* swap B and D */
+
+ if ((NPD_OP_X (B) != NPD_OP_X (C)) && (NPD_OP_Y (B) != NPD_OP_Y (C)))
+ {
+ /* (new) B and C form corner */
+ C = neighbors[0]; D = neighbors[1]; /* swap C and D */
+ }
+ }
+
+ /* B and C form segment */
+ if (NPD_OP_X (B) == NPD_OP_X (C))
+ {
+ if (NPD_OP_X (B) < NPD_OP_X (D))
+ {
+ /* |_
+ | */
+ a = 2; b = 1; c = 3; d = 0;
+ }
+ else
+ {
+ /* _|
+ | */
+ a = 3; b = 0; c = 2; d = 1;
+ }
+ }
+ else if (NPD_OP_Y (B) == NPD_OP_Y (C))
+ {
+ if (NPD_OP_Y (B) < NPD_OP_Y (D))
+ {
+ /* _ _
+ | */
+ a = 2; b = 3; c = 1; d = 0;
+ }
+ else
+ {
+ /* _|_ */
+ a = 1; b = 0; c = 2; d = 3;
+ }
+ }
+
+ NPD_ADD_COUNT (2);
+ NPD_ADD_P (NPD_OP2SQ (a), a);
+ NPD_ADD_P (NPD_OP2SQ (b), b);
+ NPD_ADD_COUNT (1);
+ NPD_ADD_P (NPD_OP2SQ (c), c);
+ NPD_ADD_COUNT (1);
+ NPD_ADD_P (NPD_OP2SQ (d), d);
+ }
+ else
+ if (num_of_neighbors == 4)
+ {
+ NPD_ADD_COUNT (1);
+ NPD_ADD_P (NPD_OP2SQ (0), 0);
+ NPD_ADD_COUNT (1);
+ NPD_ADD_P (NPD_OP2SQ (1), 1);
+ NPD_ADD_COUNT (1);
+ NPD_ADD_P (NPD_OP2SQ (2), 2);
+ NPD_ADD_COUNT (1);
+ NPD_ADD_P (NPD_OP2SQ (3), 3);
+ }
+ }
+#undef NPD_ADD_P
+#undef NPD_OP2SQ
+#undef NPD_OP_X
+#undef NPD_OP_Y
+#undef NPD_ADD_COUNT
+
+ return ops;
+}
diff --git a/libs/npd/lattice_cut.h b/libs/npd/lattice_cut.h
new file mode 100644
index 0000000..b331ba3
--- /dev/null
+++ b/libs/npd/lattice_cut.h
@@ -0,0 +1,40 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#ifndef __REFINE_H__
+#define __REFINE_H__
+
+#include "npd_common.h"
+
+gboolean npd_is_edge_empty (NPDImage *image,
+ gint X1,
+ gint Y1,
+ gint X2,
+ gint Y2);
+GList** npd_find_edges (NPDImage *image,
+ gint count_x,
+ gint count_y,
+ gint square_size);
+GList* npd_cut_edges (GList **edges,
+ gint ow,
+ gint oh);
+
+#endif /* __REFINE_H__ */
diff --git a/libs/npd/npd.h b/libs/npd/npd.h
new file mode 100644
index 0000000..654c1ce
--- /dev/null
+++ b/libs/npd/npd.h
@@ -0,0 +1,32 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#ifndef __NPD_H__
+#define __NPD_H__
+
+#include "npd_common.h"
+#include "graphics.h"
+#include "deformation.h"
+#include "npd_math.h"
+#include "lattice_cut.h"
+
+#endif /* __NPD_H__ */
+
diff --git a/libs/npd/npd_common.c b/libs/npd/npd_common.c
new file mode 100644
index 0000000..1b427b9
--- /dev/null
+++ b/libs/npd/npd_common.c
@@ -0,0 +1,619 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#include "npd_common.h"
+#include "npd_math.h"
+#include <math.h>
+#include <glib.h>
+#include <glib/gprintf.h>
+
+gint npd_int_sort_function_descending (gconstpointer a,
+ gconstpointer b);
+
+void
+npd_init_model (NPDModel *model)
+{
+ NPDHiddenModel *hidden_model;
+ GArray *control_points;
+
+ /* init hidden model */
+ hidden_model = g_new (NPDHiddenModel, 1);
+ model->hidden_model = hidden_model;
+ hidden_model->ASAP = FALSE;
+ hidden_model->MLS_weights = FALSE;
+ hidden_model->MLS_weights_alpha = 1;
+ hidden_model->num_of_bones = 0;
+ hidden_model->num_of_overlapping_points = 0;
+
+ /* init control points */
+ control_points = g_array_new (FALSE, FALSE, sizeof (NPDControlPoint));
+ model->control_points = control_points;
+ model->control_point_radius = 6;
+ model->control_points_visible = TRUE;
+
+ /* init other */
+ model->mesh_visible = TRUE;
+ model->texture_visible = TRUE;
+}
+
+void
+npd_destroy_hidden_model (NPDHiddenModel *hm)
+{
+ gint i;
+ for (i = 0; i < hm->num_of_overlapping_points; i++)
+ {
+ g_free (hm->list_of_overlapping_points[i].points);
+ }
+
+ g_free (hm->list_of_overlapping_points);
+
+ for (i = 0; i < hm->num_of_bones; i++)
+ {
+ g_free (hm->current_bones[i].weights);
+ g_free (hm->current_bones[i].points);
+ g_free (hm->reference_bones[i].points);
+ }
+
+ g_free (hm->current_bones);
+ g_free (hm->reference_bones);
+}
+
+void
+npd_destroy_model (NPDModel *model)
+{
+ /* destroy control points */
+ g_array_free (model->control_points, TRUE);
+
+ /* destroy hidden model */
+ npd_destroy_hidden_model (model->hidden_model);
+ g_free (model->hidden_model);
+}
+
+/**
+ * Finds nearest (to specified position) overlapping points, creates a new
+ * control point at the position of overlapping points and assigns them to the
+ * control point.
+ *
+ * @param model
+ * @param coord specified position
+ * @return pointer to a newly created control point or NULL when there already
+ * is a control point at the position of nearest overlapping points
+ */
+NPDControlPoint*
+npd_add_control_point (NPDModel *model,
+ NPDPoint *coord)
+{
+ gint num_of_ops, i, closest;
+ gfloat min, current;
+ NPDOverlappingPoints *list_of_ops;
+ NPDControlPoint cp;
+ NPDPoint *closest_point;
+
+ list_of_ops = model->hidden_model->list_of_overlapping_points;
+ num_of_ops = model->hidden_model->num_of_overlapping_points;
+
+ /* find closest overlapping points */
+ closest = 0;
+ min = npd_SED (coord, list_of_ops[0].representative);
+
+ for (i = 1; i < num_of_ops; i++)
+ {
+ NPDPoint *op = list_of_ops[i].representative;
+ current = npd_SED(coord, op);
+
+ if (min > current)
+ {
+ closest = i;
+ min = current;
+ }
+ }
+
+ closest_point = list_of_ops[closest].representative;
+
+ /* we want to create a new control point only when there isn't any
+ * control point associated to the closest overlapping points - i.e. we
+ * don't want to have two (or more) different control points manipulating
+ * one overlapping points */
+ if (!npd_get_control_point_at (model, closest_point))
+ {
+ cp.point.weight = closest_point->weight;
+ cp.overlapping_points = &list_of_ops[closest];
+
+ npd_set_point_coordinates (&cp.point, closest_point);
+ g_array_append_val (model->control_points, cp);
+
+ if (model->hidden_model->MLS_weights)
+ npd_compute_MLS_weights (model);
+
+ return &g_array_index (model->control_points,
+ NPDControlPoint,
+ model->control_points->len - 1);
+ }
+ else
+ return NULL;
+}
+
+/**
+ * Beware, when you use this function on previously stored pointers to control
+ * points it needn't to work properly, because g_array_remove_index can
+ * preserve pointers but change (move) data.
+ * In this situation use npd_remove_control_points instead.
+ */
+void
+npd_remove_control_point (NPDModel *model,
+ NPDControlPoint *control_point)
+{
+ gint i;
+ NPDControlPoint *cp;
+
+ for (i = 0; i < model->control_points->len; i++)
+ {
+ cp = &g_array_index (model->control_points, NPDControlPoint, i);
+
+ if (cp == control_point)
+ {
+ npd_set_control_point_weight (cp, 1.0);
+ g_array_remove_index (model->control_points, i);
+
+ if (model->hidden_model->MLS_weights)
+ npd_compute_MLS_weights (model);
+
+ return;
+ }
+ }
+}
+
+gint
+npd_int_sort_function_descending (gconstpointer a,
+ gconstpointer b)
+{
+ return GPOINTER_TO_INT (b) - GPOINTER_TO_INT (a);
+}
+
+void
+npd_remove_control_points (NPDModel *model,
+ GList *control_points)
+{
+ gint i;
+ NPDControlPoint *cp;
+ GList *indices = NULL;
+
+ /* first we find indices of control points we want to remove */
+ while (control_points != NULL)
+ {
+ for (i = 0; i < model->control_points->len; i++)
+ {
+ cp = &g_array_index (model->control_points, NPDControlPoint, i);
+ if (cp == control_points->data)
+ {
+ npd_set_control_point_weight (cp, 1.0);
+ indices = g_list_insert_sorted (indices,
+ GINT_TO_POINTER (i),
+ npd_int_sort_function_descending);
+ }
+ }
+
+ control_points = g_list_next (control_points);
+ }
+
+ /* indices are sorted in descending order, so we can simply iterate over them
+ * and remove corresponding control points one by one */
+ while (indices != NULL)
+ {
+ g_array_remove_index (model->control_points, GPOINTER_TO_INT (indices->data));
+ indices = g_list_next (indices);
+ }
+
+ if (model->hidden_model->MLS_weights)
+ npd_compute_MLS_weights (model);
+
+ g_list_free (indices);
+}
+
+void
+npd_remove_all_control_points (NPDModel *model)
+{
+ g_array_remove_range (model->control_points,
+ 0,
+ model->control_points->len);
+}
+
+void
+npd_set_control_point_weight (NPDControlPoint *cp,
+ gfloat weight)
+{
+ npd_set_overlapping_points_weight(cp->overlapping_points, weight);
+}
+
+gboolean
+npd_equal_coordinates (NPDPoint *p1,
+ NPDPoint *p2)
+{
+ return npd_equal_coordinates_epsilon(p1, p2, NPD_EPSILON);
+}
+
+gboolean
+npd_equal_coordinates_epsilon (NPDPoint *p1,
+ NPDPoint *p2,
+ gfloat epsilon)
+{
+ if (npd_equal_floats_epsilon (p1->x, p2->x, epsilon) &&
+ npd_equal_floats_epsilon (p1->y, p2->y, epsilon))
+ {
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
+NPDControlPoint*
+npd_get_control_point_with_radius_at (NPDModel *model,
+ NPDPoint *coord,
+ gfloat radius)
+{
+ gint i;
+ for (i = 0; i < model->control_points->len; i++)
+ {
+ NPDControlPoint *cp = &g_array_index (model->control_points,
+ NPDControlPoint,
+ i);
+ if (npd_equal_coordinates_epsilon (&cp->point,
+ coord,
+ radius))
+ {
+ return cp;
+ }
+ }
+
+ return NULL;
+}
+
+NPDControlPoint*
+npd_get_control_point_at (NPDModel *model,
+ NPDPoint *coord)
+{
+ return npd_get_control_point_with_radius_at (model,
+ coord,
+ model->control_point_radius);
+}
+
+void
+npd_create_square (NPDBone *square,
+ gint x,
+ gint y,
+ gint width,
+ gint height)
+{
+ gint i;
+ square->num_of_points = 4;
+ square->points = g_new (NPDPoint, 4);
+ square->weights = g_new (gfloat, 4);
+
+ square->points[0].x = x; square->points[0].y = y;
+ square->points[1].x = x + width; square->points[1].y = y;
+ square->points[2].x = x + width; square->points[2].y = y + height;
+ square->points[3].x = x; square->points[3].y = y + height;
+
+ for (i = 0; i < 4; i++)
+ {
+ square->weights[i] = 1.0;
+ square->points[i].weight = &square->weights[i];
+ square->points[i].fixed = FALSE;
+ square->points[i].index = i;
+ }
+}
+
+void
+npd_create_list_of_overlapping_points (NPDHiddenModel *hm)
+{
+ gint i, j, num_of_bones;
+ NPDBone *bone;
+ NPDPoint *point;
+ GPtrArray *list_of_ops;
+ GHashTable *coords_to_cluster;
+
+ list_of_ops = g_ptr_array_new ();
+ num_of_bones = hm->num_of_bones;
+ coords_to_cluster = g_hash_table_new_full
+ (g_str_hash, g_str_equal,
+ g_free, (GDestroyNotify) g_hash_table_destroy);
+
+ for (i = 0; i < num_of_bones; i++)
+ {
+ bone = &hm->current_bones[i];
+
+ for (j = 0; j < bone->num_of_points; j++)
+ {
+ point = &bone->points[j];
+ add_point_to_suitable_cluster (coords_to_cluster,
+ point,
+ list_of_ops);
+ }
+ }
+
+ hm->list_of_overlapping_points = g_new (NPDOverlappingPoints,
+ list_of_ops->len);
+ hm->num_of_overlapping_points = list_of_ops->len;
+
+ for (i = 0; i < list_of_ops->len; i++)
+ {
+ GPtrArray *op = g_ptr_array_index (list_of_ops, i);
+ hm->list_of_overlapping_points[i].points = (NPDPoint**) op->pdata;
+ hm->list_of_overlapping_points[i].num_of_points = op->len;
+ hm->list_of_overlapping_points[i].representative =
+ hm->list_of_overlapping_points[i].points[0];
+
+ for (j = 0; j < op->len; j++)
+ {
+ NPDPoint *p = hm->list_of_overlapping_points[i].points[j];
+ p->overlapping_points = &hm->list_of_overlapping_points[i];
+ p->counterpart->overlapping_points = &hm->list_of_overlapping_points[i];
+ }
+
+ g_ptr_array_free (op, FALSE); /* we want to preserve the underlying
+ array */
+ }
+
+ /* free allocated memory */
+ g_hash_table_destroy (coords_to_cluster);
+ g_ptr_array_free (list_of_ops, TRUE);
+}
+
+#define NPD_FLOAT_TO_STRING(name_of_string, value) \
+/* must be freed */ \
+name_of_string = g_new (gchar, 10); \
+g_ascii_dtostr (name_of_string, 10, value);
+
+void
+add_point_to_suitable_cluster (GHashTable *coords_to_cluster,
+ NPDPoint *point,
+ GPtrArray *list_of_overlapping_points)
+{
+ gchar *str_coord_x, *str_coord_y;
+ GHashTable *coord_y;
+ GPtrArray *op;
+
+ NPD_FLOAT_TO_STRING (str_coord_x, point->x);
+ NPD_FLOAT_TO_STRING (str_coord_y, point->y);
+
+ coord_y = g_hash_table_lookup (coords_to_cluster, str_coord_x);
+
+ if (coord_y == NULL)
+ {
+ /* coordinate doesn't exist */
+ coord_y = g_hash_table_new_full (g_str_hash, /* is freed during */
+ g_str_equal, /* destroying */
+ g_free, /* coords_to_cluster */
+ NULL); /* hash table */
+ g_hash_table_insert (coords_to_cluster, str_coord_x, coord_y);
+ }
+
+ op = g_hash_table_lookup (coord_y, str_coord_y);
+ if (op == NULL)
+ {
+ op = g_ptr_array_new ();
+ g_hash_table_insert (coord_y, str_coord_y, op);
+ g_ptr_array_add (list_of_overlapping_points, op);
+ }
+
+ g_ptr_array_add (op, point);
+}
+
+void
+npd_set_overlapping_points_weight (NPDOverlappingPoints *op,
+ gfloat weight)
+{
+ gint i;
+ for (i = 0; i < op->num_of_points; i++)
+ {
+ (*op->points[i]->weight) = weight;
+ }
+}
+
+void
+npd_set_point_coordinates (NPDPoint *target,
+ NPDPoint *source)
+{
+ target->x = source->x;
+ target->y = source->y;
+}
+
+/**
+ * Sets type of deformation. The function doesn't perform anything if supplied
+ * deformation type doesn't differ from currently set one.
+ *
+ * @param model
+ * @param ASAP TRUE = ASAP deformation, FALSE = ARAP deformation
+ * @param MLS_weights use weights from Moving Least Squares deformation method
+ */
+void
+npd_set_deformation_type (NPDModel *model,
+ gboolean ASAP,
+ gboolean MLS_weights)
+{
+ NPDHiddenModel *hm = model->hidden_model;
+
+ if (hm->ASAP == ASAP && hm->MLS_weights == MLS_weights) return;
+
+ if (MLS_weights)
+ npd_compute_MLS_weights (model);
+ else if (hm->MLS_weights)
+ npd_reset_weights (hm);
+
+ hm->ASAP = ASAP;
+ hm->MLS_weights = MLS_weights;
+}
+
+void
+npd_compute_MLS_weights (NPDModel *model)
+{
+ NPDHiddenModel *hm = model->hidden_model;
+ NPDControlPoint *cp;
+ NPDOverlappingPoints *op;
+ NPDPoint *cp_reference, *op_reference;
+ gfloat min, SED, MLS_weight;
+ gint i, j;
+
+ if (model->control_points->len == 0)
+ {
+ npd_reset_weights (hm);
+ return;
+ }
+
+ for (i = 0; i < hm->num_of_overlapping_points; i++)
+ {
+ op = &hm->list_of_overlapping_points[i];
+ op_reference = op->representative->counterpart;
+ min = INFINITY;
+
+ for (j = 0; j < model->control_points->len; j++)
+ {
+ cp = &g_array_index (model->control_points,
+ NPDControlPoint,
+ j);
+ cp_reference = cp->overlapping_points->representative->counterpart;
+
+ /* TODO - use geodetic distance */
+ SED = npd_SED (cp_reference,
+ op_reference);
+ if (SED < min) min = SED;
+ }
+
+ if (npd_equal_floats (min, 0.0)) min = 0.0000001;
+ MLS_weight = 1 / pow (min, hm->MLS_weights_alpha);
+ npd_set_overlapping_points_weight (op, MLS_weight);
+ }
+}
+
+void
+npd_reset_weights (NPDHiddenModel *hm)
+{
+ NPDOverlappingPoints *op;
+ gint i;
+
+ for (i = 0; i < hm->num_of_overlapping_points; i++)
+ {
+ op = &hm->list_of_overlapping_points[i];
+ npd_set_overlapping_points_weight (op, 1.0);
+ }
+}
+
+void
+npd_print_model (NPDModel *model,
+ gboolean print_control_points)
+{
+ gint i;
+ g_printf ("NPDModel:\n");
+ g_printf ("control point radius: %f\n", model->control_point_radius);
+ g_printf ("control points visible: %d\n", model->control_points_visible);
+ g_printf ("mesh visible: %d\n", model->mesh_visible);
+ g_printf ("texture visible: %d\n", model->texture_visible);
+ g_printf ("mesh square size: %d\n", model->mesh_square_size);
+
+ npd_print_hidden_model (model->hidden_model, FALSE, FALSE);
+
+ if (print_control_points)
+ {
+ g_printf ("%d control points:\n", model->control_points->len);
+ for (i = 0; i < model->control_points->len; i++)
+ {
+ NPDControlPoint *cp = &g_array_index (model->control_points,
+ NPDControlPoint,
+ i);
+ npd_print_point (&cp->point, TRUE);
+ }
+ }
+}
+
+void
+npd_print_hidden_model (NPDHiddenModel *hm,
+ gboolean print_bones,
+ gboolean print_overlapping_points)
+{
+ gint i;
+ g_printf ("NPDHiddenModel:\n");
+ g_printf ("number of bones: %d\n", hm->num_of_bones);
+ g_printf ("ASAP: %d\n", hm->ASAP);
+ g_printf ("MLS weights: %d\n", hm->MLS_weights);
+ g_printf ("number of overlapping points: %d\n", hm->num_of_overlapping_points);
+
+ if (print_bones)
+ {
+ g_printf ("bones:\n");
+ for (i = 0; i < hm->num_of_bones; i++)
+ {
+ npd_print_bone (&hm->current_bones[i]);
+ }
+ }
+
+ if (print_overlapping_points)
+ {
+ g_printf ("overlapping points:\n");
+ for (i = 0; i < hm->num_of_overlapping_points; i++)
+ {
+ npd_print_overlapping_points (&hm->list_of_overlapping_points[i]);
+ }
+ }
+}
+
+void
+npd_print_bone (NPDBone *bone)
+{
+ gint i;
+ g_printf ("NPDBone:\n");
+ g_printf ("number of points: %d\n", bone->num_of_points);
+ g_printf ("points:\n");
+ for (i = 0; i < bone->num_of_points; i++)
+ {
+ npd_print_point (&bone->points[i], TRUE);
+ }
+}
+
+void
+npd_print_point (NPDPoint *point,
+ gboolean print_details)
+{
+ if (print_details)
+ {
+ g_printf ("(NPDPoint: x: %f, y: %f, weight: %f, fixed: %d)\n",
+ point->x, point->y, *point->weight, point->fixed);
+ }
+ else
+ {
+ g_printf ("(NPDPoint: x: %f, y: %f)\n",
+ point->x, point->y);
+ }
+}
+
+void
+npd_print_overlapping_points (NPDOverlappingPoints *op)
+{
+ gint i;
+ g_printf ("NPDOverlappingPoints:\n");
+ g_printf ("number of points: %d\n", op->num_of_points);
+ g_printf ("representative: ");
+ npd_print_point (op->representative, TRUE);
+ g_printf ("points:\n");
+ for (i = 0; i < op->num_of_points; i++)
+ {
+ npd_print_point (op->points[i], TRUE);
+ }
+}
diff --git a/libs/npd/npd_common.h b/libs/npd/npd_common.h
new file mode 100644
index 0000000..b3be82e
--- /dev/null
+++ b/libs/npd/npd_common.h
@@ -0,0 +1,157 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#ifndef __NPD_COMMON_H__
+#define __NPD_COMMON_H__
+
+#include <glib.h>
+
+/* opaque types for independency on used display library */
+typedef struct _NPDImage NPDImage;
+typedef struct _NPDColor NPDColor;
+typedef struct _NPDDisplay NPDDisplay;
+typedef struct _NPDMatrix NPDMatrix;
+
+typedef struct _NPDPoint NPDPoint;
+typedef struct _NPDBone NPDBone;
+typedef struct _NPDOverlappingPoints NPDOverlappingPoints;
+
+struct _NPDPoint
+{
+ gfloat x;
+ gfloat y;
+ gboolean fixed;
+ gfloat *weight; /* pointer to weight in array of weights */
+ gint index;
+ NPDBone *current_bone;
+ NPDBone *reference_bone;
+ NPDPoint *counterpart;
+ NPDOverlappingPoints *overlapping_points;
+};
+
+struct _NPDBone
+{
+ gint num_of_points;
+ NPDPoint *points; /* array of points */
+ gfloat *weights; /* array of weights */
+};
+
+struct _NPDOverlappingPoints
+{
+ gint num_of_points;
+ NPDPoint *representative; /* pointer to representative of cluster */
+ NPDPoint **points; /* array of pointers to points */
+};
+
+typedef struct
+{
+ gint num_of_bones;
+ gint num_of_overlapping_points;
+ gboolean ASAP; /* don't change directly!
+ * use npd_set_deformation_type function */
+ gboolean MLS_weights; /* don't change directly!
+ * use npd_set_deformation_type function */
+ gfloat MLS_weights_alpha;
+ NPDBone *current_bones; /* array of current bones */
+ NPDBone *reference_bones; /* array of reference bones */
+ NPDOverlappingPoints *list_of_overlapping_points; /* array of overlapping points */
+} NPDHiddenModel;
+
+typedef struct
+{
+ NPDPoint point;
+ NPDOverlappingPoints *overlapping_points; /* pointer to overlapping points */
+} NPDControlPoint;
+
+typedef struct
+{
+ gfloat control_point_radius;
+ gboolean control_points_visible;
+ gboolean mesh_visible;
+ gboolean texture_visible;
+ gint mesh_square_size;
+ GArray *control_points; /* GArray of control points */
+ NPDHiddenModel *hidden_model;
+ NPDImage *reference_image;
+ NPDDisplay *display;
+} NPDModel;
+
+#define npd_init(set_pixel, get_pixel, \
+ draw_line) \
+npd_set_pixel_color = set_pixel; \
+npd_get_pixel_color = get_pixel; \
+npd_draw_line = draw_line;
+
+void npd_init_model (NPDModel *model);
+void npd_destroy_hidden_model (NPDHiddenModel *model);
+void npd_destroy_model (NPDModel *model);
+
+NPDControlPoint *npd_add_control_point (NPDModel *model,
+ NPDPoint *coord);
+void npd_remove_control_point (NPDModel *model,
+ NPDControlPoint *control_point);
+void npd_remove_control_points (NPDModel *model,
+ GList *control_points);
+void npd_remove_all_control_points (NPDModel *model);
+void npd_set_control_point_weight (NPDControlPoint *cp,
+ gfloat weight);
+gboolean npd_equal_coordinates (NPDPoint *p1,
+ NPDPoint *p2);
+gboolean npd_equal_coordinates_epsilon (NPDPoint *p1,
+ NPDPoint *p2,
+ gfloat epsilon);
+NPDControlPoint *npd_get_control_point_with_radius_at
+ (NPDModel *model,
+ NPDPoint *coord,
+ gfloat control_point_radius);
+NPDControlPoint *npd_get_control_point_at (NPDModel *model,
+ NPDPoint *coord);
+void npd_create_square (NPDBone *square,
+ gint x,
+ gint y,
+ gint width,
+ gint height);
+void npd_create_list_of_overlapping_points
+ (NPDHiddenModel *model);
+void add_point_to_suitable_cluster (GHashTable *coords_to_cluster,
+ NPDPoint *point,
+ GPtrArray *list_of_overlapping_points);
+void npd_set_overlapping_points_weight
+ (NPDOverlappingPoints *op,
+ gfloat weight);
+void npd_set_point_coordinates (NPDPoint *target,
+ NPDPoint *source);
+void npd_set_deformation_type (NPDModel *model,
+ gboolean ASAP,
+ gboolean MLS_weights);
+void npd_compute_MLS_weights (NPDModel *model);
+void npd_reset_weights (NPDHiddenModel *hidden_model);
+
+void npd_print_model (NPDModel *model,
+ gboolean print_control_points);
+void npd_print_hidden_model (NPDHiddenModel *hm,
+ gboolean print_bones,
+ gboolean print_overlapping_points);
+void npd_print_bone (NPDBone *bone);
+void npd_print_point (NPDPoint *point,
+ gboolean print_details);
+void npd_print_overlapping_points (NPDOverlappingPoints *op);
+#endif /* __NPD_COMMON_H__ */
diff --git a/libs/npd/npd_gegl.c b/libs/npd/npd_gegl.c
new file mode 100644
index 0000000..1a8abaf
--- /dev/null
+++ b/libs/npd/npd_gegl.c
@@ -0,0 +1,68 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#include "npd_gegl.h"
+#include <glib.h>
+
+void
+npd_new_matrix (NPDMatrix **matrix)
+{
+ *matrix = g_new (NPDMatrix, 1);
+}
+
+void
+npd_destroy_matrix (NPDMatrix **matrix)
+{
+ g_free (*matrix);
+}
+
+void
+npd_compute_affinity (NPDPoint *p11,
+ NPDPoint *p21,
+ NPDPoint *p31,
+ NPDPoint *p12,
+ NPDPoint *p22,
+ NPDPoint *p32,
+ NPDMatrix *T)
+{
+ GeglMatrix3 Y, X;
+
+ Y.coeff[0][0] = p12->x; Y.coeff[1][0] = p12->y; Y.coeff[2][0] = 1;
+ Y.coeff[0][1] = p22->x; Y.coeff[1][1] = p22->y; Y.coeff[2][1] = 1;
+ Y.coeff[0][2] = p32->x; Y.coeff[1][2] = p32->y; Y.coeff[2][2] = 1;
+
+ X.coeff[0][0] = p11->x; X.coeff[1][0] = p11->y; X.coeff[2][0] = 1;
+ X.coeff[0][1] = p21->x; X.coeff[1][1] = p21->y; X.coeff[2][1] = 1;
+ X.coeff[0][2] = p31->x; X.coeff[1][2] = p31->y; X.coeff[2][2] = 1;
+
+ gegl_matrix3_invert (&X);
+ gegl_matrix3_multiply (&Y, &X, &T->matrix);
+}
+
+void
+npd_apply_transformation (NPDMatrix *T,
+ NPDPoint *src,
+ NPDPoint *dest)
+{
+ gdouble x = src->x, y = src->y;
+ gegl_matrix3_transform_point (&T->matrix, &x, &y);
+ dest->x = x; dest->y = y;
+}
diff --git a/libs/npd/npd_gegl.h b/libs/npd/npd_gegl.h
new file mode 100644
index 0000000..7ce4790
--- /dev/null
+++ b/libs/npd/npd_gegl.h
@@ -0,0 +1,33 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#ifndef __NPD_GEGL_H__
+#define __NPD_GEGL_H__
+
+#include "npd_math.h"
+#include <gegl-matrix.h>
+
+struct _NPDMatrix
+{
+ GeglMatrix3 matrix;
+};
+
+#endif /* __NPD_GEGL_H__ */
diff --git a/libs/npd/npd_math.c b/libs/npd/npd_math.c
new file mode 100644
index 0000000..b14f73f
--- /dev/null
+++ b/libs/npd/npd_math.c
@@ -0,0 +1,47 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#include "npd_math.h"
+#include <math.h>
+
+gboolean
+npd_equal_floats (gfloat a,
+ gfloat b)
+{
+ return npd_equal_floats_epsilon (a, b, NPD_EPSILON);
+}
+
+gboolean
+npd_equal_floats_epsilon (gfloat a,
+ gfloat b,
+ gfloat epsilon)
+{
+ return fabs (a - b) < epsilon;
+}
+
+gfloat
+npd_SED (NPDPoint *p1,
+ NPDPoint *p2)
+{
+ gint dx = p1->x - p2->x;
+ gint dy = p1->y - p2->y;
+ return dx * dx + dy * dy;
+}
diff --git a/libs/npd/npd_math.h b/libs/npd/npd_math.h
new file mode 100644
index 0000000..411f4e9
--- /dev/null
+++ b/libs/npd/npd_math.h
@@ -0,0 +1,58 @@
+/*
+ * This file is part of N-point image deformation library.
+ *
+ * N-point image deformation library is free software: you can
+ * redistribute it and/or modify it under the terms of the
+ * GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 3 of the License,
+ * or (at your option) any later version.
+ *
+ * N-point image deformation library 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 Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with N-point image deformation library.
+ * If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#ifndef __NPD_MATH_H__
+#define __NPD_MATH_H__
+
+#include "npd_common.h"
+
+#define NPD_EPSILON 0.00001
+
+void npd_compute_homography (NPDPoint *p11,
+ NPDPoint *p21,
+ NPDPoint *p31,
+ NPDPoint *p41,
+ NPDPoint *p12,
+ NPDPoint *p22,
+ NPDPoint *p32,
+ NPDPoint *p42,
+ NPDMatrix *T);
+void npd_compute_affinity (NPDPoint *p11,
+ NPDPoint *p21,
+ NPDPoint *p31,
+ NPDPoint *p12,
+ NPDPoint *p22,
+ NPDPoint *p32,
+ NPDMatrix *T);
+void npd_apply_transformation (NPDMatrix *T,
+ NPDPoint *src,
+ NPDPoint *dest);
+gboolean npd_equal_floats_epsilon (gfloat a,
+ gfloat b,
+ gfloat epsilon);
+gboolean npd_equal_floats (gfloat a,
+ gfloat b);
+gfloat npd_SED (NPDPoint *p1,
+ NPDPoint *p2);
+void npd_new_matrix (NPDMatrix **matrix);
+void npd_destroy_matrix (NPDMatrix **matrix);
+
+#endif /* __NPD_MATH_H__ */
diff --git a/operations/external/Makefile.am b/operations/external/Makefile.am
index b929744..6b2dc4f 100644
--- a/operations/external/Makefile.am
+++ b/operations/external/Makefile.am
@@ -158,5 +158,13 @@ rgbe_save_la_SOURCES = rgbe-save.c
rgbe_save_la_CFLAGS = $(AM_CFLAGS) -I $(top_srcdir)/libs
rgbe_save_la_LIBADD = $(op_libs) $(top_builddir)/libs/rgbe/librgbe.la
+# Dependencies are in our source tree
+if HAVE_CAIRO
+ops += npd.la
+npd_la_SOURCES = npd.c
+npd_la_CFLAGS = $(AM_CFLAGS) $(NPD_CFLAGS) $(CAIRO_CFLAGS)
+npd_la_LIBADD = $(op_libs) $(NPD_LIBS) $(CAIRO_LIBS)
+endif
+
opdir = $(libdir)/gegl- GEGL_API_VERSION@
op_LTLIBRARIES = $(ops)
diff --git a/operations/external/npd.c b/operations/external/npd.c
new file mode 100644
index 0000000..63bfc37
--- /dev/null
+++ b/operations/external/npd.c
@@ -0,0 +1,329 @@
+/* This file is an image processing operation for GEGL
+ *
+ * GEGL is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 3 of the License, or (at your option) any later version.
+ *
+ * GEGL 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
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with GEGL; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Copyright (C) 2013 Marek Dvoroznak <dvoromar gmail com>
+ */
+
+#include "config.h"
+#include <glib/gi18n-lib.h>
+
+#ifdef GEGL_CHANT_PROPERTIES
+gegl_chant_pointer (model, _("model"),
+ _("Model - basic element we operate on"))
+
+gegl_chant_int (square_size, _("square size"),
+ 5, 1000, 20,
+ _("Size of an edge of square the mesh consists of"))
+
+gegl_chant_int (rigidity, _("rigidity"),
+ 0, 10000, 100,
+ _("The number of deformation iterations"))
+
+gegl_chant_boolean (ASAP_deformation, _("ASAP deformation"),
+ FALSE,
+ _("ASAP deformation is performed when TRUE, ARAP deformation otherwise"))
+
+gegl_chant_boolean (MLS_weights, _("MLS weights"),
+ FALSE,
+ _("Use MLS weights"))
+
+gegl_chant_double (MLS_weights_alpha, _("MLS weights alpha"),
+ 0.1, 2.0, 1.0,
+ _("Alpha parameter of MLS weights"))
+
+gegl_chant_boolean (mesh_visible, _("mesh visible"),
+ TRUE,
+ _("Should the mesh be visible?"))
+#else
+
+#define GEGL_CHANT_TYPE_FILTER
+#define GEGL_CHANT_C_FILE "npd.c"
+
+#include "gegl-chant.h"
+#include <stdio.h>
+#include <math.h>
+#include <npd/npd.h>
+#include <npd/npd_gegl.h>
+#include <cairo.h>
+
+struct _NPDImage
+{
+ gint width;
+ gint height;
+ NPDPoint position;
+ guchar *buffer;
+};
+
+struct _NPDDisplay
+{
+ NPDImage image;
+ cairo_t *cr;
+};
+
+typedef struct
+{
+ gboolean first_run;
+ NPDModel model;
+} NPDProperties;
+
+void npd_create_image (NPDImage *image,
+ GeglBuffer *gegl_buffer,
+ const Babl *format);
+
+void npd_set_pixel_color_impl (NPDImage *image,
+ gint x,
+ gint y,
+ NPDColor *color);
+
+void npd_get_pixel_color_impl (NPDImage *image,
+ gint x,
+ gint y,
+ NPDColor *color);
+
+void npd_draw_line_impl (NPDDisplay *display,
+ gfloat x0,
+ gfloat y0,
+ gfloat x1,
+ gfloat y1);
+
+void npd_set_pixel_color_impl (NPDImage *image,
+ gint x,
+ gint y,
+ NPDColor *color)
+{
+ if (x > -1 && x < image->width &&
+ y > -1 && y < image->height)
+ {
+ gint position = 4 * (y * image->width + x);
+
+ image->buffer[position + 0] = color->r;
+ image->buffer[position + 1] = color->g;
+ image->buffer[position + 2] = color->b;
+ image->buffer[position + 3] = color->a;
+ }
+}
+
+void
+npd_get_pixel_color_impl (NPDImage *image,
+ gint x,
+ gint y,
+ NPDColor *color)
+{
+ if (x > -1 && x < image->width &&
+ y > -1 && y < image->height)
+ {
+ gint position = 4 * (y * image->width + x);
+
+ color->r = image->buffer[position + 0];
+ color->g = image->buffer[position + 1];
+ color->b = image->buffer[position + 2];
+ color->a = image->buffer[position + 3];
+ }
+ else
+ {
+ color->r = color->g = color->b = color->a = 0;
+ }
+}
+
+void npd_draw_line_impl (NPDDisplay *display,
+ gfloat x0,
+ gfloat y0,
+ gfloat x1,
+ gfloat y1)
+{
+ cairo_move_to (display->cr, x0, y0);
+ cairo_line_to (display->cr, x1, y1);
+}
+
+void
+npd_draw_model (NPDModel *model,
+ NPDDisplay *display)
+{
+ NPDHiddenModel *hm = model->hidden_model;
+ NPDImage *image = model->reference_image;
+ gint i;
+
+ /* draw texture */
+ if (model->texture_visible)
+ {
+ for (i = 0; i < hm->num_of_bones; i++)
+ {
+ npd_texture_quadrilateral(&hm->reference_bones[i],
+ &hm->current_bones[i],
+ image,
+ &display->image,
+ NPD_BILINEAR_INTERPOLATION | NPD_ALPHA_BLENDING);
+ }
+ }
+
+ /* draw mesh */
+ if (model->mesh_visible)
+ {
+ cairo_surface_t *surface;
+
+ surface = cairo_image_surface_create_for_data (display->image.buffer,
+ CAIRO_FORMAT_ARGB32,
+ display->image.width,
+ display->image.height,
+ display->image.width * 4);
+ display->cr = cairo_create (surface);
+ cairo_set_line_width (display->cr, 1);
+ cairo_set_source_rgba (display->cr, 0, 0, 0, 1);
+ npd_draw_mesh (model, display);
+ cairo_stroke (display->cr);
+ }
+}
+
+void
+npd_create_model_from_image (NPDModel *model,
+ NPDImage *image,
+ gint square_size)
+{
+ npd_init_model(model);
+ model->reference_image = image;
+ model->mesh_square_size = square_size;
+
+ npd_create_mesh_from_image (model, image->width, image->height, 0, 0);
+}
+
+void
+npd_create_image (NPDImage *image,
+ GeglBuffer *gegl_buffer,
+ const Babl *format)
+{
+ guchar *buffer;
+ buffer = g_new0 (guchar, gegl_buffer_get_pixel_count (gegl_buffer) * 4);
+ gegl_buffer_get (gegl_buffer, NULL, 1.0, format,
+ buffer, GEGL_AUTO_ROWSTRIDE, GEGL_ABYSS_NONE);
+
+ image->buffer = buffer;
+ image->width = gegl_buffer_get_width (gegl_buffer);
+ image->height = gegl_buffer_get_height (gegl_buffer);
+}
+
+void
+npd_destroy_image (NPDImage *image)
+{
+ g_free(image->buffer);
+}
+
+static void
+prepare (GeglOperation *operation)
+{
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+ NPDProperties *props;
+
+ if (o->chant_data == NULL)
+ {
+ props = g_new (NPDProperties, 1);
+ props->first_run = TRUE;
+ o->chant_data = props;
+ }
+
+ gegl_operation_set_format (operation, "input",
+ babl_format ("RGBA float"));
+ gegl_operation_set_format (operation, "output",
+ babl_format ("RGBA float"));
+}
+
+static gboolean
+process (GeglOperation *operation,
+ GeglBuffer *input,
+ GeglBuffer *output,
+ const GeglRectangle *result,
+ gint level)
+{
+ GeglChantO *o = GEGL_CHANT_PROPERTIES (operation);
+ const Babl *format = babl_format ("RGBA u8");
+ NPDProperties *props = o->chant_data;
+ NPDModel *model = &props->model;
+ NPDHiddenModel *hm;
+ guchar *output_buffer;
+ gint length = gegl_buffer_get_pixel_count (input) * 4;
+
+ if (props->first_run)
+ {
+ gint width, height;
+ NPDImage *input_image = g_new (NPDImage, 1);
+ NPDDisplay *display = g_new (NPDDisplay, 1);
+
+ npd_init (npd_set_pixel_color_impl,
+ npd_get_pixel_color_impl,
+ npd_draw_line_impl);
+
+ npd_create_image (input_image, input, format);
+ width = input_image->width;
+ height = input_image->height;
+
+ output_buffer = g_new0 (guchar, length);
+ display->image.width = width;
+ display->image.height = height;
+ display->image.buffer = output_buffer;
+ model->display = display;
+
+ npd_create_model_from_image (model, input_image, o->square_size);
+ hm = model->hidden_model;
+/* npd_create_list_of_overlapping_points (hm);*/
+
+ o->model = model;
+
+ memcpy (output_buffer, input_image->buffer, length);
+
+ props->first_run = FALSE;
+ }
+ else
+ {
+ npd_set_deformation_type (model, o->ASAP_deformation, o->MLS_weights);
+
+ if (o->MLS_weights &&
+ model->hidden_model->MLS_weights_alpha != o->MLS_weights_alpha)
+ {
+ model->hidden_model->MLS_weights_alpha = o->MLS_weights_alpha;
+ npd_compute_MLS_weights (model);
+ }
+
+ model->mesh_visible = o->mesh_visible;
+
+ output_buffer = model->display->image.buffer;
+ memset (output_buffer, 0, length);
+ npd_deform_model (model, o->rigidity);
+ npd_draw_model (model, model->display);
+ }
+
+ gegl_buffer_set (output, NULL, 0, format, output_buffer, GEGL_AUTO_ROWSTRIDE);
+
+ return TRUE;
+}
+
+static void
+gegl_chant_class_init (GeglChantClass *klass)
+{
+ GeglOperationClass *operation_class;
+ GeglOperationFilterClass *filter_class;
+
+ operation_class = GEGL_OPERATION_CLASS (klass);
+ filter_class = GEGL_OPERATION_FILTER_CLASS (klass);
+
+ filter_class->process = process;
+ operation_class->prepare = prepare;
+
+ gegl_operation_class_set_keys (operation_class,
+ "categories" , "transform",
+ "name" , "gegl:npd",
+ "description" , _("Performs n-point image deformation"),
+ NULL);
+}
+
+#endif
\ No newline at end of file
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 91377d0..8b672fa 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -117,6 +117,7 @@ operations/external/jpg-load.c
operations/external/jpg-save.c
operations/external/lcms-from-profile.c
operations/external/matting-levin.c
+operations/external/npd.c
operations/external/npy-save.c
operations/external/path.c
operations/external/pixbuf.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]