[gegl] Meson port - integrating merge request !35



commit 9a89095fdd9d055024f5aa03c7c076e088e75f7b
Author: Øyvind Kolås <pippin gimp org>
Date:   Tue Aug 13 13:28:35 2019 +0200

    Meson port - integrating merge request !35
    
    This commit contains the meson build system changes from is a combination
    of commits from that branch, with most of the meson port done by
    Félix Piédallu - with some adaptations to sources and build by Øyvind Kolås.
    
    We need a transition phase where meson works on top of autotools to
    switch many of the CI systems gegl is involved in over to meson.
    
    Among the commits squashed into this one are:
    
    Author: Félix Piédallu <felix piedallu me>
        Remove check for py2
        post-merge
        build: add gegl-npd-lib to pkgconfig
        Added meson files
        [src] move libnsgif to subdir
        [src] Compatibility with Meson: libs/ -> subprojects/
    
    Author: Øyvind Kolås <pippin gimp org>
        tools: exp_combine fix signedness warning
        gegl: fix Babl gir name
        meson: do not builds docs and workshop by default
        bin/meson: link binary with gio
        operations/meson: add missing operations
        meson: skip opencl tests, opt all other tests out of parallel running
        put all genrated webcontent in one folder
        docs/meson: build of ops examples/html
        tools: skip searching for source path for ops, adapt for meson
        docs: make asciidoc part of build work with meson, using bits from nielsdg's meson branch
        bin:meson add lua deps
        meson: check for luajit instead of lua
        meson: generate gtk-doc
        meson: drop graphviz checking
        gegl: add back GEGL_MAJOR_VERSION etc in gegl-version.h
        build: add check for malloc_trim

 bin/lua/meson.build                                |   33 +
 bin/meson.build                                    |   50 +
 bin/ui-core.c                                      |   12 +-
 docs/babl/graphics/meson.build                     |   49 +
 docs/babl/meson.build                              |   64 ++
 docs/install-docs.sh                               |    6 +
 docs/meson.build                                   |  179 +++
 examples/meson.build                               |   52 +
 gegl/buffer/meson.build                            |   61 +
 gegl/graph/meson.build                             |   15 +
 gegl/meson.build                                   |  128 +++
 gegl/module/meson.build                            |    5 +
 gegl/opencl/meson.build                            |   27 +
 gegl/operation/meson.build                         |   47 +
 gegl/process/meson.build                           |   11 +
 gegl/property-types/meson.build                    |   15 +
 libs/npd/meson.build                               |   39 +
 libs/rgbe/meson.build                              |   11 +
 meson.build                                        |  414 +++++++
 meson_options.txt                                  |   37 +
 opencl/cltostring.py                               |   20 +-
 opencl/meson.build                                 |   61 +
 operations/common-cxx/meson.build                  |   34 +
 operations/common-gpl3+/meson.build                |   82 ++
 operations/common/meson.build                      |  144 +++
 operations/core/meson.build                        |   39 +
 operations/external/meson.build                    |  123 ++
 operations/generated/meson.build                   |   56 +
 operations/json/meson.build                        |    3 +
 operations/meson.build                             |   13 +
 operations/seamless-clone/meson.build              |   26 +
 operations/transform/meson.build                   |   31 +
 operations/workshop/external/meson.build           |   80 ++
 operations/workshop/generated/meson.build          |   27 +
 operations/workshop/meson.build                    |   45 +
 perf/meson.build                                   |   42 +
 po/meson.build                                     |    1 +
 seamless-clone/meson.build                         |   55 +
 subprojects/libnsgif/COPYING                       |   20 +
 subprojects/libnsgif/README                        |   36 +
 subprojects/libnsgif/libnsgif.c                    | 1169 ++++++++++++++++++++
 subprojects/libnsgif/libnsgif.h                    |  183 +++
 subprojects/libnsgif/libnsgif.pc.in                |   10 +
 subprojects/libnsgif/log.h                         |   21 +
 subprojects/libnsgif/lzw.c                         |  377 +++++++
 subprojects/libnsgif/lzw.h                         |  105 ++
 subprojects/libnsgif/meson.build                   |   16 +
 subprojects/poly2tri-c/COPYING                     |   40 +
 subprojects/poly2tri-c/LICENSE-Poly2Tri-C.txt      |   30 +
 subprojects/poly2tri-c/LICENSE-Poly2Tri.txt        |   31 +
 subprojects/poly2tri-c/README                      |   13 +
 subprojects/poly2tri-c/meson.build                 |   31 +
 .../poly2tri-c/poly2tri-c/p2t/common/cutils.h      |   67 ++
 .../poly2tri-c/poly2tri-c/p2t/common/meson.build   |    5 +
 .../poly2tri-c/p2t/common/poly2tri-private.h       |   62 ++
 .../poly2tri-c/poly2tri-c/p2t/common/shapes.c      |  677 ++++++++++++
 .../poly2tri-c/poly2tri-c/p2t/common/shapes.h      |  262 +++++
 .../poly2tri-c/poly2tri-c/p2t/common/utils.c       |  114 ++
 .../poly2tri-c/poly2tri-c/p2t/common/utils.h       |   67 ++
 subprojects/poly2tri-c/poly2tri-c/p2t/meson.build  |   10 +
 subprojects/poly2tri-c/poly2tri-c/p2t/poly2tri.h   |   43 +
 .../poly2tri-c/p2t/sweep/advancing_front.c         |  229 ++++
 .../poly2tri-c/p2t/sweep/advancing_front.h         |   92 ++
 subprojects/poly2tri-c/poly2tri-c/p2t/sweep/cdt.c  |   95 ++
 subprojects/poly2tri-c/poly2tri-c/p2t/sweep/cdt.h  |  105 ++
 .../poly2tri-c/poly2tri-c/p2t/sweep/meson.build    |    7 +
 .../poly2tri-c/poly2tri-c/p2t/sweep/sweep.c        | 1024 +++++++++++++++++
 .../poly2tri-c/poly2tri-c/p2t/sweep/sweep.h        |  282 +++++
 .../poly2tri-c/p2t/sweep/sweep_context.c           |  319 ++++++
 .../poly2tri-c/p2t/sweep/sweep_context.h           |  137 +++
 .../poly2tri-c/poly2tri-c/refine/bounded-line.c    |   84 ++
 .../poly2tri-c/poly2tri-c/refine/bounded-line.h    |   58 +
 .../poly2tri-c/poly2tri-c/refine/cdt-flipfix.c     |  159 +++
 .../poly2tri-c/poly2tri-c/refine/cdt-flipfix.h     |   49 +
 subprojects/poly2tri-c/poly2tri-c/refine/cdt.c     |  493 +++++++++
 subprojects/poly2tri-c/poly2tri-c/refine/cdt.h     |  127 +++
 subprojects/poly2tri-c/poly2tri-c/refine/circle.c  |   47 +
 subprojects/poly2tri-c/poly2tri-c/refine/circle.h  |   46 +
 subprojects/poly2tri-c/poly2tri-c/refine/cluster.c |  146 +++
 subprojects/poly2tri-c/poly2tri-c/refine/cluster.h |   55 +
 .../poly2tri-c/refine/delaunay-terminator.c        |  534 +++++++++
 .../poly2tri-c/refine/delaunay-terminator.h        |   71 ++
 subprojects/poly2tri-c/poly2tri-c/refine/edge.c    |  247 +++++
 subprojects/poly2tri-c/poly2tri-c/refine/edge.h    |  107 ++
 subprojects/poly2tri-c/poly2tri-c/refine/line.c    |  102 ++
 subprojects/poly2tri-c/poly2tri-c/refine/line.h    |   66 ++
 .../poly2tri-c/poly2tri-c/refine/mesh-action.c     |  228 ++++
 .../poly2tri-c/poly2tri-c/refine/mesh-action.h     |  170 +++
 subprojects/poly2tri-c/poly2tri-c/refine/mesh.c    |  408 +++++++
 subprojects/poly2tri-c/poly2tri-c/refine/mesh.h    |  361 ++++++
 .../poly2tri-c/poly2tri-c/refine/meson.build       |   25 +
 subprojects/poly2tri-c/poly2tri-c/refine/point.c   |  227 ++++
 subprojects/poly2tri-c/poly2tri-c/refine/point.h   |   99 ++
 subprojects/poly2tri-c/poly2tri-c/refine/pslg.c    |  105 ++
 subprojects/poly2tri-c/poly2tri-c/refine/pslg.h    |  115 ++
 subprojects/poly2tri-c/poly2tri-c/refine/refine.h  |   59 +
 subprojects/poly2tri-c/poly2tri-c/refine/refiner.c |   70 ++
 subprojects/poly2tri-c/poly2tri-c/refine/refiner.h |   61 +
 subprojects/poly2tri-c/poly2tri-c/refine/rmath.c   |  301 +++++
 subprojects/poly2tri-c/poly2tri-c/refine/rmath.h   |  132 +++
 .../poly2tri-c/poly2tri-c/refine/triangle.c        |  313 ++++++
 .../poly2tri-c/poly2tri-c/refine/triangle.h        |   94 ++
 .../poly2tri-c/poly2tri-c/refine/triangulation.h   |   49 +
 subprojects/poly2tri-c/poly2tri-c/refine/utils.c   |   51 +
 subprojects/poly2tri-c/poly2tri-c/refine/utils.h   |   88 ++
 subprojects/poly2tri-c/poly2tri-c/refine/vector2.c |   84 ++
 subprojects/poly2tri-c/poly2tri-c/refine/vector2.h |  101 ++
 subprojects/poly2tri-c/poly2tri-c/refine/vedge.c   |  228 ++++
 subprojects/poly2tri-c/poly2tri-c/refine/vedge.h   |  143 +++
 .../poly2tri-c/poly2tri-c/refine/visibility.c      |  438 ++++++++
 .../poly2tri-c/poly2tri-c/refine/visibility.h      |   46 +
 .../poly2tri-c/poly2tri-c/refine/vtriangle.c       |  145 +++
 .../poly2tri-c/poly2tri-c/refine/vtriangle.h       |   70 ++
 .../poly2tri-c/poly2tri-c/render/mesh-render.c     |  247 +++++
 .../poly2tri-c/poly2tri-c/render/mesh-render.h     |  175 +++
 .../poly2tri-c/poly2tri-c/render/meson.build       |    6 +
 .../poly2tri-c/poly2tri-c/render/svg-plot.c        |  204 ++++
 .../poly2tri-c/poly2tri-c/render/svg-plot.h        |   75 ++
 tests/buffer/buffer-test-wrapper.c                 |  370 +++++++
 tests/buffer/buffer-tests-run.sh                   |   11 +
 tests/buffer/meson.build                           |  106 ++
 tests/compositions/meson.build                     |  100 ++
 tests/ff-load-save/meson.build                     |   13 +
 tests/ff-load-save/tests_ff_load_save.sh           |   38 +
 tests/meson.build                                  |   12 +
 tests/mipmap/meson.build                           |   23 +
 tests/opencl/meson.build                           |   44 +
 tests/opencl/opencl_test.sh                        |   13 +
 tests/python/meson.build                           |   25 +
 tests/simple/meson.build                           |   60 +
 tests/xml/meson.build                              |   34 +
 tools/meson.build                                  |   80 ++
 tools/operations_html.c                            |   28 +-
 133 files changed, 16055 insertions(+), 27 deletions(-)
---
diff --git a/bin/lua/meson.build b/bin/lua/meson.build
new file mode 100644
index 000000000..61ffcd710
--- /dev/null
+++ b/bin/lua/meson.build
@@ -0,0 +1,33 @@
+lua_files = files(
+  'cairo_h.lua',
+  'cairo.lua',
+  'gegl_box-blur.lua',
+  'gegl_c2g.lua',
+  'gegl_crop.lua',
+  'gegl_edge-neon.lua',
+  'gegl_fill-path.lua',
+  'gegl_gaussian-blur.lua',
+  'gegl_linear-gradient.lua',
+  'gegl_median-blur.lua',
+  'gegl_radial-gradient.lua',
+  'gegl_rectangle.lua',
+  'gegl_rotate.lua',
+  'gegl_snn-mean.lua',
+  'gegl_stress.lua',
+  'gegl_translate.lua',
+  'gegl_unsharp-mask.lua',
+  'gegl_vector-stroke.lua',
+  'init.lua',
+  'mime.lua',
+  'mrg.lua',
+  'mrl.lua',
+  'preferences.lua',
+  'viewer.lua',
+)
+
+if lua.found()
+  install_data(
+    lua_files,
+    install_dir: get_option('datadir') /  api_name / 'lua'
+  )
+endif
diff --git a/bin/meson.build b/bin/meson.build
new file mode 100644
index 000000000..8e568535b
--- /dev/null
+++ b/bin/meson.build
@@ -0,0 +1,50 @@
+subdir('lua')
+
+gegl_sources = files(
+  'gegl-options.c',
+  'gegl-path-smooth.c',
+  'gegl.c',
+)
+
+gegl_deps = [
+  babl,
+  glib,
+  gobject,
+  math,
+]
+
+if libpng.found()
+  gegl_deps += [ libpng, ]
+endif
+
+if mrg.found() and gexiv2.found() and lua.found()
+  gegl_sources += files(
+    'argvs.c',
+    'mrg-gegl.c',
+    'ui-collection.c',
+    'ui-core.c',
+    'ui-viewer.c',
+  )
+
+  gegl_sources += custom_target('argvs-commands.inc',
+    input : gegl_sources,
+    output: 'argvs-commands.inc',
+    command: [ argvs_extract, '@INPUT@' ],
+    capture: true,
+  )
+
+  gegl_deps += [ mrg, gexiv2, sdl1, lua, gio ]
+endif
+
+if libspiro.found()
+  gegl_sources += files('gegl-path-spiro.c')
+  gegl_deps += [ libspiro, ]
+endif
+
+gegl_bin = executable('gegl',
+  gegl_sources,
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: gegl_deps,
+  link_with: gegl_lib,
+  install: true,
+)
diff --git a/bin/ui-core.c b/bin/ui-core.c
index 8990839b6..84c1477a1 100644
--- a/bin/ui-core.c
+++ b/bin/ui-core.c
@@ -5665,7 +5665,7 @@ static void gegl_ui (Mrg *mrg, void *data)
 
           if (g_str_has_suffix (o->path, ".lui"))
           {
-#if HAVE_LUA
+#ifdef HAVE_LUA
             gsize length = 0;
             int result;
 
@@ -5714,7 +5714,7 @@ static void gegl_ui (Mrg *mrg, void *data)
 
   if (g_str_has_suffix (o->path, ".lui"))
   {
-#if HAVE_LUA
+#ifdef HAVE_LUA
   int result;
   int status;
   if (lui_contents)
@@ -5737,7 +5737,7 @@ static void gegl_ui (Mrg *mrg, void *data)
 #endif
   }
 
-#if HAVE_LUA
+#ifdef HAVE_LUA
   if (run_lua_file ("viewer.lua"))
   {
   }
@@ -5752,7 +5752,7 @@ static void gegl_ui (Mrg *mrg, void *data)
     }
     else if (S_ISDIR (stat_buf.st_mode))
     {
-#if HAVE_LUA
+#ifdef HAVE_LUA
   if (run_lua_file ("collection.lua"))
   {
   }
@@ -5769,7 +5769,7 @@ static void gegl_ui (Mrg *mrg, void *data)
 
   if (o->show_preferences)
   {
-#if HAVE_LUA
+#ifdef HAVE_LUA
   if (run_lua_file ("preferences.lua"))
   {
   }
@@ -7038,7 +7038,7 @@ int cmd_save (COMMAND_ARGS) /* "save", 0, "", ""*/
 
   if (o->src_path)
   {
-    char *prepended = g_strdup_printf ("gegl:load path=%s\n%s", basename(o->src_path), serialized);
+    char *prepended = g_strdup_printf ("gegl:load path=%s\n%s", g_basename(o->src_path), serialized);
     g_file_set_contents (o->chain_path, prepended, -1, NULL);
     g_free (prepended);
   }
diff --git a/docs/babl/graphics/meson.build b/docs/babl/graphics/meson.build
new file mode 100644
index 000000000..fad8d10d2
--- /dev/null
+++ b/docs/babl/graphics/meson.build
@@ -0,0 +1,49 @@
+
+graphic_files = [
+  'babl-16x16.svg',
+  'babl-48x48.svg',
+  'babl-a4poster.svg',
+]
+
+graphic_files_install = []
+
+# Dummy config to copy index.html to build
+graphic_files_install += configure_file(
+  input:  'index.html',
+  output: '@PLAINNAME@',
+  configuration: configuration_data()
+)
+
+
+foreach file : graphic_files
+  # Dummy config to copy svg files to build
+  graphic_files_install += configure_file(
+    input:  file,
+    output: '@PLAINNAME@',
+    configuration: configuration_data()
+  )
+endforeach
+
+if rsvg_convert_bin.found()
+  foreach file : graphic_files
+    sizeinfo = (
+      file.contains('a4poster')
+      ? [ '-w', '256', ]
+      : []
+    )
+
+    graphic_files_install += custom_target(
+      file,
+      input: file,
+      output: '@BASENAME@.png',
+      command: [
+        rsvg_convert_bin,
+        sizeinfo,
+        '-o', '@OUTPUT@',
+        '@INPUT@',
+      ],
+      install: false,
+      build_by_default: true,
+    )
+  endforeach
+endif
diff --git a/docs/babl/meson.build b/docs/babl/meson.build
new file mode 100644
index 000000000..b63e767ca
--- /dev/null
+++ b/docs/babl/meson.build
@@ -0,0 +1,64 @@
+subdir('graphics')
+
+host    = 'pippin.gimp.org'
+location= 'public_html/babl'
+scptarget = host + ':' + location + '/'
+
+
+xml_insert = find_program(join_paths('tools', 'xml_insert.sh'))
+
+index_static_html = configure_file(
+  input : 'index-static.html.in',
+  output: 'index-static.html',
+  configuration: conf,
+)
+
+babl_css = configure_file(
+  input : 'babl.css',
+  output : 'babl.css',
+  copy: true
+)
+
+index_html_tmp_env = [
+  'BABL_PATH='+ join_paths(meson.build_root(), 'extensions'),
+]
+
+index_html_tmp = custom_target('index.html.tmp',
+  input : [ babl_html_dump, ],
+  output: [ 'index.html.tmp', ],
+  command: [
+    env_bin,
+    index_html_tmp_env,
+    babl_html_dump
+  ],
+  capture: true,
+)
+
+index_html = custom_target('index.html',
+  input : [
+    index_static_html,
+    index_html_tmp,
+    join_paths(meson.source_root(), 'AUTHORS'),
+    join_paths(meson.source_root(), 'TODO'),
+    join_paths(meson.source_root(), 'NEWS'),
+  ],
+  output: [ 'index.html', ],
+  command: [
+    env_bin,
+    'cp', '@INPUT0@', '@OUTPUT@',
+    '&&', xml_insert, '@OUTPUT@', 'BablBase', '@INPUT1@',
+    '&&', xml_insert, '@OUTPUT@', 'AUTHORS',  '@INPUT2@',
+    '&&', xml_insert, '@OUTPUT@', 'TODO',     '@INPUT3@',
+    '&&', xml_insert, '@OUTPUT@', 'NEWS',     '@INPUT4@',
+  ],
+  build_by_default: true,
+)
+
+
+run_target('push_web',
+  command: [
+    'scp', index_html, index_static_html, babl_css, scptarget,
+    '&&',
+    'scp', graphic_files_install, scptarget + 'graphics/'
+  ],
+)
diff --git a/docs/install-docs.sh b/docs/install-docs.sh
new file mode 100755
index 000000000..c62d9f4bc
--- /dev/null
+++ b/docs/install-docs.sh
@@ -0,0 +1,6 @@
+#!/bin/sh
+
+echo "installing docs $MESON_BUILD_ROOT"
+
+
+
diff --git a/docs/meson.build b/docs/meson.build
new file mode 100644
index 000000000..3419b321b
--- /dev/null
+++ b/docs/meson.build
@@ -0,0 +1,179 @@
+doc_config = configuration_data()
+doc_config.set('top_srcdir', meson.source_root())
+
+html_files = []
+gtkdoc_files = []
+
+#######################################
+# HTML files
+
+html_files += custom_target('class-hierarchy.html',
+  input : [ ],
+  output: [ 'class-hierarchy.html' ],
+  command: [ find_program('env'),
+    'GEGL_SWAP=RAM',
+    'GEGL_PATH='+ meson.build_root() / 'operations',
+    introspect,
+  ],
+  build_by_default : true,
+  capture: true,
+)
+
+if asciidoc.found()
+asciidoc_files = files(
+    'abyss_policy.txt',
+    'build.txt',
+    'commandline.txt',
+    'contribute.txt',
+    'development.txt',
+    'editor.txt',
+    'environment.txt',
+    'features.txt',
+    'gegl-chain.txt',
+    'glossary.txt',
+    'journal.txt',
+    'NEWS.txt',
+    'operation-api.txt',
+    'source-overview.txt',
+  )
+asciidoc_files += [
+    configure_file(
+      input : 'copyright.txt.in',
+      output: 'copyright.txt',
+      configuration: doc_config,
+    ),
+    configure_file(
+      input : 'hello-world.txt.in',
+      output: 'hello-world.txt',
+      configuration: doc_config,
+    ),
+    configure_file(
+      input : 'index.txt.in',
+      output: 'index.txt',
+      configuration: doc_config,
+    ),
+  ]
+
+
+foreach file: asciidoc_files
+  rendered_file = custom_target('@0@.html'.format(file).underscorify(),
+    output: '@BASENAME@.html',
+    input: file,
+    build_by_default : true,
+    command: [ asciidoc,
+      '--unsafe',
+      '-o', '@OUTPUT@',
+      '-a', 'stylesheet="@0@"'.format(join_paths(meson.source_root(), 'gegl.css')),
+      '-a', 'quirks!',
+      '@INPUT@',
+    ],
+  )
+endforeach
+
+
+endif
+
+
+#######################################
+# GTK Doc files
+
+gtkdoc_files += files(
+  'gegl.css',
+)
+
+gtkdoc_images = files(
+  'images/standard-input.png',
+  'images/standard-panorama.png',
+  'images/standard-aux.png',
+  'images/GEGL.png',
+)
+
+
+install_data(gtkdoc_files,
+  install_dir: gnome.gtkdoc_html_dir('gegl')
+)
+install_data(gtkdoc_images,
+  install_dir: join_paths (gnome.gtkdoc_html_dir('gegl') , 'images')
+)
+
+
+
+configure_file(input: 'images/GEGL.png',
+               output: 'GEGL.png',
+               copy: true,
+)
+
+
+docpath = join_paths(get_option('datadir'), 'gtk-doc', 'html')
+
+glib_prefix = dependency('glib-2.0').get_pkgconfig_variable('prefix')
+glib_docpath = join_paths(glib_prefix, 'share', 'gtk-doc', 'html')
+
+
+gnome.gtkdoc('gegl',
+             dependencies : gegl_lib,
+             main_xml : meson.project_name() + '-docs.xml',
+  src_dir: [
+    join_paths(meson.source_root(), 'gegl'),
+    join_paths(meson.build_root(), 'gegl'),
+  ],
+
+  fixxref_args: [
+    '--html-dir=@0@'.format(docpath),
+    '--extra-dir=@0@'.format(glib_docpath),
+  ],
+  install : true,
+)
+
+
+ doc_operations_examples_dir = join_paths(
+   meson.current_build_dir(), 'ophtml',
+ )
+ exclusion_pattern = '|'.join([
+   'alpha-inpaint',
+   'box-blur', 'box-percentile', 'buffer-cache', 'buffer-source',
+   'clone', 'convert-format',
+   'disc-percentile', 'dropshadow',
+   'exp-combine', 'exr-load',
+   'hstack',
+   'image-compare', 'integral-image', 'introspect',
+   'jpg-load',
+   'kuwahara',
+   'layer', 'line-profile', 'load',
+   'magick-load', 'mandelbrot', 'matting-global',
+   'nop',
+   'open-buffer',
+   'pixbuf', 'png-load',
+   'remap',
+   'snn-percentile', 'stretch-contrast', 'svg-load',
+   'v4l2',
+   'warp',
+ ])
+
+ doc_operations_examples = custom_target('gallery-stamp',
+   install:true,
+   install_dir: gnome.gtkdoc_html_dir('gegl'),
+   output: ['gallery-stamp'],
+   command: [ bash, '-c',
+     ' '.join([
+     'mkdir', '-p', doc_operations_examples_dir,
+     '&&',
+     'cd', join_paths(meson.current_build_dir(), 'ophtml'),
+     '&&',
+     'GEGL_PATH=' + join_paths(meson.build_root(), 'operations'),
+     operations_html.full_path(),
+     '&&',
+     'echo', 'Generating example images',
+     '&&',
+     'BABL_TOLERANCE=0.0',
+     'GEGL_SWAP=RAM',
+     'GEGL_PATH=' + join_paths(meson.build_root(), 'operations'),
+     gegl_tester.full_path(),
+     '--all', '-o',  doc_operations_examples_dir,
+     '-d', join_paths(meson.current_source_dir(), 'images'),
+     '-e', '"' + exclusion_pattern + '"', '||', 'true', '&&', 'touch', '../gallery-stamp'
+     ])
+   ],
+ )
+
+meson.add_install_script('install-docs.sh')
diff --git a/examples/meson.build b/examples/meson.build
new file mode 100644
index 000000000..d3ac1f0ac
--- /dev/null
+++ b/examples/meson.build
@@ -0,0 +1,52 @@
+
+examples = [
+  { 'name': '2geglbuffer', },
+  { 'name': 'frame-counter', },
+  { 'name': 'gegl-convert', },
+  { 'name': 'gegl-slicer', },
+  { 'name': 'geglbuffer-add-image', },
+  { 'name': 'geglbuffer-clock', },
+  { 'name': 'hello-world', },
+  { 'name': 'video-invert', },
+]
+
+if sdl2.found()
+  examples += {
+    'name': 'sdl-draw',
+    'deps': [ sdl2, math, ]
+  }
+endif
+if gexiv2.found()
+  examples += {
+    'name': 'gegl-video',
+    'deps': [ gexiv2, ]
+  }
+endif
+
+
+foreach example : examples
+  example_name = example.get('name')
+  example_srcs = example.get('srcs', example_name + '.c')
+  example_deps = example.get('deps', [])
+
+  exe = executable(example_name,
+    example_srcs,
+    include_directories: [ geglInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+      example_deps,
+    ],
+    link_with: [ gegl_lib, ],
+    install: false,
+  )
+
+  # Fore use as tests for ff-load-save
+  if example_name == 'gegl-video'
+    gegl_video_example = exe
+  endif
+  if example_name == 'frame-counter'
+    frame_counter_example = exe
+  endif
+endforeach
diff --git a/gegl/buffer/meson.build b/gegl/buffer/meson.build
new file mode 100644
index 000000000..e997d71eb
--- /dev/null
+++ b/gegl/buffer/meson.build
@@ -0,0 +1,61 @@
+gegl_sources += files(
+  'gegl-algorithms.c',
+  'gegl-buffer-access.c',
+  'gegl-buffer-config.c',
+  'gegl-buffer-enums.c',
+  'gegl-buffer-iterator.c',
+  'gegl-buffer-iterator2.c',
+  'gegl-buffer-linear.c',
+  'gegl-buffer-load.c',
+  'gegl-buffer-matrix2.c',
+  'gegl-buffer-save.c',
+  'gegl-buffer-swap.c',
+  'gegl-buffer.c',
+  'gegl-compression-nop.c',
+  'gegl-compression-rle.c',
+  'gegl-compression-zlib.c',
+  'gegl-compression.c',
+  'gegl-memory.c',
+  'gegl-rectangle.c',
+  'gegl-sampler-cubic.c',
+  'gegl-sampler-linear.c',
+  'gegl-sampler-lohalo.c',
+  'gegl-sampler-nearest.c',
+  'gegl-sampler-nohalo.c',
+  'gegl-sampler.c',
+  'gegl-scratch.c',
+  'gegl-tile-alloc.c',
+  'gegl-tile-backend-buffer.c',
+  'gegl-tile-backend-file-async.c',
+  'gegl-tile-backend-ram.c',
+  'gegl-tile-backend-swap.c',
+  'gegl-tile-backend.c',
+  'gegl-tile-handler-cache.c',
+  'gegl-tile-handler-chain.c',
+  'gegl-tile-handler-empty.c',
+  'gegl-tile-handler-log.c',
+  'gegl-tile-handler-zoom.c',
+  'gegl-tile-handler.c',
+  'gegl-tile-source.c',
+  'gegl-tile-storage.c',
+  'gegl-tile.c',
+)
+
+gegl_introspectable_headers += files(
+  'gegl-buffer-backend.h',
+  'gegl-buffer-enums.h',
+  'gegl-buffer-iterator.h',
+  'gegl-buffer-matrix2.h',
+  'gegl-buffer-swap.h',
+  'gegl-buffer.h',
+  'gegl-memory.h',
+  'gegl-rectangle.h',
+  'gegl-scratch.h',
+  'gegl-tile-backend.h',
+  'gegl-tile-handler.h',
+  'gegl-tile-source.h',
+)
+
+gegl_headers += files(
+  'gegl-tile.h',
+)
diff --git a/gegl/graph/meson.build b/gegl/graph/meson.build
new file mode 100644
index 000000000..9e9ef120f
--- /dev/null
+++ b/gegl/graph/meson.build
@@ -0,0 +1,15 @@
+gegl_sources += files(
+  'gegl-cache.c',
+  'gegl-callback-visitor.c',
+  'gegl-connection.c',
+  'gegl-node-output-visitable.c',
+  'gegl-node.c',
+  'gegl-pad.c',
+  'gegl-region-generic.c',
+  'gegl-visitable.c',
+  'gegl-visitor.c',
+)
+
+gegl_introspectable_headers += files(
+  'gegl-node.h',
+)
diff --git a/gegl/meson.build b/gegl/meson.build
new file mode 100644
index 000000000..1bf00740b
--- /dev/null
+++ b/gegl/meson.build
@@ -0,0 +1,128 @@
+
+geglInclude = include_directories(
+  '.',
+  'buffer',
+  'graph',
+  'module',
+  'opencl',
+  'operation',
+  'process',
+  'property-types',
+)
+
+gegl_cflags = [
+  '-DLIBDIR="@0@"'.format(get_option('prefix') / get_option('libdir')),
+  '-DG_LOG_DOMAIN="@0@"'.format('GEGL'),
+  '-DGEGL_LOCALEDIR="@0@"'.format(get_option('localedir')),
+]
+
+gegl_version_h = configure_file(
+  input : 'gegl-version.h.in',
+  output: 'gegl-version.h',
+  configuration: config,
+)
+
+gegl_introspectable_headers = files(
+  'gegl.h',
+  'gegl-apply.h',
+  'gegl-enums.h',
+  'gegl-init.h',
+  'gegl-lookup.h',
+  'gegl-matrix.h',
+  'gegl-operations-util.h',
+  'gegl-parallel.h',
+  'gegl-random.h',
+  'gegl-types.h',
+  'gegl-utils.h',
+) + [
+  gegl_version_h,
+]
+
+gegl_headers = files(
+  'gegl-cpuaccel.h',
+  'gegl-debug.h',
+  'gegl-op.h',
+  'gegl-plugin.h',
+)
+
+gegl_sources = files(
+  'gegl-apply.c',
+  'gegl-config.c',
+  'gegl-cpuaccel.c',
+  'gegl-dot-visitor.c',
+  'gegl-dot.c',
+  'gegl-enums.c',
+  'gegl-gio.c',
+  'gegl-init.c',
+  'gegl-instrument.c',
+  'gegl-introspection-support.c',
+  'gegl-lookup.c',
+  'gegl-matrix.c',
+  'gegl-parallel.c',
+  'gegl-random.c',
+  'gegl-serialize.c',
+  'gegl-stats.c',
+  'gegl-utils.c',
+  'gegl-xml.c',
+)
+
+subdir('buffer')
+subdir('graph')
+subdir('module')
+subdir('opencl')
+subdir('operation')
+subdir('process')
+subdir('property-types')
+
+gegl_headers += gegl_introspectable_headers
+
+install_headers(gegl_headers,
+  subdir: api_name
+)
+
+gegl_lib = library(api_name,
+  gegl_sources,
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [
+    babl,
+    glib,
+    gio,
+    math,
+    gmodule,
+  ],
+  c_args: [ gegl_cflags, ],
+  install: true,
+  version: so_version,
+)
+
+
+introspection_sources = gegl_introspectable_headers + files(
+  'gegl-introspection-support.h',
+  'opencl' / 'gegl-cl-introspection-support.h',
+)
+
+
+if get_option('introspection')
+  gegl_gir = gnome.generate_gir(gegl_lib,
+    sources: introspection_sources,
+    nsversion: api_version,
+    namespace: 'Gegl',
+    identifier_prefix: 'Gegl',
+    symbol_prefix: 'gegl',
+    header: 'gegl.h',
+    includes: [ 'GLib-2.0', 'GObject-2.0', 'Babl-0.1' ],
+    include_directories: [
+      rootInclude,
+      geglInclude,
+    ],
+    install: true,
+  )
+
+  if vapigen.found()
+     gnome.generate_vapi(api_name,
+       metadata_dirs: '.',
+       sources: gegl_gir[0],
+       install: true,
+     )
+  endif
+endif
diff --git a/gegl/module/meson.build b/gegl/module/meson.build
new file mode 100644
index 000000000..cff16af9c
--- /dev/null
+++ b/gegl/module/meson.build
@@ -0,0 +1,5 @@
+gegl_sources += files(
+  'gegldatafiles.c',
+  'geglmodule.c',
+  'geglmoduledb.c',
+)
diff --git a/gegl/opencl/meson.build b/gegl/opencl/meson.build
new file mode 100644
index 000000000..778c463f9
--- /dev/null
+++ b/gegl/opencl/meson.build
@@ -0,0 +1,27 @@
+gegl_sources += files(
+  'gegl-buffer-cl-cache.c',
+  'gegl-buffer-cl-iterator.c',
+  'gegl-cl-color.c',
+  'gegl-cl-init.c',
+  'gegl-cl-random.c',
+  'gegl-cl.c',
+)
+
+gegl_opencl_headers = files(
+  'cl_d3d10.h',
+  'cl_ext.h',
+  'cl_gl_ext.h',
+  'cl_gl.h',
+  'cl_platform.h',
+  'cl.h',
+  'gegl-cl-color.h',
+  'gegl-cl-init.h',
+  'gegl-cl-random.h',
+  'gegl-cl-types.h',
+  'gegl-cl.h',
+  'opencl.h',
+)
+
+install_headers(gegl_opencl_headers,
+  subdir: api_name / 'opencl'
+)
diff --git a/gegl/operation/meson.build b/gegl/operation/meson.build
new file mode 100644
index 000000000..6a3aa4eac
--- /dev/null
+++ b/gegl/operation/meson.build
@@ -0,0 +1,47 @@
+gegl_sources += files(
+  'gegl-extension-handler.c',
+  'gegl-operation-area-filter.c',
+  'gegl-operation-composer.c',
+  'gegl-operation-composer3.c',
+  'gegl-operation-context-private.h',
+  'gegl-operation-context.c',
+  'gegl-operation-filter.c',
+  'gegl-operation-handlers-private.h',
+  'gegl-operation-handlers.c',
+  'gegl-operation-meta-json.c',
+  'gegl-operation-meta.c',
+  'gegl-operation-point-composer.c',
+  'gegl-operation-point-composer3.c',
+  'gegl-operation-point-filter.c',
+  'gegl-operation-point-render.c',
+  'gegl-operation-property-keys.c',
+  'gegl-operation-sink.c',
+  'gegl-operation-source.c',
+  'gegl-operation-temporal.c',
+  'gegl-operation.c',
+  'gegl-operations.c',
+)
+gegl_operation_headers = files(
+  'gegl-extension-handler.h',
+  'gegl-operation-area-filter.h',
+  'gegl-operation-composer.h',
+  'gegl-operation-composer3.h',
+  'gegl-operation-context.h',
+  'gegl-operation-filter.h',
+  'gegl-operation-handlers.h',
+  'gegl-operation-meta-json.h',
+  'gegl-operation-meta.h',
+  'gegl-operation-point-composer.h',
+  'gegl-operation-point-composer3.h',
+  'gegl-operation-point-filter.h',
+  'gegl-operation-point-render.h',
+  'gegl-operation-property-keys.h',
+  'gegl-operation-sink.h',
+  'gegl-operation-source.h',
+  'gegl-operation-temporal.h',
+  'gegl-operation.h',
+)
+
+install_headers(gegl_operation_headers,
+  subdir: api_name / 'operation',
+)
diff --git a/gegl/process/meson.build b/gegl/process/meson.build
new file mode 100644
index 000000000..be1416bb0
--- /dev/null
+++ b/gegl/process/meson.build
@@ -0,0 +1,11 @@
+gegl_sources += files(
+  'gegl-eval-manager.c',
+  'gegl-graph-traversal-debug.c',
+  'gegl-graph-traversal.c',
+  'gegl-processor.c',
+)
+
+gegl_introspectable_headers += files(
+  'gegl-graph-debug.h',
+  'gegl-processor.h',
+)
diff --git a/gegl/property-types/meson.build b/gegl/property-types/meson.build
new file mode 100644
index 000000000..73e7ab377
--- /dev/null
+++ b/gegl/property-types/meson.build
@@ -0,0 +1,15 @@
+gegl_sources += files(
+  'gegl-audio-fragment.c',
+  'gegl-color.c',
+  'gegl-curve.c',
+  'gegl-paramspecs.c',
+  'gegl-path.c',
+)
+
+gegl_introspectable_headers += files(
+  'gegl-audio-fragment.h',
+  'gegl-color.h',
+  'gegl-curve.h',
+  'gegl-paramspecs.h',
+  'gegl-path.h',
+)
diff --git a/libs/npd/meson.build b/libs/npd/meson.build
new file mode 100644
index 000000000..6baf9c942
--- /dev/null
+++ b/libs/npd/meson.build
@@ -0,0 +1,39 @@
+gegl_npd_sources = files(
+  'deformation.c',
+  'graphics.c',
+  'lattice_cut.c',
+  'npd_common.c',
+  'npd_debug.c',
+  'npd_gegl.c',
+  'npd_math.c',
+)
+
+gegl_npd_headers = files(
+  'deformation.h',
+  'graphics.h',
+  'lattice_cut.h',
+  'npd_common.h',
+  'npd_debug.h',
+  'npd_gegl.h',
+  'npd_math.h',
+  'npd.h',
+)
+
+gegl_npd_lib = library(meson.project_name() + '-npd-' + api_version,
+  gegl_npd_sources,
+  dependencies: [ glib, babl, math, ],
+  include_directories: geglInclude,
+  link_with: [
+    gegl_lib,
+  ],
+  install: true,
+)
+
+libnpd = declare_dependency(
+  include_directories: include_directories('..'),
+  link_with: gegl_npd_lib,
+)
+
+install_headers(gegl_npd_headers,
+  subdir: api_name / 'npd',
+)
diff --git a/libs/rgbe/meson.build b/libs/rgbe/meson.build
new file mode 100644
index 000000000..f7cfd5eba
--- /dev/null
+++ b/libs/rgbe/meson.build
@@ -0,0 +1,11 @@
+
+rgbe_lib = static_library('rgbe',
+  'rgbe.c',
+  dependencies: glib,
+  include_directories: rootInclude,
+)
+
+librgbe = declare_dependency(
+  include_directories: include_directories('..'),
+  link_with: rgbe_lib,
+)
diff --git a/meson.build b/meson.build
new file mode 100644
index 000000000..9c4f4e20f
--- /dev/null
+++ b/meson.build
@@ -0,0 +1,414 @@
+project('gegl',
+  'c', 'cpp',
+  license: 'GPL3+',
+  version: '0.4.17',
+  meson_version: '>=0.50.0',
+  default_options: [
+    'c_std=gnu11',
+    'cpp_std=gnu++14',
+  ],
+)
+
+# Making releases on the stable branch:
+#   micro_version += 1;
+#   interface_age += 1;
+#   binary_age += 1;
+# if any functions have been added,
+#    set interface_age to 0.
+# if backwards compatibility has been broken,
+#    set binary_age _and_ interface_age to 0.
+
+pkgconfig = import('pkgconfig')
+i18n      = import('i18n')
+gnome     = import('gnome')
+python    = import('python3').find_python()
+
+cc  = meson.get_compiler('c')
+config= configuration_data()
+
+################################################################################
+# Project infos
+
+version = meson.project_version()
+array_version = version.split('.')
+major_version = array_version[0].to_int()
+minor_version = array_version[1].to_int()
+micro_version = array_version[2].to_int()
+
+api_version = '@0@.@1@'.format(major_version, minor_version)
+api_name    = meson.project_name() + '-' + api_version
+gettext_package = api_name
+
+stability_version_number = (major_version != 0 ? minor_version : micro_version)
+stable = (stability_version_number % 2 == 0)
+
+config.set        ('GEGL_MAJOR_VERSION',  '@0@'.format(major_version))
+config.set        ('GEGL_MINOR_VERSION',  '@0@'.format(minor_version))
+config.set        ('GEGL_MICRO_VERSION',  '@0@'.format(micro_version))
+config.set        ('GEGL_UNSTABLE',       not stable)
+
+config.set_quoted ('GEGL_LIBRARY',        '@0@'.format(api_name))
+config.set_quoted ('GETTEXT_PACKAGE',     '@0@'.format(gettext_package))
+
+# Libtool versionning
+interface_age = 1
+
+binary_age = 100 * minor_version + micro_version
+lt_current = binary_age - interface_age
+so_version  = '@0@.@1@.@2@'.format(0, lt_current, interface_age)
+
+################################################################################
+# Host system detection
+
+host_os = host_machine.system()
+os_win32  = host_os.contains('mingw') or host_os.contains('windows')
+os_android= host_os.contains('android')
+os_osx    = host_os.contains('darwin')
+
+if os_osx and cc.get_id() != 'clang'
+  error('You should use Clang on OSx.')
+endif
+
+
+host_cpu = host_machine.cpu()
+if   host_cpu.startswith('i') and host_cpu.endswith('86')
+  have_x86 = true
+  config.set10('ARCH_X86',    true)
+elif host_cpu == 'x86_64'
+  have_x86 = true
+  config.set10('ARCH_X86',    true)
+  config.set10('ARCH_X86_64', true)
+elif host_cpu == 'ppc' or host_cpu == 'powerpc'
+  have_ppc = true
+  config.set10('ARCH_PPC',    true)
+elif host_cpu == 'ppc64' or host_cpu == 'powerpc64'
+  have_ppc = true
+  config.set10('ARCH_PPC',    true)
+  config.set10('ARCH_PPC64',  true)
+else
+  error('Unknown host architecture')
+endif
+
+# Should be filled with c_arguments, is checked before use.
+c_arguments = [
+  '-DHAVE_CONFIG_H',
+]
+
+################################################################################
+# Extra warnings
+
+c_arguments += [
+  '-Winit-self',
+  '-Wmissing-declarations',
+  '-Wmissing-prototypes',
+  '-Wold-style-definition',
+  '-Wpointer-arith',
+  '-Wno-deprecated-declarations',
+]
+
+################################################################################
+# Check for compiler CPU extensions
+
+c_arguments += [
+  '-mfpmath=sse',
+]
+
+optionnal_arguments = [
+  'mmx',
+  'sse',
+  'sse2',
+  'sse4.1',
+  'f16c',
+]
+
+foreach arg : optionnal_arguments
+  if get_option('enable-@0@'.format(arg.underscorify()))
+    c_arguments += '-m@0@'.format(arg)
+  endif
+endforeach
+
+add_project_arguments(cc.get_supported_arguments(c_arguments), language: 'c')
+
+################################################################################
+# Utilities
+
+bash        = find_program('bash')
+
+asciidoc    = find_program('asciidoc',      required: false)
+enscript    = find_program('enscript',      required: false)
+perl        = find_program('perl5', 'perl', required: false)
+rsvg_convert= find_program('rsvg-convert',  required: false)
+ruby        = find_program('ruby',          required: false)
+w3m         = find_program('w3m',           required: false)
+
+################################################################################
+# Required Dependencies
+
+config.set('HAVE_UNISTD_H',    cc.has_header('unistd.h'))
+config.set('HAVE_EXECINFO_H',  cc.has_header('execinfo.h'))
+config.set('HAVE_FSYNC',       cc.has_function('fsync'))
+config.set('HAVE_MALLOC_TRIM', cc.has_function('malloc_trim'))
+
+math    = cc.find_library('m', required: false)
+thread  = dependency('threads')
+
+babl      = dependency('babl',        version: '>=0.1.70')
+glib      = dependency('glib-2.0',    version: '>=2.44.0')
+gobject   = dependency('gobject-2.0', version: '>=2.44.0')
+gmodule   = dependency('gmodule-2.0', version: '>=2.44.0')
+gthread   = dependency('gthread-2.0', version: '>=2.44.0')
+gio_os    = os_win32 ? 'gio-windows-2.0' : 'gio-unix-2.0'
+gio       = [
+            dependency('gio-2.0',     version: '>=2.44.0'),
+            dependency(gio_os,        version: '>=2.44.0'),
+]
+json_glib = dependency('json-glib-1.0', version: '>=1.2.6')
+
+# Required libraries eventually provided in subprojects/ subdir
+
+poly2tri_c= dependency('poly2tri-c',  version: '>=0.0.0',
+  fallback: [ 'poly2tri-c', 'poly2tri_c' ],
+  required: false,
+)
+
+libnsgif = dependency('libnsgif',
+  fallback: [ 'libnsgif', 'libnsgif' ],
+)
+
+################################################################################
+# Optionnal Dependencies
+
+gdk_pixbuf= dependency('gdk-pixbuf-2.0', version:'>=2.32.0',
+  required: get_option('gdk-pixbuf')
+)
+pango     = dependency('pango',       version: '>=1.38.0',
+  required: get_option('pango')
+)
+pangocairo= dependency('pangocairo',  version: '>=1.38.0',
+  required: get_option('pangocairo')
+)
+cairo     = dependency('cairo',       version: '>=1.12.2',
+  required: get_option('cairo')
+)
+exiv2     = dependency('exiv2',       version: '>=0.25',
+  required: get_option('exiv2')
+)
+gexiv2    = dependency('gexiv2',      version: '>=0.0.0',
+  required: get_option('gexiv2')
+)
+config.set('HAVE_GEXIV2', gexiv2.found())
+
+jasper    = dependency('jasper',      version: '>=1.900.1',
+  required: get_option('jasper')
+)
+lcms      = dependency('lcms2',       version: '>=2.8',
+  required: get_option('lcms')
+)
+lensfun   = dependency('lensfun',     version: '>=0.2.5',
+  required: get_option('lensfun')
+)
+libjpeg   = dependency('libjpeg',     version: '>=1.0.0',
+  required: get_option('libjpeg')
+)
+libpng    = dependency('libpng',      version: '>=1.6.0',
+  required: get_option('libpng')
+)
+libraw    = dependency('libraw',      version: '>=0.15.4',
+  required: get_option('libraw')
+)
+librsvg   = dependency('librsvg-2.0', version: '>=2.40.6',
+  required: get_option('librsvg')
+)
+libspiro  = dependency('libspiro',    version: '>=0.5.0',
+  required: get_option('libspiro')
+)
+libtiff   = dependency('libtiff-4',   version: '>=4.0.0',
+  required: get_option('libtiff')
+)
+libv4l1   = dependency('libv4l1',     version: '>=1.0.1',
+  required: get_option('libv4l')
+)
+libv4l2   = dependency('libv4l2',     version: '>=1.0.1',
+  required: get_option('libv4l2')
+)
+lua       = dependency('luajit',      version: '>=2.0.4',
+  required: get_option('lua')
+)
+config.set('HAVE_LUA', lua.found())
+mrg       = dependency('mrg',         version: '>=0.0.0',
+  required: get_option('mrg')
+)
+config.set('HAVE_MRG', mrg.found())
+
+openexr   = dependency('OpenEXR',     version: '>=1.6.1',
+  required: get_option('openexr')
+)
+sdl1      = dependency('sdl',         version: '>=1.2.0',
+  required: get_option('sdl1')
+)
+sdl2      = dependency('sdl2',        version: '>=2.0.5',
+  required: get_option('sdl2')
+)
+
+vapigen   = dependency('vapigen',     version:'>=0.20.0',
+  required: get_option('vapigen')
+)
+libwebp   = dependency('libwebp',     version:'>=0.5.0',
+  required: get_option('webp')
+)
+
+pygobject3  = dependency('pygobject-3.0', version: '>=3.2.0',
+  required: get_option('pygobject')
+)
+gobj_introsp = dependency('gobject-introspection-1.0', version: '>=1.32.0',
+  required: get_option('pygobject')
+)
+pygobject_found = pygobject3.found() and gobj_introsp.found()
+
+libavcodec  = dependency('libavcodec',   version: '>=55.69.100',
+  required: get_option('libav')
+)
+libavformat = dependency('libavformat',  version: '>=55.48.100',
+  required: get_option('libav')
+)
+libavutil   = dependency('libavutil',    version: '>=55.92.100',
+  required: get_option('libav')
+)
+libswscale  = dependency('libswscale',   version: '>=2.6.100',
+  required: get_option('libav')
+)
+avlibs_found = (
+  libavcodec.found() and
+  libavformat.found() and
+  libavutil.found() and
+  libswscale.found()
+)
+avlibs = avlibs_found ? [ libavcodec, libavformat, libavutil, libswscale ] : []
+
+libumfpack = cc.find_library('umfpack', required: get_option('umfpack'))
+if libumfpack.found()
+  have_umfpack = cc.has_header('umfpack.h')
+  have_ss_umfpack = cc.has_header_symbol(
+    'suitesparse/umfpack.h',
+    'umfpack_dl_solve'
+  )
+
+  if not (have_umfpack or have_ss_umfpack)
+    if get_option('umfpack').auto()
+      libumfpack = dependency('', required: false)
+    else
+      error('UmfPack library found but not headers.')
+    endif
+  endif
+  config.set('HAVE_UMFPACK_H', have_umfpack)
+  config.set('HAVE_SUITESPARSE_UMFPACK_H', have_ss_umfpack)
+endif
+
+################################################################################
+# Subdirs
+
+configure_file(
+  output: 'config.h',
+  configuration: config
+)
+
+rootInclude = include_directories('.')
+
+argvs_extract = find_program('tools/argvs_extract.sh')
+
+subdir('libs/rgbe')
+subdir('opencl')
+subdir('gegl')
+subdir('libs/npd')
+subdir('seamless-clone')
+subdir('bin')
+subdir('tools')
+subdir('operations')
+subdir('examples')
+subdir('tests')
+subdir('perf')
+subdir('po')
+if get_option('docs')
+  subdir('docs')
+endif
+
+#if w3m.found()
+#  custom_target('README',
+#    input : 'docs/index.html',
+#    output: 'README',
+#    command: [ w3m, '-cols', '72', '-dump', '@INPUT@' ],
+#    capture: true,
+#  )
+#  custom_target('NEWS',
+#    input : 'docs/NEWS.html',
+#    output: 'NEWS',
+#    command: [ w3m, '-cols', '72', '-dump', '@INPUT@' ],
+#    capture: true,
+#  )
+#endif
+
+pkgconfig.generate(filebase: 'gegl-' + api_version,
+  name: 'GEGL',
+  description: 'Generic Graphics Library',
+  version: meson.project_version(),
+  variables: 'pluginsdir=' + '${prefix}' / get_option('libdir') / api_name,
+  requires: [
+    gobject,
+    gmodule,
+    gio,
+    json_glib,
+
+    babl,
+  ],
+  libraries: [
+    gegl_lib,
+    gegl_npd_lib,
+  ],
+  subdirs: api_name,
+)
+
+
+
+
+message('\n'.join(['',
+'Building GEGL with prefix=@0@'.format(get_option('prefix')),
+'',
+'Optional features:',
+'  GEGL docs:       @0@'.format(get_option('docs')),
+'  Build workshop:  @0@'.format(get_option('workshop')),
+'  Build website:   @0@'.format(asciidoc.found()),
+'  SIMD:            sse:@0@ mmx:@1@ sse2:@2@'.format(
+  get_option('enable-sse'),
+  get_option('enable-mmx'),
+  get_option('enable-sse2')
+),
+'  Vala support:    @0@'.format(vapigen.found()),
+'',
+'Optional dependencies:',
+'  asciidoc:        @0@'.format(asciidoc.found()),
+'  enscript:        @0@'.format(enscript.found()),
+'  mrg:             @0@'.format(mrg.found()),
+'  Ruby:            @0@'.format(ruby.found()),
+'  Luajit:          @0@'.format(lua.found()),
+'  Cairo:           @0@'.format(cairo.found()),
+'  Pango:           @0@'.format(pango.found()),
+'  pangocairo:      @0@'.format(pangocairo.found()),
+'  GDKPixbuf:       @0@'.format(gdk_pixbuf.found()),
+'  JPEG:            @0@'.format(libjpeg.found()),
+'  PNG:             @0@'.format(libpng.found()),
+'  OpenEXR:         @0@'.format(openexr.found()),
+'  rsvg:            @0@'.format(librsvg.found()),
+'  SDL:             @0@'.format(sdl1.found()),
+'  libraw:          @0@'.format(libraw.found()),
+'  Jasper:          @0@'.format(jasper.found()),
+'  av libs:         @0@'.format(avlibs_found),
+'  V4L:             @0@'.format(libv4l1.found()),
+'  V4L2:            @0@'.format(libv4l2.found()),
+'  spiro:           @0@'.format(libspiro.found()),
+'  EXIV:            @0@'.format(exiv2.found()),
+'  gexiv2:          @0@'.format(gexiv2.found()),
+'  umfpack:         @0@'.format(libumfpack.found()),
+'  TIFF             @0@'.format(libtiff.found()),
+'  webp:            @0@'.format(libwebp.found()),
+'  poly2tri-c:      @0@ (@1@)'.format(poly2tri_c.found(),poly2tri_c.type_name()),
+'']))
diff --git a/meson_options.txt b/meson_options.txt
new file mode 100644
index 000000000..e9185d668
--- /dev/null
+++ b/meson_options.txt
@@ -0,0 +1,37 @@
+option('enable-mmx',    type: 'boolean', value: true, description: 'enable MMX support')
+option('enable-sse',    type: 'boolean', value: true, description: 'enable SSE support')
+option('enable-sse2',   type: 'boolean', value: true, description: 'enable SSE2 support')
+option('enable-sse4_1', type: 'boolean', value: true, description: 'enable SSE4.1 support')
+option('enable-f16c',   type: 'boolean', value: true, description: 'enable hardware half-float support')
+option('docs',          type: 'boolean', value: 'false')
+option('workshop',      type: 'boolean', value: 'false')
+option('introspection', type: 'boolean', value: 'true')
+
+option('exiv2',         type: 'feature', value: 'auto')
+option('gdk-pixbuf',    type: 'feature', value: 'auto')
+option('gexiv2',        type: 'feature', value: 'auto')
+option('graphviz',      type: 'feature', value: 'auto')
+option('jasper',        type: 'feature', value: 'auto')
+option('lcms',          type: 'feature', value: 'auto')
+option('lensfun',       type: 'feature', value: 'auto')
+option('libav',         type: 'feature', value: 'auto')
+option('libjpeg',       type: 'feature', value: 'auto')
+option('libpng',        type: 'feature', value: 'auto')
+option('libraw',        type: 'feature', value: 'auto')
+option('librsvg',       type: 'feature', value: 'auto')
+option('libspiro',      type: 'feature', value: 'auto')
+option('libtiff',       type: 'feature', value: 'auto')
+option('libv4l',        type: 'feature', value: 'auto')
+option('libv4l2',       type: 'feature', value: 'auto')
+option('lua',           type: 'feature', value: 'auto')
+option('mrg',           type: 'feature', value: 'auto')
+option('openexr',       type: 'feature', value: 'auto')
+option('cairo',         type: 'feature', value: 'auto')
+option('pango',         type: 'feature', value: 'auto')
+option('pangocairo',    type: 'feature', value: 'auto')
+option('pygobject',     type: 'feature', value: 'auto')
+option('sdl1',          type: 'feature', value: 'disabled')
+option('sdl2',          type: 'feature', value: 'auto')
+option('umfpack',       type: 'feature', value: 'auto')
+option('vapigen',       type: 'feature', value: 'auto')
+option('webp',          type: 'feature', value: 'auto')
diff --git a/opencl/cltostring.py b/opencl/cltostring.py
index bf8f46e43..f2831c644 100755
--- a/opencl/cltostring.py
+++ b/opencl/cltostring.py
@@ -4,10 +4,6 @@ from __future__ import print_function
 import os
 import sys
 
-if len(sys.argv) != 2:
-  print("Usage: %s file.cl" % sys.argv[0])
-  sys.exit(1)
-
 # Search for lines that look like #include "blah.h" and replace them
 # with the contents of blah.h.
 def do_includes (source):
@@ -37,8 +33,18 @@ def escape_string(s):
   return result
 
 
-infile  = open(sys.argv[1], "r")
-outfile = open(sys.argv[1] + ".h", "w")
+if len(sys.argv) == 2:
+  infile  = open(sys.argv[1], "r")
+  outfile = open(sys.argv[1] + '.h',  "w")
+
+elif len(sys.argv) == 3:
+  infile  = open(sys.argv[1], "r")
+  outfile = open(sys.argv[2], "w")
+
+else:
+  print("Usage: %s input [output]" % sys.argv[0])
+  sys.exit(1)
+
 
 cl_source = infile.read()
 cl_source = do_includes(cl_source)
@@ -55,4 +61,4 @@ for line in cl_source.rstrip().split("\n"):
   line = '"%-78s\\n"\n' % line
   outfile.write(line)
 outfile.write(";\n")
-outfile.close()
\ No newline at end of file
+outfile.close()
diff --git a/opencl/meson.build b/opencl/meson.build
new file mode 100644
index 000000000..6c5bee0a2
--- /dev/null
+++ b/opencl/meson.build
@@ -0,0 +1,61 @@
+
+opencl_sources = [
+  'alien-map.cl',
+  'bilateral-filter-fast.cl',
+  'bilateral-filter.cl',
+  'box-blur.cl',
+  'box-max.cl',
+  'box-min.cl',
+  'brightness-contrast.cl',
+  'c2g.cl',
+  'checkerboard.cl',
+  'color-exchange.cl',
+  'color-temperature.cl',
+  'color-to-alpha.cl',
+  'colors-8bit-lut.cl',
+  'colors.cl',
+  'contrast-curve.cl',
+  'diffraction-patterns.cl',
+  'edge-laplace.cl',
+  'edge-sobel.cl',
+  'gaussian-blur-selective.cl',
+  'gblur-1d.cl',
+  'hue-chroma.cl',
+  'invert-linear.cl',
+  'levels.cl',
+  'mono-mixer.cl',
+  'motion-blur-circular.cl',
+  'motion-blur-linear.cl',
+  'noise-cell.cl',
+  'noise-hsv.cl',
+  'noise-hurl.cl',
+  'noise-reduction.cl',
+  'noise-simplex.cl',
+  'oilify.cl',
+  'opacity.cl',
+  'pixelize.cl',
+  'posterize.cl',
+  'random.cl',
+  'red-eye-removal.cl',
+  'shadows-highlights-correction.cl',
+  'snn-mean.cl',
+  'stretch-contrast.cl',
+  'svg-src-over.cl',
+  'texturize-canvas.cl',
+  'threshold.cl',
+  'value-invert.cl',
+  'video-degradation.cl',
+  'vignette.cl',
+  'weighted-blend.cl',
+]
+
+cltostring = find_program('cltostring.py')
+
+opencl_headers = files()
+foreach source : opencl_sources
+  opencl_headers += custom_target(source + '.h',
+    input : source,
+    output: source +'.h',
+    command: [ cltostring, '@INPUT@', '@OUTPUT@' ],
+  )
+endforeach
diff --git a/operations/common-cxx/meson.build b/operations/common-cxx/meson.build
new file mode 100644
index 000000000..27c3817ed
--- /dev/null
+++ b/operations/common-cxx/meson.build
@@ -0,0 +1,34 @@
+
+gegl_common_cxx_sources = files(
+  'distance-transform.cc',
+  'warp.cc',
+)
+
+gegl_common_cxx_sources += custom_target('module_common_gpl3.c',
+  input : [ gegl_common_cxx_sources ],
+  output: [ 'module_common_gpl3.c' ],
+  command: [
+    gen_loader,
+    '@INPUT@',
+  ],
+  capture: true
+)
+
+gegl_common_cxx = shared_library('gegl-common-cxx',
+  [ gegl_common_cxx_sources, opencl_headers, ],
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [
+    babl,
+    glib,
+    json_glib,
+    math,
+  ],
+  link_with: [
+    gegl_lib,
+  ],
+  c_args: [   '-DGEGL_OP_BUNDLE', ],
+  cpp_args: [ '-DGEGL_OP_BUNDLE', ],
+  name_prefix: '',
+  install: true,
+  install_dir: get_option('libdir') / api_name,
+)
diff --git a/operations/common-gpl3+/meson.build b/operations/common-gpl3+/meson.build
new file mode 100644
index 000000000..68ed5c5b2
--- /dev/null
+++ b/operations/common-gpl3+/meson.build
@@ -0,0 +1,82 @@
+gegl_common_sources = files(
+  'antialias.c',
+  'apply-lens.c',
+  'bayer-matrix.c',
+  'bump-map.c',
+  'cartoon.c',
+  'channel-mixer.c',
+  'color-exchange.c',
+  'color-to-alpha.c',
+  'cubism.c',
+  'deinterlace.c',
+  'diffraction-patterns.c',
+  'displace.c',
+  'edge-laplace.c',
+  'edge.c',
+  'emboss.c',
+  'engrave.c',
+  'fractal-explorer.c',
+  'fractal-trace.c',
+  'gaussian-blur-selective.c',
+  'illusion.c',
+  'lens-distortion.c',
+  'lens-flare.c',
+  'linear-sinusoid.c',
+  'maze.c',
+  'mosaic.c',
+  'motion-blur-circular.c',
+  'motion-blur-zoom.c',
+  'noise-slur.c',
+  'noise-solid.c',
+  'oilify.c',
+  'photocopy.c',
+  'plasma.c',
+  'polar-coordinates.c',
+  'red-eye-removal.c',
+  'ripple.c',
+  'shadows-highlights-correction.c',
+  'shadows-highlights.c',
+  'shift.c',
+  'sinus.c',
+  'softglow.c',
+  'spiral.c',
+  'supernova.c',
+  'texturize-canvas.c',
+  'tile-glass.c',
+  'tile-paper.c',
+  'value-propagate.c',
+  'video-degradation.c',
+  'waves.c',
+  'whirl-pinch.c',
+  'wind.c',
+)
+
+gegl_common_sources += custom_target('module_common_gpl3.c',
+  input : [ gegl_common_sources ],
+  output: [ 'module_common_gpl3.c' ],
+  command: [
+    gen_loader,
+    '@INPUT@',
+  ],
+  capture: true
+)
+
+gegl_common = shared_library('gegl-common-gpl3',
+  [ gegl_common_sources, opencl_headers, ],
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [
+    babl,
+    glib,
+    json_glib,
+    math,
+  ],
+  link_with: [
+    gegl_lib,
+  ],
+  c_args: [
+    '-DGEGL_OP_BUNDLE',
+  ],
+  name_prefix: '',
+  install: true,
+  install_dir: get_option('libdir') / api_name,
+)
diff --git a/operations/common/meson.build b/operations/common/meson.build
new file mode 100644
index 000000000..20d23e3a9
--- /dev/null
+++ b/operations/common/meson.build
@@ -0,0 +1,144 @@
+gegl_common_sources = files(
+  'absolute.c',
+  'alien-map.c',
+  'bilateral-filter.c',
+  'box-blur.c',
+  'brightness-contrast.c',
+  'buffer-sink.c',
+  'buffer-source.c',
+  'c2g.c',
+  'checkerboard.c',
+  'color-assimilation-grid.c',
+  'color-enhance.c',
+  'color-overlay.c',
+  'color-rotate.c',
+  'color-temperature.c',
+  'color-warp.c',
+  'color.c',
+  'component-extract.c',
+  'contrast-curve.c',
+  'convolution-matrix.c',
+  'copy-buffer.c',
+  'difference-of-gaussians.c',
+  'display.c',
+  'dither.c',
+  'domain-transform.c',
+  'dropshadow.c',
+  'edge-neon.c',
+  'edge-sobel.c',
+  'exp-combine.c',
+  'exposure.c',
+  'fattal02.c',
+  'gaussian-blur.c',
+  'gblur-1d.c',
+  'gegl-buffer-load-op.c',
+  'gegl-buffer-save-op.c',
+  'gegl.c',
+  'grey.c',
+  'grid.c',
+  'high-pass.c',
+  'hue-chroma.c',
+  'icc-save.c',
+  'image-compare.c',
+  'image-gradient.c',
+  'introspect.c',
+  'invert-gamma.c',
+  'invert-linear.c',
+  'layer.c',
+  'levels.c',
+  'linear-gradient.c',
+  'little-planet.c',
+  'long-shadow.c',
+  'magick-load.c',
+  'mantiuk06.c',
+  'map-absolute.c',
+  'map-relative.c',
+  'matting-global.c',
+  'mblur.c',
+  'mean-curvature-blur.c',
+  'median-blur.c',
+  'mirrors.c',
+  'mix.c',
+  'mono-mixer.c',
+  'motion-blur-linear.c',
+  'newsprint.c',
+  'noise-cell.c',
+  'noise-cie-lch.c',
+  'noise-hsv.c',
+  'noise-hurl.c',
+  'noise-perlin.c',
+  'noise-pick.c',
+  'noise-reduction.c',
+  'noise-rgb.c',
+  'noise-simplex.c',
+  'noise-spread.c',
+  'opacity.c',
+  'open-buffer.c',
+  'over.c',
+  'panorama-projection.c',
+  'pixelize.c',
+  'posterize.c',
+  'radial-gradient.c',
+  'rectangle.c',
+  'recursive-transform.c',
+  'reinhard05.c',
+  'remap.c',
+  'rgb-clip.c',
+  'saturation.c',
+  'save.c',
+  'sepia.c',
+  'slic.c',
+  'snn-mean.c',
+  'spherize.c',
+  'stress.c',
+  'stretch-contrast-hsv.c',
+  'stretch-contrast.c',
+  'svg-huerotate.c',
+  'svg-luminancetoalpha.c',
+  'svg-matrix.c',
+  'svg-saturate.c',
+  'threshold.c',
+  'tile-seamless.c',
+  'tile.c',
+  'unpremultiply.c',
+  'unsharp-mask.c',
+  'value-invert.c',
+  'vignette.c',
+  'waterpixels.c',
+  'watershed-transform.c',
+  'wavelet-blur-1d.c',
+  'wavelet-blur.c',
+  'weighted-blend.c',
+  'write-buffer.c',
+)
+
+gegl_common_sources += custom_target('module_common.c',
+  input : [ gegl_common_sources ],
+  output: [ 'module_common.c' ],
+  command: [
+    gen_loader,
+    '@INPUT@',
+  ],
+  capture: true
+)
+
+gegl_common = shared_library('gegl-common',
+  gegl_common_sources,
+  opencl_headers,
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [
+    babl,
+    glib,
+    json_glib,
+    math,
+  ],
+  link_with: [
+    gegl_lib,
+  ],
+  c_args: [
+    '-DGEGL_OP_BUNDLE',
+  ],
+  name_prefix: '',
+  install: true,
+  install_dir: get_option('libdir') / api_name,
+)
diff --git a/operations/core/meson.build b/operations/core/meson.build
new file mode 100644
index 000000000..ceac1fe88
--- /dev/null
+++ b/operations/core/meson.build
@@ -0,0 +1,39 @@
+
+gegl_core_sources = files(
+  'cache.c',
+  'cast-format.c',
+  'clone.c',
+  'convert-format.c',
+  'crop.c',
+  'json.c',
+  'load.c',
+  'nop.c',
+)
+
+gegl_core_sources += custom_target('module_core.c',
+  input : gegl_core_sources,
+  output: 'module_core.c',
+  command: [ gen_loader, '@INPUT@', ],
+  capture: true
+)
+
+gegl_core = shared_library('gegl-core',
+  gegl_core_sources,
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [
+    babl,
+    glib,
+    gmodule,
+    json_glib,
+    math,
+  ],
+  link_with: [
+    gegl_lib,
+  ],
+  c_args: [
+    '-DGEGL_OP_BUNDLE',
+  ],
+  name_prefix: '',
+  install: true,
+  install_dir: get_option('libdir') / api_name,
+)
diff --git a/operations/external/meson.build b/operations/external/meson.build
new file mode 100644
index 000000000..4e54bc6ef
--- /dev/null
+++ b/operations/external/meson.build
@@ -0,0 +1,123 @@
+
+operations = [
+  { 'name': 'ppm-load' },
+  { 'name': 'ppm-save' },
+  { 'name': 'npy-save' },
+  { 'name': 'rgbe-load', 'deps': librgbe },
+  { 'name': 'rgbe-save', 'deps': librgbe },
+  { 'name': 'gif-load',  'deps': libnsgif },
+]
+
+
+if pangocairo.found()
+  operations += { 'name': 'text', 'deps': pangocairo }
+endif
+
+if cairo.found()
+  operations += [
+    { 'name': 'path',           'deps': cairo },
+    { 'name': 'vector-fill',    'deps': cairo },
+    { 'name': 'vector-stroke',  'deps': cairo },
+    { 'name': 'npd',            'deps': [ cairo, libnpd ] },
+  ]
+endif
+
+if libpng.found()
+  operations += [
+    { 'name': 'png-load', 'deps': libpng },
+    { 'name': 'png-save', 'deps': libpng },
+  ]
+endif
+
+if libjpeg.found()
+  operations += [
+    { 'name': 'jpg-load', 'deps': libjpeg },
+    { 'name': 'jpg-save', 'deps': libjpeg },
+  ]
+endif
+
+if cairo.found() and librsvg.found()
+  operations += [
+    { 'name': 'svg-load', 'deps': [ librsvg, cairo ], },
+  ]
+endif
+
+if gdk_pixbuf.found()
+  operations += [
+    { 'name': 'pixbuf-load',  'srcs': 'pixbuf.c',      'deps': gdk_pixbuf },
+    { 'name': 'pixbuf-save',  'srcs': 'save-pixbuf.c', 'deps': gdk_pixbuf },
+  ]
+endif
+
+if openexr.found()
+  operations += [
+    { 'name': 'exr-load',   'srcs': 'exr-load.cpp', 'deps': openexr },
+    { 'name': 'exr-save',   'srcs': 'exr-save.cc',  'deps': openexr },
+  ]
+endif
+
+if sdl1.found()
+  operations += { 'name': 'sdl-display', 'deps': sdl1 }
+endif
+
+if libraw.found()
+  operations += { 'name': 'raw-load', 'deps': libraw }
+endif
+
+if libv4l1.found()
+  operations += { 'name': 'v4l', 'deps': [ libv4l1, thread ], }
+endif
+
+if jasper.found()
+  operations += { 'name': 'jp2-load', 'deps': jasper }
+endif
+
+if avlibs_found
+  operations += [
+    { 'name': 'ff-load', 'deps': avlibs },
+    { 'name': 'ff-save', 'deps': avlibs },
+  ]
+endif
+
+if libumfpack.found()
+  operations += {
+    'name': 'matting-levin',
+    'srcs': [ 'matting-levin.c', 'matting-levin-cblas.c', ],
+    'deps': libumfpack
+  }
+endif
+
+if lcms.found()
+  operations += { 'name': 'lcms-from-profile', 'deps': lcms }
+endif
+
+if libtiff.found()
+  operations += [
+    { 'name': 'tiff-load', 'deps': libtiff },
+    { 'name': 'tiff-save', 'deps': libtiff },
+  ]
+endif
+
+if libwebp.found()
+  operations += [
+    { 'name': 'webp-load', 'deps': libwebp },
+    { 'name': 'webp-save', 'deps': libwebp },
+  ]
+endif
+
+
+foreach operation : operations
+  libname = operation.get('name')
+  libsrcs = operation.get('srcs', libname + '.c')
+  libdeps = operation.get('deps', [])
+
+  shared_library(libname,
+    libsrcs,
+    dependencies: [ babl, gio, glib, gobject, math, libdeps, ],
+    include_directories: [ rootInclude, geglInclude, ],
+    link_with: [ gegl_lib, ],
+    name_prefix: '',
+    install: true,
+    install_dir: get_option('libdir') / api_name,
+  )
+endforeach
diff --git a/operations/generated/meson.build b/operations/generated/meson.build
new file mode 100644
index 000000000..2a654656c
--- /dev/null
+++ b/operations/generated/meson.build
@@ -0,0 +1,56 @@
+gegl_generated_sources = files(
+  'add.c',
+  'clear.c',
+  'color-burn.c',
+  'color-dodge.c',
+  'darken.c',
+  'difference.c',
+  'divide.c',
+  'dst-atop.c',
+  'dst-in.c',
+  'dst-out.c',
+  'dst-over.c',
+  'dst.c',
+  'exclusion.c',
+  'gamma.c',
+  'hard-light.c',
+  'lighten.c',
+  'multiply.c',
+  'overlay.c',
+  'plus.c',
+  'screen.c',
+  'soft-light.c',
+  'src-atop.c',
+  'src-in.c',
+  'src-out.c',
+  'src.c',
+  'subtract.c',
+  'xor.c',
+)
+
+gegl_generated_sources += custom_target('module_generated_gpl3.c',
+  input : gegl_generated_sources,
+  output: 'module_generated_gpl3.c',
+  command: [ gen_loader, '@INPUT@', ],
+  capture: true
+)
+
+gegl_generated = shared_library('gegl-generated',
+  gegl_generated_sources,
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [
+    babl,
+    glib,
+    json_glib,
+    math,
+  ],
+  link_with: [
+    gegl_lib,
+  ],
+  c_args: [
+    '-DGEGL_OP_BUNDLE',
+  ],
+  name_prefix: '',
+  install: true,
+  install_dir: get_option('libdir') / api_name,
+)
diff --git a/operations/json/meson.build b/operations/json/meson.build
new file mode 100644
index 000000000..1d861ecca
--- /dev/null
+++ b/operations/json/meson.build
@@ -0,0 +1,3 @@
+install_data('grey2.json',
+  install_dir: get_option('libdir') / api_name,
+)
diff --git a/operations/meson.build b/operations/meson.build
new file mode 100644
index 000000000..e2fec07d9
--- /dev/null
+++ b/operations/meson.build
@@ -0,0 +1,13 @@
+subdir('common-gpl3+')
+subdir('common-cxx')
+subdir('common')
+subdir('core')
+subdir('external')
+subdir('generated')
+subdir('json')
+subdir('seamless-clone')
+subdir('transform')
+
+if get_option('workshop')
+  subdir('workshop')
+endif
diff --git a/operations/seamless-clone/meson.build b/operations/seamless-clone/meson.build
new file mode 100644
index 000000000..27ee947f7
--- /dev/null
+++ b/operations/seamless-clone/meson.build
@@ -0,0 +1,26 @@
+
+seamless_clone_libs = [
+  'seamless-clone',
+  'seamless-clone-compose',
+]
+
+foreach lib : seamless_clone_libs
+  shared_library(lib,
+    files(lib + '.c'),
+    include_directories: [ rootInclude, geglInclude, seamlessInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      json_glib,
+      poly2tri_c,
+      math,
+    ],
+    link_with: [
+      gegl_lib,
+      seamlessclone_lib,
+    ],
+    name_prefix: '',
+    install: true,
+    install_dir: get_option('libdir') / api_name,
+  )
+endforeach
diff --git a/operations/transform/meson.build b/operations/transform/meson.build
new file mode 100644
index 000000000..4e0fc2a2d
--- /dev/null
+++ b/operations/transform/meson.build
@@ -0,0 +1,31 @@
+gegl_transformops_sources = files(
+  'module.c',
+  'reflect.c',
+  'rotate-on-center.c',
+  'rotate.c',
+  'scale-ratio.c',
+  'scale-size-keepaspect.c',
+  'scale-size.c',
+  'scale.c',
+  'shear.c',
+  'transform-core.c',
+  'transform.c',
+  'translate.c',
+)
+
+gegl_transformops = shared_library('transformops',
+  gegl_transformops_sources,
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [
+    babl,
+    glib,
+    json_glib,
+    math,
+  ],
+  link_with: [
+    gegl_lib,
+  ],
+  name_prefix: '',
+  install: true,
+  install_dir: get_option('libdir') / api_name,
+)
diff --git a/operations/workshop/external/meson.build b/operations/workshop/external/meson.build
new file mode 100644
index 000000000..94e8322db
--- /dev/null
+++ b/operations/workshop/external/meson.build
@@ -0,0 +1,80 @@
+
+if lua.found()
+  shared_library('gluas',
+    'gluas.c',
+    include_directories: [ rootInclude, geglInclude, seamlessInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+      lua,
+      math,
+    ],
+    link_with: [
+      gegl_lib,
+    ],
+    name_prefix: '',
+    install: true,
+    install_dir: get_option('libdir') / api_name,
+  )
+endif
+
+if lensfun.found()
+  shared_library('lens-correct',
+    'lens-correct.c',
+    include_directories: [ rootInclude, geglInclude, seamlessInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+      lensfun,
+      math,
+    ],
+    link_with: [
+      gegl_lib,
+    ],
+    name_prefix: '',
+    install: true,
+    install_dir: get_option('libdir') / api_name,
+  )
+endif
+
+if cairo.found()
+  shared_library('line-profile',
+    'line-profile.c',
+    include_directories: [ rootInclude, geglInclude, seamlessInclude, ],
+    dependencies: [
+      babl,
+      cairo,
+      glib,
+      gobject,
+      math,
+    ],
+    link_with: [
+      gegl_lib,
+    ],
+    name_prefix: '',
+    install: true,
+    install_dir: get_option('libdir') / api_name,
+  )
+endif
+
+if libv4l2.found()
+  shared_library('v4l2',
+    'v4l2.c',
+    include_directories: [ rootInclude, geglInclude, seamlessInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+      libv4l2,
+      math,
+    ],
+    link_with: [
+      gegl_lib,
+    ],
+    name_prefix: '',
+    install: true,
+    install_dir: get_option('libdir') / api_name,
+  )
+endif
diff --git a/operations/workshop/generated/meson.build b/operations/workshop/generated/meson.build
new file mode 100644
index 000000000..fd463db54
--- /dev/null
+++ b/operations/workshop/generated/meson.build
@@ -0,0 +1,27 @@
+
+libraries = [
+  'average',
+  'blend-reflect',
+  'negation',
+  'soft-burn',
+  'soft-dodge',
+  'subtractive',
+]
+
+foreach lib : libraries
+  shared_library(lib,
+    lib + '.c',
+    include_directories: [ rootInclude, geglInclude, seamlessInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+    ],
+    link_with: [
+      gegl_lib,
+    ],
+    name_prefix: '',
+    install: true,
+    install_dir: get_option('libdir') / api_name,
+  )
+endforeach
diff --git a/operations/workshop/meson.build b/operations/workshop/meson.build
new file mode 100644
index 000000000..688d62fe0
--- /dev/null
+++ b/operations/workshop/meson.build
@@ -0,0 +1,45 @@
+subdir('external')
+subdir('generated')
+
+libraries = [
+  { 'name': 'aces-rrt', },
+  { 'name': 'alpha-inpaint', },
+  { 'name': 'bilateral-filter-fast', },
+  { 'name': 'boxblur-1d', },
+  { 'name': 'boxblur', },
+  { 'name': 'connected-components', },
+  { 'name': 'demosaic-bimedian', },
+  { 'name': 'demosaic-simple', },
+  { 'name': 'ditto', },
+  { 'name': 'gcr', },
+  { 'name': 'gradient-map', },
+  { 'name': 'hstack', },
+  { 'name': 'integral-image', },
+  { 'name': 'rawbayer-load', },
+  { 'name': 'segment-kmeans', },
+  { 'name': 'selective-hue-saturation', },
+  { 'name': 'voronoi-diagram', 'srcs': 'voronoi-diagram.cc', },
+]
+
+foreach lib : libraries
+  libname = lib.get('name')
+  libsrcs = lib.get('srcs', libname + '.c')
+  shared_library(libname,
+    libsrcs,
+    opencl_headers,
+    include_directories: [ rootInclude, geglInclude, seamlessInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+      math,
+    ],
+    link_with: [
+      gegl_lib,
+      seamlessclone_lib,
+    ],
+    name_prefix: '',
+    install: true,
+    install_dir: get_option('libdir') / api_name,
+  )
+endforeach
diff --git a/perf/meson.build b/perf/meson.build
new file mode 100644
index 000000000..290038c01
--- /dev/null
+++ b/perf/meson.build
@@ -0,0 +1,42 @@
+
+perf_tests = [
+  'bcontrast-4x',
+  'bcontrast-minichunk',
+  'bcontrast',
+  'blur',
+  'gegl-buffer-access',
+  'init',
+  'rotate',
+  'samplers',
+  'saturation',
+  'scale',
+  'translate',
+  'unsharpmask',
+]
+
+foreach testname : perf_tests
+  perf_test_exe = executable(testname,
+    'test-' + testname + '.c',
+    include_directories: [ rootInclude, geglInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+    ],
+    link_with: [ gegl_lib, ],
+    c_args: [
+      '-DG_DISABLE_SINGLE_INCLUDES',
+      '-DGLIB_DISABLE_DEPRECATION_WARNINGS',
+      '-DCLUTTER_DISABLE_DEPRECATION_WARNINGS',
+      '-DTESTS_DATA_DIR="@0@"'.format( meson.source_root() / 'tests' / 'data'),
+    ],
+    install: false,
+  )
+
+  benchmark('Perf Test ' + testname, perf_test_exe,
+    env: [
+      'GEGL_PATH=' + meson.build_root() / 'operations',
+      'GEGL_USE_OPENCL=no',
+    ],
+  )
+endforeach
diff --git a/po/meson.build b/po/meson.build
new file mode 100644
index 000000000..d2a005f83
--- /dev/null
+++ b/po/meson.build
@@ -0,0 +1 @@
+i18n.gettext(gettext_package)
diff --git a/seamless-clone/meson.build b/seamless-clone/meson.build
new file mode 100644
index 000000000..d67e279a9
--- /dev/null
+++ b/seamless-clone/meson.build
@@ -0,0 +1,55 @@
+
+seamlessInclude = include_directories('.')
+
+seamlessclone_sources = [
+  'sc-context.c',
+  'sc-outline.c',
+  'sc-sample.c',
+]
+
+seamlessclone_headers = [
+  'sc-common.h',
+  'sc-context.h',
+  'sc-outline.h',
+  'sc-sample.h',
+]
+
+seamlessclone_lib = library('gegl-sc-' + api_version,
+  seamlessclone_sources,
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [
+    babl,
+    glib,
+    gobject,
+    poly2tri_c,
+  ],
+  link_with: [
+    gegl_lib,
+  ],
+  install: true,
+)
+
+install_headers(
+  seamlessclone_headers,
+  subdir: api_name / 'sc'
+)
+
+
+pkgconfig.generate(
+  filebase: 'gegl-sc-' + api_version,
+  name: 'GEGL Seamless Cloning',
+  description: 'Seamless Cloning Library Based on GEGL',
+  version: meson.project_version(),
+  requires: [
+    gobject,
+    gmodule,
+    gio,
+    json_glib,
+
+    babl,
+    poly2tri_c.type_name() == 'internal' ? [] : poly2tri_c,
+    'gegl-' + api_version,
+  ],
+  libraries: seamlessclone_lib,
+  subdirs: api_name / 'sc',
+)
diff --git a/subprojects/libnsgif/COPYING b/subprojects/libnsgif/COPYING
new file mode 100644
index 000000000..c6e1688d7
--- /dev/null
+++ b/subprojects/libnsgif/COPYING
@@ -0,0 +1,20 @@
+Copyright (C) 2004 Richard Wilson
+Copyright (C) 2008 Sean Fox
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+  * The above copyright notice and this permission notice shall be included in
+    all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.  IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/subprojects/libnsgif/README b/subprojects/libnsgif/README
new file mode 100644
index 000000000..498ee4663
--- /dev/null
+++ b/subprojects/libnsgif/README
@@ -0,0 +1,36 @@
+libnsgif - Decoding GIF files
+=============================
+
+The functions provided by this library allow for efficient progressive
+GIF decoding. Whilst the initialisation does not ensure that there is
+sufficient image data to complete the entire frame, it does ensure
+that the information provided is valid. Any subsequent attempts to
+decode an initialised GIF are guaranteed to succeed, and any bytes of
+the image not present are assumed to be totally transparent.
+
+To begin decoding a GIF, the 'gif' structure must be initialised with
+the 'gif_data' and 'buffer_size' set to their initial values. The
+'buffer_position' should initially be 0, and will be internally
+updated as the decoding commences. The caller should then repeatedly
+call gif_initialise() with the structure until the function returns 1,
+or no more data is avaliable.
+
+Once the initialisation has begun, the decoder completes the variables
+'frame_count' and 'frame_count_partial'. The former being the total
+number of frames that have been successfully initialised, and the
+latter being the number of frames that a partial amount of data is
+available for. This assists the caller in managing the animation
+whilst decoding is continuing.
+
+To decode a frame, the caller must use gif_decode_frame() which
+updates the current 'frame_image' to reflect the desired frame. The
+required 'disposal_method' is also updated to reflect how the frame
+should be plotted. The caller must not assume that the current
+'frame_image' will be valid between calls if initialisation is still
+occuring, and should either always request that the frame is decoded
+(no processing will occur if the 'decoded_frame' has not been
+invalidated by initialisation) or perform the check itself.
+
+It should be noted that gif_finalise() should always be called, even
+if no frames were initialised.  Additionally, it is the responsibility
+of the caller to free 'gif_data'.
diff --git a/subprojects/libnsgif/libnsgif.c b/subprojects/libnsgif/libnsgif.c
new file mode 100644
index 000000000..7865c1471
--- /dev/null
+++ b/subprojects/libnsgif/libnsgif.c
@@ -0,0 +1,1169 @@
+/*
+ * Copyright 2004 Richard Wilson <richard wilson netsurf-browser org>
+ * Copyright 2008 Sean Fox <dyntryx gmail com>
+ *
+ * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
+ * Licenced under the MIT License,
+ *                http://www.opensource.org/licenses/mit-license.php
+ */
+
+#include <stdbool.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <stdlib.h>
+#include <assert.h>
+#include "libnsgif.h"
+#include "log.h"
+
+#include "lzw.h"
+
+/**
+ *
+ * \file
+ * \brief GIF image decoder
+ *
+ * The GIF format is thoroughly documented; a full description can be found at
+ * http://www.w3.org/Graphics/GIF/spec-gif89a.txt
+ *
+ * \todo Plain text and comment extensions should be implemented.
+ */
+
+
+/** Maximum colour table size */
+#define GIF_MAX_COLOURS 256
+
+/** Internal flag that the colour table needs to be processed */
+#define GIF_PROCESS_COLOURS 0xaa000000
+
+/** Internal flag that a frame is invalid/unprocessed */
+#define GIF_INVALID_FRAME -1
+
+/** Transparent colour */
+#define GIF_TRANSPARENT_COLOUR 0x00
+
+/* GIF Flags */
+#define GIF_FRAME_COMBINE 1
+#define GIF_FRAME_CLEAR 2
+#define GIF_FRAME_RESTORE 3
+#define GIF_FRAME_QUIRKS_RESTORE 4
+
+#define GIF_IMAGE_SEPARATOR 0x2c
+#define GIF_INTERLACE_MASK 0x40
+#define GIF_COLOUR_TABLE_MASK 0x80
+#define GIF_COLOUR_TABLE_SIZE_MASK 0x07
+#define GIF_EXTENSION_INTRODUCER 0x21
+#define GIF_EXTENSION_GRAPHIC_CONTROL 0xf9
+#define GIF_DISPOSAL_MASK 0x1c
+#define GIF_TRANSPARENCY_MASK 0x01
+#define GIF_EXTENSION_COMMENT 0xfe
+#define GIF_EXTENSION_PLAIN_TEXT 0x01
+#define GIF_EXTENSION_APPLICATION 0xff
+#define GIF_BLOCK_TERMINATOR 0x00
+#define GIF_TRAILER 0x3b
+
+/** standard GIF header size */
+#define GIF_STANDARD_HEADER_SIZE 13
+
+
+/**
+ * Updates the sprite memory size
+ *
+ * \param gif The animation context
+ * \param width The width of the sprite
+ * \param height The height of the sprite
+ * \return GIF_INSUFFICIENT_MEMORY for a memory error GIF_OK for success
+ */
+static gif_result
+gif_initialise_sprite(gif_animation *gif,
+                      unsigned int width,
+                      unsigned int height)
+{
+        unsigned int max_width;
+        unsigned int max_height;
+        struct bitmap *buffer;
+
+        /* Check if we've changed */
+        if ((width <= gif->width) && (height <= gif->height)) {
+                return GIF_OK;
+        }
+
+        /* Get our maximum values */
+        max_width = (width > gif->width) ? width : gif->width;
+        max_height = (height > gif->height) ? height : gif->height;
+
+        /* Allocate some more memory */
+        assert(gif->bitmap_callbacks.bitmap_create);
+        buffer = gif->bitmap_callbacks.bitmap_create(max_width, max_height);
+        if (buffer == NULL) {
+                return GIF_INSUFFICIENT_MEMORY;
+        }
+
+        assert(gif->bitmap_callbacks.bitmap_destroy);
+        gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);
+        gif->frame_image = buffer;
+        gif->width = max_width;
+        gif->height = max_height;
+
+        /* Invalidate our currently decoded image */
+        gif->decoded_frame = GIF_INVALID_FRAME;
+        return GIF_OK;
+}
+
+
+/**
+ * Attempts to initialise the frame's extensions
+ *
+ * \param gif The animation context
+ * \param frame The frame number
+ * @return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the
+ *         frame GIF_OK for successful initialisation.
+ */
+static gif_result
+gif_initialise_frame_extensions(gif_animation *gif, const int frame)
+{
+        unsigned char *gif_data, *gif_end;
+        int gif_bytes;
+        unsigned int block_size;
+
+        /* Get our buffer position etc.        */
+        gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
+        gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
+
+        /* Initialise the extensions */
+        while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+                ++gif_data;
+                if ((gif_bytes = (gif_end - gif_data)) < 1) {
+                        return GIF_INSUFFICIENT_FRAME_DATA;
+                }
+
+                /* Switch on extension label */
+                switch (gif_data[0]) {
+                case GIF_EXTENSION_GRAPHIC_CONTROL:
+                        /* 6-byte Graphic Control Extension is:
+                         *
+                         *     +0      CHAR    Graphic Control Label
+                         *     +1      CHAR    Block Size
+                         *     +2      CHAR    __Packed Fields__
+                         *                     3BITS   Reserved
+                         *                     3BITS   Disposal Method
+                         *                     1BIT    User Input Flag
+                         *                     1BIT    Transparent Color Flag
+                         *     +3      SHORT   Delay Time
+                         *     +5      CHAR    Transparent Color Index
+                         */
+                        if (gif_bytes < 6) {
+                                return GIF_INSUFFICIENT_FRAME_DATA;
+                        }
+
+                        gif->frames[frame].frame_delay = gif_data[3] | (gif_data[4] << 8);
+                        if (gif_data[2] & GIF_TRANSPARENCY_MASK) {
+                                gif->frames[frame].transparency = true;
+                                gif->frames[frame].transparency_index = gif_data[5];
+                        }
+                        gif->frames[frame].disposal_method = ((gif_data[2] & GIF_DISPOSAL_MASK) >> 2);
+                        /* I have encountered documentation and GIFs in the
+                         * wild that use 0x04 to restore the previous frame,
+                         * rather than the officially documented 0x03.  I
+                         * believe some (older?)  software may even actually
+                         * export this way.  We handle this as a type of
+                         * "quirks" mode.
+                         */
+                        if (gif->frames[frame].disposal_method == GIF_FRAME_QUIRKS_RESTORE) {
+                                gif->frames[frame].disposal_method = GIF_FRAME_RESTORE;
+                        }
+                        gif_data += (2 + gif_data[1]);
+                        break;
+
+                case GIF_EXTENSION_APPLICATION:
+                        /* 14-byte+ Application Extension is:
+                         *
+                         *     +0    CHAR    Application Extension Label
+                         *     +1    CHAR    Block Size
+                         *     +2    8CHARS  Application Identifier
+                         *     +10   3CHARS  Appl. Authentication Code
+                         *     +13   1-256   Application Data (Data sub-blocks)
+                         */
+                        if (gif_bytes < 17) {
+                                return GIF_INSUFFICIENT_FRAME_DATA;
+                        }
+                        if ((gif_data[1] == 0x0b) &&
+                            (strncmp((const char *) gif_data + 2,
+                                     "NETSCAPE2.0", 11) == 0) &&
+                            (gif_data[13] == 0x03) &&
+                            (gif_data[14] == 0x01)) {
+                                gif->loop_count = gif_data[15] | (gif_data[16] << 8);
+                        }
+                        gif_data += (2 + gif_data[1]);
+                        break;
+
+                case GIF_EXTENSION_COMMENT:
+                        /* Move the pointer to the first data sub-block Skip 1
+                         * byte for the extension label
+                         */
+                        ++gif_data;
+                        break;
+
+                default:
+                        /* Move the pointer to the first data sub-block Skip 2
+                         * bytes for the extension label and size fields Skip
+                         * the extension size itself
+                         */
+                        if (gif_bytes < 2) {
+                                return GIF_INSUFFICIENT_FRAME_DATA;
+                        }
+                        gif_data += (2 + gif_data[1]);
+                }
+
+                /* Repeatedly skip blocks until we get a zero block or run out
+                 * of data This data is ignored by this gif decoder
+                 */
+                gif_bytes = (gif_end - gif_data);
+                block_size = 0;
+                while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
+                        block_size = gif_data[0] + 1;
+                        if ((gif_bytes -= block_size) < 0) {
+                                return GIF_INSUFFICIENT_FRAME_DATA;
+                        }
+                        gif_data += block_size;
+                }
+                ++gif_data;
+        }
+
+        /* Set buffer position and return */
+        gif->buffer_position = (gif_data - gif->gif_data);
+        return GIF_OK;
+}
+
+
+/**
+ * Attempts to initialise the next frame
+ *
+ * \param gif The animation context
+ * \return error code
+ *         - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ *         - GIF_FRAME_DATA_ERROR for GIF frame data error
+ *         - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ *         - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
+ *         - GIF_DATA_ERROR for GIF error (invalid frame header)
+ *         - GIF_OK for successful decoding
+ *         - GIF_WORKING for successful decoding if more frames are expected
+*/
+static gif_result gif_initialise_frame(gif_animation *gif)
+{
+        int frame;
+        gif_frame *temp_buf;
+
+        unsigned char *gif_data, *gif_end;
+        int gif_bytes;
+        unsigned int flags = 0;
+        unsigned int width, height, offset_x, offset_y;
+        unsigned int block_size, colour_table_size;
+        bool first_image = true;
+        gif_result return_value;
+
+        /* Get the frame to decode and our data position */
+        frame = gif->frame_count;
+
+        /* Get our buffer position etc. */
+        gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
+        gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
+        gif_bytes = (gif_end - gif_data);
+
+        /* Check if we've finished */
+        if ((gif_bytes > 0) && (gif_data[0] == GIF_TRAILER)) {
+                return GIF_OK;
+        }
+
+        /* Check if there is enough data remaining. The shortest block of data
+         * is a 4-byte comment extension + 1-byte block terminator + 1-byte gif
+         * trailer
+         */
+        if (gif_bytes < 6) {
+                return GIF_INSUFFICIENT_DATA;
+        }
+
+        /* We could theoretically get some junk data that gives us millions of
+         * frames, so we ensure that we don't have a silly number
+         */
+        if (frame > 4096) {
+                return GIF_FRAME_DATA_ERROR;
+        }
+
+        /* Get some memory to store our pointers in etc. */
+        if ((int)gif->frame_holders <= frame) {
+                /* Allocate more memory */
+                temp_buf = (gif_frame *)realloc(gif->frames, (frame + 1) * sizeof(gif_frame));
+                if (temp_buf == NULL) {
+                        return GIF_INSUFFICIENT_MEMORY;
+                }
+                gif->frames = temp_buf;
+                gif->frame_holders = frame + 1;
+        }
+
+        /* Store our frame pointer. We would do it when allocating except we
+         * start off with one frame allocated so we can always use realloc.
+         */
+        gif->frames[frame].frame_pointer = gif->buffer_position;
+        gif->frames[frame].display = false;
+        gif->frames[frame].virgin = true;
+        gif->frames[frame].disposal_method = 0;
+        gif->frames[frame].transparency = false;
+        gif->frames[frame].frame_delay = 100;
+        gif->frames[frame].redraw_required = false;
+
+        /* Invalidate any previous decoding we have of this frame */
+        if (gif->decoded_frame == frame) {
+                gif->decoded_frame = GIF_INVALID_FRAME;
+        }
+
+        /* We pretend to initialise the frames, but really we just skip over
+         * all the data contained within. This is all basically a cut down
+         * version of gif_decode_frame that doesn't have any of the LZW bits in
+         * it.
+         */
+
+        /* Initialise any extensions */
+        gif->buffer_position = gif_data - gif->gif_data;
+        return_value = gif_initialise_frame_extensions(gif, frame);
+        if (return_value != GIF_OK) {
+                return return_value;
+        }
+        gif_data = (gif->gif_data + gif->buffer_position);
+        gif_bytes = (gif_end - gif_data);
+
+        /* Check if we've finished */
+        if ((gif_bytes = (gif_end - gif_data)) < 1) {
+                return GIF_INSUFFICIENT_FRAME_DATA;
+        }
+
+        if (gif_data[0] == GIF_TRAILER) {
+                gif->buffer_position = (gif_data - gif->gif_data);
+                gif->frame_count = frame + 1;
+                return GIF_OK;
+        }
+
+        /* If we're not done, there should be an image descriptor */
+        if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
+                return GIF_FRAME_DATA_ERROR;
+        }
+
+        /* Do some simple boundary checking */
+        if (gif_bytes < 10) {
+                return GIF_INSUFFICIENT_FRAME_DATA;
+        }
+        offset_x = gif_data[1] | (gif_data[2] << 8);
+        offset_y = gif_data[3] | (gif_data[4] << 8);
+        width = gif_data[5] | (gif_data[6] << 8);
+        height = gif_data[7] | (gif_data[8] << 8);
+
+        /* Set up the redraw characteristics. We have to check for extending
+         * the area due to multi-image frames.
+         */
+        if (!first_image) {
+                if (gif->frames[frame].redraw_x > offset_x) {
+                        gif->frames[frame].redraw_width += (gif->frames[frame].redraw_x - offset_x);
+                        gif->frames[frame].redraw_x = offset_x;
+                }
+
+                if (gif->frames[frame].redraw_y > offset_y) {
+                        gif->frames[frame].redraw_height += (gif->frames[frame].redraw_y - offset_y);
+                        gif->frames[frame].redraw_y = offset_y;
+                }
+
+                if ((offset_x + width) > (gif->frames[frame].redraw_x + gif->frames[frame].redraw_width)) {
+                        gif->frames[frame].redraw_width = (offset_x + width) - gif->frames[frame].redraw_x;
+                }
+
+                if ((offset_y + height) > (gif->frames[frame].redraw_y + gif->frames[frame].redraw_height)) {
+                        gif->frames[frame].redraw_height = (offset_y + height) - gif->frames[frame].redraw_y;
+                }
+        } else {
+                first_image = false;
+                gif->frames[frame].redraw_x = offset_x;
+                gif->frames[frame].redraw_y = offset_y;
+                gif->frames[frame].redraw_width = width;
+                gif->frames[frame].redraw_height = height;
+        }
+
+        /* if we are clearing the background then we need to redraw enough to
+         * cover the previous frame too
+         */
+        gif->frames[frame].redraw_required = ((gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) ||
+                                                (gif->frames[frame].disposal_method == GIF_FRAME_RESTORE));
+
+        /* Boundary checking - shouldn't ever happen except with junk data */
+        if (gif_initialise_sprite(gif, (offset_x + width), (offset_y + height))) {
+                return GIF_INSUFFICIENT_MEMORY;
+        }
+
+        /* Decode the flags */
+        flags = gif_data[9];
+        colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
+
+        /* Move our data onwards and remember we've got a bit of this frame */
+        gif_data += 10;
+        gif_bytes = (gif_end - gif_data);
+        gif->frame_count_partial = frame + 1;
+
+        /* Skip the local colour table */
+        if (flags & GIF_COLOUR_TABLE_MASK) {
+                gif_data += 3 * colour_table_size;
+                if ((gif_bytes = (gif_end - gif_data)) < 0) {
+                        return GIF_INSUFFICIENT_FRAME_DATA;
+                }
+        }
+
+        /* Ensure we have a correct code size */
+        if (gif_bytes < 1) {
+                return GIF_INSUFFICIENT_FRAME_DATA;
+        }
+        if (gif_data[0] > LZW_CODE_MAX) {
+                return GIF_DATA_ERROR;
+        }
+
+        /* Move our pointer to the actual image data */
+        gif_data++;
+        --gif_bytes;
+
+        /* Repeatedly skip blocks until we get a zero block or run out of data
+         * These blocks of image data are processed later by gif_decode_frame()
+         */
+        block_size = 0;
+        while (block_size != 1) {
+                if (gif_bytes < 1) return GIF_INSUFFICIENT_FRAME_DATA;
+                block_size = gif_data[0] + 1;
+                /* Check if the frame data runs off the end of the file        */
+                if ((int)(gif_bytes - block_size) < 0) {
+                        /* Try to recover by signaling the end of the gif.
+                         * Once we get garbage data, there is no logical way to
+                         * determine where the next frame is.  It's probably
+                         * better to partially load the gif than not at all.
+                         */
+                        if (gif_bytes >= 2) {
+                                gif_data[0] = 0;
+                                gif_data[1] = GIF_TRAILER;
+                                gif_bytes = 1;
+                                ++gif_data;
+                                break;
+                        } else {
+                                return GIF_INSUFFICIENT_FRAME_DATA;
+                        }
+                } else {
+                        gif_bytes -= block_size;
+                        gif_data += block_size;
+                }
+        }
+
+        /* Add the frame and set the display flag */
+        gif->buffer_position = gif_data - gif->gif_data;
+        gif->frame_count = frame + 1;
+        gif->frames[frame].display = true;
+
+        /* Check if we've finished */
+        if (gif_bytes < 1) {
+                return GIF_INSUFFICIENT_FRAME_DATA;
+        } else {
+                if (gif_data[0] == GIF_TRAILER) {
+                        return GIF_OK;
+                }
+        }
+        return GIF_WORKING;
+}
+
+
+
+
+/**
+ * Skips the frame's extensions (which have been previously initialised)
+ *
+ * \param gif The animation context
+ * \return GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the
+ *         frame GIF_OK for successful decoding
+ */
+static gif_result gif_skip_frame_extensions(gif_animation *gif)
+{
+        unsigned char *gif_data, *gif_end;
+        int gif_bytes;
+        unsigned int block_size;
+
+        /* Get our buffer position etc.        */
+        gif_data = (unsigned char *)(gif->gif_data + gif->buffer_position);
+        gif_end = (unsigned char *)(gif->gif_data + gif->buffer_size);
+        gif_bytes = (gif_end - gif_data);
+
+        /* Skip the extensions */
+        while (gif_data < gif_end && gif_data[0] == GIF_EXTENSION_INTRODUCER) {
+                ++gif_data;
+                if (gif_data >= gif_end) {
+                        return GIF_INSUFFICIENT_FRAME_DATA;
+                }
+
+                /* Switch on extension label */
+                switch(gif_data[0]) {
+                case GIF_EXTENSION_COMMENT:
+                        /* Move the pointer to the first data sub-block
+                         * 1 byte for the extension label
+                         */
+                        ++gif_data;
+                        break;
+
+                default:
+                        /* Move the pointer to the first data sub-block 2 bytes
+                         * for the extension label and size fields Skip the
+                         * extension size itself
+                         */
+                        if (gif_data + 1 >= gif_end) {
+                                return GIF_INSUFFICIENT_FRAME_DATA;
+                        }
+                        gif_data += (2 + gif_data[1]);
+                }
+
+                /* Repeatedly skip blocks until we get a zero block or run out
+                 * of data This data is ignored by this gif decoder
+                 */
+                gif_bytes = (gif_end - gif_data);
+                block_size = 0;
+                while (gif_data < gif_end && gif_data[0] != GIF_BLOCK_TERMINATOR) {
+                        block_size = gif_data[0] + 1;
+                        if ((gif_bytes -= block_size) < 0) {
+                                return GIF_INSUFFICIENT_FRAME_DATA;
+                        }
+                        gif_data += block_size;
+                }
+                ++gif_data;
+        }
+
+        /* Set buffer position and return */
+        gif->buffer_position = (gif_data - gif->gif_data);
+        return GIF_OK;
+}
+
+static unsigned int gif_interlaced_line(int height, int y) {
+        if ((y << 3) < height) {
+                return (y << 3);
+        }
+        y -= ((height + 7) >> 3);
+        if ((y << 3) < (height - 4)) {
+                return (y << 3) + 4;
+        }
+        y -= ((height + 3) >> 3);
+        if ((y << 2) < (height - 2)) {
+                return (y << 2) + 2;
+        }
+        y -= ((height + 1) >> 2);
+        return (y << 1) + 1;
+}
+
+
+static gif_result gif_error_from_lzw(lzw_result l_res)
+{
+        static const gif_result g_res[] = {
+                [LZW_OK]        = GIF_OK,
+                [LZW_OK_EOD]    = GIF_END_OF_FRAME,
+                [LZW_NO_MEM]    = GIF_INSUFFICIENT_MEMORY,
+                [LZW_NO_DATA]   = GIF_INSUFFICIENT_FRAME_DATA,
+                [LZW_EOI_CODE]  = GIF_FRAME_DATA_ERROR,
+                [LZW_BAD_ICODE] = GIF_FRAME_DATA_ERROR,
+                [LZW_BAD_CODE]  = GIF_FRAME_DATA_ERROR,
+        };
+        return g_res[l_res];
+}
+
+
+/**
+ * decode a gif frame
+ *
+ * \param gif gif animation context.
+ * \param frame The frame number to decode.
+ * \param clear_image flag for image data being cleared instead of plotted.
+ */
+static gif_result
+gif_internal_decode_frame(gif_animation *gif,
+                          unsigned int frame,
+                          bool clear_image)
+{
+        unsigned int index = 0;
+        unsigned char *gif_data, *gif_end;
+        int gif_bytes;
+        unsigned int width, height, offset_x, offset_y;
+        unsigned int flags, colour_table_size, interlace;
+        unsigned int *colour_table;
+        unsigned int *frame_data = 0;  // Set to 0 for no warnings
+        unsigned int *frame_scanline;
+        unsigned int save_buffer_position;
+        unsigned int return_value = 0;
+        unsigned int x, y, decode_y, burst_bytes;
+        register unsigned char colour;
+
+        /* Ensure this frame is supposed to be decoded */
+        if (gif->frames[frame].display == false) {
+                return GIF_OK;
+        }
+
+        /* Ensure the frame is in range to decode */
+        if (frame > gif->frame_count_partial) {
+                return GIF_INSUFFICIENT_DATA;
+        }
+
+        /* done if frame is already decoded */
+        if ((!clear_image) &&
+            ((int)frame == gif->decoded_frame)) {
+                return GIF_OK;
+        }
+
+        /* Get the start of our frame data and the end of the GIF data */
+        gif_data = gif->gif_data + gif->frames[frame].frame_pointer;
+        gif_end = gif->gif_data + gif->buffer_size;
+        gif_bytes = (gif_end - gif_data);
+
+        /*
+         * Ensure there is a minimal amount of data to proceed.  The shortest
+         * block of data is a 10-byte image descriptor + 1-byte gif trailer
+         */
+        if (gif_bytes < 12) {
+                return GIF_INSUFFICIENT_FRAME_DATA;
+        }
+
+        /* Save the buffer position */
+        save_buffer_position = gif->buffer_position;
+        gif->buffer_position = gif_data - gif->gif_data;
+
+        /* Skip any extensions because they have allready been processed */
+        if ((return_value = gif_skip_frame_extensions(gif)) != GIF_OK) {
+                goto gif_decode_frame_exit;
+        }
+        gif_data = (gif->gif_data + gif->buffer_position);
+        gif_bytes = (gif_end - gif_data);
+
+        /* Ensure we have enough data for the 10-byte image descriptor + 1-byte
+         * gif trailer
+         */
+        if (gif_bytes < 12) {
+                return_value = GIF_INSUFFICIENT_FRAME_DATA;
+                goto gif_decode_frame_exit;
+        }
+
+        /* 10-byte Image Descriptor is:
+         *
+         *     +0      CHAR    Image Separator (0x2c)
+         *     +1      SHORT   Image Left Position
+         *     +3      SHORT   Image Top Position
+         *     +5      SHORT   Width
+         *     +7      SHORT   Height
+         *     +9      CHAR    __Packed Fields__
+         *                     1BIT    Local Colour Table Flag
+         *                     1BIT    Interlace Flag
+         *                     1BIT    Sort Flag
+         *                     2BITS   Reserved
+         *                     3BITS   Size of Local Colour Table
+         */
+        if (gif_data[0] != GIF_IMAGE_SEPARATOR) {
+                return_value = GIF_DATA_ERROR;
+                goto gif_decode_frame_exit;
+        }
+        offset_x = gif_data[1] | (gif_data[2] << 8);
+        offset_y = gif_data[3] | (gif_data[4] << 8);
+        width = gif_data[5] | (gif_data[6] << 8);
+        height = gif_data[7] | (gif_data[8] << 8);
+
+        /* Boundary checking - shouldn't ever happen except unless the data has
+         * been modified since initialisation.
+         */
+        if ((offset_x + width > gif->width) ||
+            (offset_y + height > gif->height)) {
+                return_value = GIF_DATA_ERROR;
+                goto gif_decode_frame_exit;
+        }
+
+        /* Decode the flags */
+        flags = gif_data[9];
+        colour_table_size = 2 << (flags & GIF_COLOUR_TABLE_SIZE_MASK);
+        interlace = flags & GIF_INTERLACE_MASK;
+
+        /* Advance data pointer to next block either colour table or image
+         * data.
+         */
+        gif_data += 10;
+        gif_bytes = (gif_end - gif_data);
+
+        /* Set up the colour table */
+        if (flags & GIF_COLOUR_TABLE_MASK) {
+                if (gif_bytes < (int)(3 * colour_table_size)) {
+                        return_value = GIF_INSUFFICIENT_FRAME_DATA;
+                        goto gif_decode_frame_exit;
+                }
+                colour_table = gif->local_colour_table;
+                if (!clear_image) {
+                        for (index = 0; index < colour_table_size; index++) {
+                                /* Gif colour map contents are r,g,b.
+                                 *
+                                 * We want to pack them bytewise into the
+                                 * colour table, such that the red component
+                                 * is in byte 0 and the alpha component is in
+                                 * byte 3.
+                                 */
+                                unsigned char *entry =
+                                        (unsigned char *) &colour_table[index];
+
+                                entry[0] = gif_data[0];        /* r */
+                                entry[1] = gif_data[1];        /* g */
+                                entry[2] = gif_data[2];        /* b */
+                                entry[3] = 0xff;       /* a */
+
+                                gif_data += 3;
+                        }
+                } else {
+                        gif_data += 3 * colour_table_size;
+                }
+                gif_bytes = (gif_end - gif_data);
+        } else {
+                colour_table = gif->global_colour_table;
+        }
+
+        /* Ensure sufficient data remains */
+        if (gif_bytes < 1) {
+                return_value = GIF_INSUFFICIENT_FRAME_DATA;
+                goto gif_decode_frame_exit;
+        }
+
+        /* check for an end marker */
+        if (gif_data[0] == GIF_TRAILER) {
+                return_value = GIF_OK;
+                goto gif_decode_frame_exit;
+        }
+
+        /* Get the frame data */
+        assert(gif->bitmap_callbacks.bitmap_get_buffer);
+        frame_data = (void *)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
+        if (!frame_data) {
+                return GIF_INSUFFICIENT_MEMORY;
+        }
+
+        /* If we are clearing the image we just clear, if not decode */
+        if (!clear_image) {
+                lzw_result res;
+                const uint8_t *stack_base;
+                const uint8_t *stack_pos;
+
+                /* Ensure we have enough data for a 1-byte LZW code size +
+                 * 1-byte gif trailer
+                 */
+                if (gif_bytes < 2) {
+                        return_value = GIF_INSUFFICIENT_FRAME_DATA;
+                        goto gif_decode_frame_exit;
+                }
+
+                /* If we only have a 1-byte LZW code size + 1-byte gif trailer,
+                 * we're finished
+                 */
+                if ((gif_bytes == 2) && (gif_data[1] == GIF_TRAILER)) {
+                        return_value = GIF_OK;
+                        goto gif_decode_frame_exit;
+                }
+
+                /* If the previous frame's disposal method requires we restore
+                 * the background colour or this is the first frame, clear
+                 * the frame data
+                 */
+                if ((frame == 0) || (gif->decoded_frame == GIF_INVALID_FRAME)) {
+                        memset((char*)frame_data,
+                               GIF_TRANSPARENT_COLOUR,
+                               gif->width * gif->height * sizeof(int));
+                        gif->decoded_frame = frame;
+                        /* The line below would fill the image with its
+                         * background color, but because GIFs support
+                         * transparency we likely wouldn't want to do that. */
+                        /* memset((char*)frame_data, colour_table[gif->background_index], gif->width * 
gif->height * sizeof(int)); */
+                } else if ((frame != 0) &&
+                           (gif->frames[frame - 1].disposal_method == GIF_FRAME_CLEAR)) {
+                        return_value = gif_internal_decode_frame(gif,
+                                                                 (frame - 1),
+                                                                 true);
+                        if (return_value != GIF_OK) {
+                                goto gif_decode_frame_exit;
+                        }
+
+                } else if ((frame != 0) &&
+                           (gif->frames[frame - 1].disposal_method == GIF_FRAME_RESTORE)) {
+                        /*
+                         * If the previous frame's disposal method requires we
+                         * restore the previous image, find the last image set
+                         * to "do not dispose" and get that frame data
+                         */
+                        int last_undisposed_frame = frame - 2;
+                        while ((last_undisposed_frame >= 0) &&
+                               (gif->frames[last_undisposed_frame].disposal_method == GIF_FRAME_RESTORE)) {
+                                last_undisposed_frame--;
+                        }
+
+                        /* If we don't find one, clear the frame data */
+                        if (last_undisposed_frame == -1) {
+                                /* see notes above on transparency
+                                 * vs. background color
+                                 */
+                                memset((char*)frame_data,
+                                       GIF_TRANSPARENT_COLOUR,
+                                       gif->width * gif->height * sizeof(int));
+                        } else {
+                                return_value = gif_internal_decode_frame(gif, last_undisposed_frame, false);
+                                if (return_value != GIF_OK) {
+                                        goto gif_decode_frame_exit;
+                                }
+                                /* Get this frame's data */
+                                assert(gif->bitmap_callbacks.bitmap_get_buffer);
+                                frame_data = (void 
*)gif->bitmap_callbacks.bitmap_get_buffer(gif->frame_image);
+                                if (!frame_data) {
+                                        return GIF_INSUFFICIENT_MEMORY;
+                                }
+                        }
+                }
+                gif->decoded_frame = frame;
+                gif->buffer_position = (gif_data - gif->gif_data) + 1;
+
+                /* Initialise the LZW decoding */
+                res = lzw_decode_init(gif->lzw_ctx, gif->gif_data,
+                                gif->buffer_size, gif->buffer_position,
+                                gif_data[0], &stack_base, &stack_pos);
+                if (res != LZW_OK) {
+                        return gif_error_from_lzw(res);
+                }
+
+                /* Decompress the data */
+                for (y = 0; y < height; y++) {
+                        if (interlace) {
+                                decode_y = gif_interlaced_line(height, y) + offset_y;
+                        } else {
+                                decode_y = y + offset_y;
+                        }
+                        frame_scanline = frame_data + offset_x + (decode_y * gif->width);
+
+                        /* Rather than decoding pixel by pixel, we try to burst
+                         * out streams of data to remove the need for end-of
+                         * data checks every pixel.
+                         */
+                        x = width;
+                        while (x > 0) {
+                                burst_bytes = (stack_pos - stack_base);
+                                if (burst_bytes > 0) {
+                                        if (burst_bytes > x) {
+                                                burst_bytes = x;
+                                        }
+                                        x -= burst_bytes;
+                                        while (burst_bytes-- > 0) {
+                                                colour = *--stack_pos;
+                                                if (((gif->frames[frame].transparency) &&
+                                                     (colour != gif->frames[frame].transparency_index)) ||
+                                                    (!gif->frames[frame].transparency)) {
+                                                        *frame_scanline = colour_table[colour];
+                                                }
+                                                frame_scanline++;
+                                        }
+                                } else {
+                                        res = lzw_decode(gif->lzw_ctx, &stack_pos);
+                                        if (res != LZW_OK) {
+                                                /* Unexpected end of frame, try to recover */
+                                                if (res == LZW_OK_EOD) {
+                                                        return_value = GIF_OK;
+                                                } else {
+                                                        return_value = gif_error_from_lzw(res);
+                                                }
+                                                goto gif_decode_frame_exit;
+                                        }
+                                }
+                        }
+                }
+        } else {
+                /* Clear our frame */
+                if (gif->frames[frame].disposal_method == GIF_FRAME_CLEAR) {
+                        for (y = 0; y < height; y++) {
+                                frame_scanline = frame_data + offset_x + ((offset_y + y) * gif->width);
+                                if (gif->frames[frame].transparency) {
+                                        memset(frame_scanline,
+                                               GIF_TRANSPARENT_COLOUR,
+                                               width * 4);
+                                } else {
+                                        memset(frame_scanline,
+                                               colour_table[gif->background_index],
+                                               width * 4);
+                                }
+                        }
+                }
+        }
+gif_decode_frame_exit:
+
+        /* Check if we should test for optimisation */
+        if (gif->frames[frame].virgin) {
+                if (gif->bitmap_callbacks.bitmap_test_opaque) {
+                        gif->frames[frame].opaque = 
gif->bitmap_callbacks.bitmap_test_opaque(gif->frame_image);
+                } else {
+                        gif->frames[frame].opaque = false;
+                }
+                gif->frames[frame].virgin = false;
+        }
+
+        if (gif->bitmap_callbacks.bitmap_set_opaque) {
+                gif->bitmap_callbacks.bitmap_set_opaque(gif->frame_image, gif->frames[frame].opaque);
+        }
+
+        if (gif->bitmap_callbacks.bitmap_modified) {
+                gif->bitmap_callbacks.bitmap_modified(gif->frame_image);
+        }
+
+        /* Restore the buffer position */
+        gif->buffer_position = save_buffer_position;
+
+        return return_value;
+}
+
+
+/* exported function documented in libnsgif.h */
+void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks)
+{
+        memset(gif, 0, sizeof(gif_animation));
+        gif->bitmap_callbacks = *bitmap_callbacks;
+        gif->decoded_frame = GIF_INVALID_FRAME;
+}
+
+
+/* exported function documented in libnsgif.h */
+gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data)
+{
+        unsigned char *gif_data;
+        unsigned int index;
+        gif_result return_value;
+
+        /* Initialize values */
+        gif->buffer_size = size;
+        gif->gif_data = data;
+
+        if (gif->lzw_ctx == NULL) {
+                lzw_result res = lzw_context_create(
+                                (struct lzw_ctx **)&gif->lzw_ctx);
+                if (res != LZW_OK) {
+                        return gif_error_from_lzw(res);
+                }
+        }
+
+        /* Check for sufficient data to be a GIF (6-byte header + 7-byte
+         * logical screen descriptor)
+         */
+        if (gif->buffer_size < GIF_STANDARD_HEADER_SIZE) {
+                return GIF_INSUFFICIENT_DATA;
+        }
+
+        /* Get our current processing position */
+        gif_data = gif->gif_data + gif->buffer_position;
+
+        /* See if we should initialise the GIF */
+        if (gif->buffer_position == 0) {
+                /* We want everything to be NULL before we start so we've no
+                 * chance of freeing bad pointers (paranoia)
+                 */
+                gif->frame_image = NULL;
+                gif->frames = NULL;
+                gif->local_colour_table = NULL;
+                gif->global_colour_table = NULL;
+
+                /* The caller may have been lazy and not reset any values */
+                gif->frame_count = 0;
+                gif->frame_count_partial = 0;
+                gif->decoded_frame = GIF_INVALID_FRAME;
+
+                /* 6-byte GIF file header is:
+                 *
+                 *     +0      3CHARS  Signature ('GIF')
+                 *     +3      3CHARS  Version ('87a' or '89a')
+                 */
+                if (strncmp((const char *) gif_data, "GIF", 3) != 0) {
+                        return GIF_DATA_ERROR;
+                }
+                gif_data += 3;
+
+                /* Ensure GIF reports version 87a or 89a */
+                /*
+                if ((strncmp(gif_data, "87a", 3) != 0) &&
+                    (strncmp(gif_data, "89a", 3) != 0))
+                               LOG(("Unknown GIF format - proceeding anyway"));
+                */
+                gif_data += 3;
+
+                /* 7-byte Logical Screen Descriptor is:
+                 *
+                 *     +0      SHORT   Logical Screen Width
+                 *     +2      SHORT   Logical Screen Height
+                 *     +4      CHAR    __Packed Fields__
+                 *                      1BIT   Global Colour Table Flag
+                 *                      3BITS  Colour Resolution
+                 *                      1BIT   Sort Flag
+                 *                      3BITS  Size of Global Colour Table
+                 *     +5      CHAR    Background Colour Index
+                 *     +6      CHAR    Pixel Aspect Ratio
+                 */
+                gif->width = gif_data[0] | (gif_data[1] << 8);
+                gif->height = gif_data[2] | (gif_data[3] << 8);
+                gif->global_colours = (gif_data[4] & GIF_COLOUR_TABLE_MASK);
+                gif->colour_table_size = (2 << (gif_data[4] & GIF_COLOUR_TABLE_SIZE_MASK));
+                gif->background_index = gif_data[5];
+                gif->aspect_ratio = gif_data[6];
+                gif->loop_count = 1;
+                gif_data += 7;
+
+                /* Some broken GIFs report the size as the screen size they
+                 * were created in. As such, we detect for the common cases and
+                 * set the sizes as 0 if they are found which results in the
+                 * GIF being the maximum size of the frames.
+                 */
+                if (((gif->width == 640) && (gif->height == 480)) ||
+                    ((gif->width == 640) && (gif->height == 512)) ||
+                    ((gif->width == 800) && (gif->height == 600)) ||
+                    ((gif->width == 1024) && (gif->height == 768)) ||
+                    ((gif->width == 1280) && (gif->height == 1024)) ||
+                    ((gif->width == 1600) && (gif->height == 1200)) ||
+                    ((gif->width == 0) || (gif->height == 0)) ||
+                    ((gif->width > 2048) || (gif->height > 2048))) {
+                        gif->width = 1;
+                        gif->height = 1;
+                }
+
+                /* Allocate some data irrespective of whether we've got any
+                 * colour tables. We always get the maximum size in case a GIF
+                 * is lying to us. It's far better to give the wrong colours
+                 * than to trample over some memory somewhere.
+                */
+                gif->global_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
+                gif->local_colour_table = calloc(GIF_MAX_COLOURS, sizeof(unsigned int));
+                if ((gif->global_colour_table == NULL) ||
+                    (gif->local_colour_table == NULL)) {
+                        gif_finalise(gif);
+                        return GIF_INSUFFICIENT_MEMORY;
+                }
+
+                /* Set the first colour to a value that will never occur in
+                 * reality so we know if we've processed it
+                */
+                gif->global_colour_table[0] = GIF_PROCESS_COLOURS;
+
+                /* Check if the GIF has no frame data (13-byte header + 1-byte
+                 * termination block) Although generally useless, the GIF
+                 * specification does not expressly prohibit this
+                 */
+                if (gif->buffer_size == (GIF_STANDARD_HEADER_SIZE + 1)) {
+                        if (gif_data[0] == GIF_TRAILER) {
+                                return GIF_OK;
+                        } else {
+                                return GIF_INSUFFICIENT_DATA;
+                        }
+                }
+
+                /* Initialise enough workspace for a frame */
+                if ((gif->frames = (gif_frame *)malloc(sizeof(gif_frame))) == NULL) {
+                        gif_finalise(gif);
+                        return GIF_INSUFFICIENT_MEMORY;
+                }
+                gif->frame_holders = 1;
+
+                /* Initialise the bitmap header */
+                assert(gif->bitmap_callbacks.bitmap_create);
+                gif->frame_image = gif->bitmap_callbacks.bitmap_create(gif->width, gif->height);
+                if (gif->frame_image == NULL) {
+                        gif_finalise(gif);
+                        return GIF_INSUFFICIENT_MEMORY;
+                }
+
+                /* Remember we've done this now */
+                gif->buffer_position = gif_data - gif->gif_data;
+        }
+
+        /*  Do the colour map if we haven't already. As the top byte is always
+         *  0xff or 0x00 depending on the transparency we know if it's been
+         *  filled in.
+         */
+        if (gif->global_colour_table[0] == GIF_PROCESS_COLOURS) {
+                /* Check for a global colour map signified by bit 7 */
+                if (gif->global_colours) {
+                        if (gif->buffer_size < (gif->colour_table_size * 3 + GIF_STANDARD_HEADER_SIZE)) {
+                                return GIF_INSUFFICIENT_DATA;
+                        }
+                        for (index = 0; index < gif->colour_table_size; index++) {
+                                /* Gif colour map contents are r,g,b.
+                                 *
+                                 * We want to pack them bytewise into the
+                                 * colour table, such that the red component
+                                 * is in byte 0 and the alpha component is in
+                                 * byte 3.
+                                 */
+                                unsigned char *entry = (unsigned char *) &gif->
+                                                       global_colour_table[index];
+
+                                entry[0] = gif_data[0];        /* r */
+                                entry[1] = gif_data[1];        /* g */
+                                entry[2] = gif_data[2];        /* b */
+                                entry[3] = 0xff;       /* a */
+
+                                gif_data += 3;
+                        }
+                        gif->buffer_position = (gif_data - gif->gif_data);
+                } else {
+                        /* Create a default colour table with the first two
+                         * colours as black and white
+                         */
+                        unsigned int *entry = gif->global_colour_table;
+
+                        entry[0] = 0x00000000;
+                        /* Force Alpha channel to opaque */
+                        ((unsigned char *) entry)[3] = 0xff;
+
+                        entry[1] = 0xffffffff;
+                }
+        }
+
+        /* Repeatedly try to initialise frames */
+        while ((return_value = gif_initialise_frame(gif)) == GIF_WORKING);
+
+        /* If there was a memory error tell the caller */
+        if ((return_value == GIF_INSUFFICIENT_MEMORY) ||
+            (return_value == GIF_DATA_ERROR)) {
+                return return_value;
+        }
+
+        /* If we didn't have some frames then a GIF_INSUFFICIENT_DATA becomes a
+         * GIF_INSUFFICIENT_FRAME_DATA
+         */
+        if ((return_value == GIF_INSUFFICIENT_DATA) &&
+            (gif->frame_count_partial > 0)) {
+                return GIF_INSUFFICIENT_FRAME_DATA;
+        }
+
+        /* Return how many we got */
+        return return_value;
+}
+
+
+/* exported function documented in libnsgif.h */
+gif_result gif_decode_frame(gif_animation *gif, unsigned int frame)
+{
+        return gif_internal_decode_frame(gif, frame, false);
+}
+
+
+/* exported function documented in libnsgif.h */
+void gif_finalise(gif_animation *gif)
+{
+        /* Release all our memory blocks */
+        if (gif->frame_image) {
+                assert(gif->bitmap_callbacks.bitmap_destroy);
+                gif->bitmap_callbacks.bitmap_destroy(gif->frame_image);
+        }
+
+        gif->frame_image = NULL;
+        free(gif->frames);
+        gif->frames = NULL;
+        free(gif->local_colour_table);
+        gif->local_colour_table = NULL;
+        free(gif->global_colour_table);
+        gif->global_colour_table = NULL;
+
+        lzw_context_destroy(gif->lzw_ctx);
+        gif->lzw_ctx = NULL;
+}
diff --git a/subprojects/libnsgif/libnsgif.h b/subprojects/libnsgif/libnsgif.h
new file mode 100644
index 000000000..a819fec0b
--- /dev/null
+++ b/subprojects/libnsgif/libnsgif.h
@@ -0,0 +1,183 @@
+/*
+ * Copyright 2004 Richard Wilson <richard wilson netsurf-browser org>
+ * Copyright 2008 Sean Fox <dyntryx gmail com>
+ *
+ * This file is part of NetSurf's libnsgif, http://www.netsurf-browser.org/
+ * Licenced under the MIT License,
+ *                http://www.opensource.org/licenses/mit-license.php
+ */
+
+/**
+ * \file
+ * Interface to progressive animated GIF file decoding.
+ */
+
+#ifndef _LIBNSGIF_H_
+#define _LIBNSGIF_H_
+
+#include <stdbool.h>
+#include <inttypes.h>
+
+/* Error return values */
+typedef enum {
+        GIF_WORKING = 1,
+        GIF_OK = 0,
+        GIF_INSUFFICIENT_FRAME_DATA = -1,
+        GIF_FRAME_DATA_ERROR = -2,
+        GIF_INSUFFICIENT_DATA = -3,
+        GIF_DATA_ERROR = -4,
+        GIF_INSUFFICIENT_MEMORY = -5,
+        GIF_FRAME_NO_DISPLAY = -6,
+        GIF_END_OF_FRAME = -7
+} gif_result;
+
+/** GIF frame data */
+typedef struct gif_frame {
+        /** whether the frame should be displayed/animated */
+        bool display;
+        /** delay (in cs) before animating the frame */
+        unsigned int frame_delay;
+
+        /* Internal members are listed below */
+
+        /** offset (in bytes) to the GIF frame data */
+        unsigned int frame_pointer;
+        /** whether the frame has previously been used */
+        bool virgin;
+        /** whether the frame is totally opaque */
+        bool opaque;
+        /** whether a forcable screen redraw is required */
+        bool redraw_required;
+        /** how the previous frame should be disposed; affects plotting */
+        unsigned char disposal_method;
+        /** whether we acknoledge transparency */
+        bool transparency;
+        /** the index designating a transparent pixel */
+        unsigned char transparency_index;
+        /** x co-ordinate of redraw rectangle */
+        unsigned int redraw_x;
+        /** y co-ordinate of redraw rectangle */
+        unsigned int redraw_y;
+        /** width of redraw rectangle */
+        unsigned int redraw_width;
+        /** height of redraw rectangle */
+        unsigned int redraw_height;
+} gif_frame;
+
+/* API for Bitmap callbacks */
+typedef void* (*gif_bitmap_cb_create)(int width, int height);
+typedef void (*gif_bitmap_cb_destroy)(void *bitmap);
+typedef unsigned char* (*gif_bitmap_cb_get_buffer)(void *bitmap);
+typedef void (*gif_bitmap_cb_set_opaque)(void *bitmap, bool opaque);
+typedef bool (*gif_bitmap_cb_test_opaque)(void *bitmap);
+typedef void (*gif_bitmap_cb_modified)(void *bitmap);
+
+/** Bitmap callbacks function table */
+typedef struct gif_bitmap_callback_vt {
+        /** Create a bitmap. */
+        gif_bitmap_cb_create bitmap_create;
+        /** Free a bitmap. */
+        gif_bitmap_cb_destroy bitmap_destroy;
+        /** Return a pointer to the pixel data in a bitmap. */
+        gif_bitmap_cb_get_buffer bitmap_get_buffer;
+
+        /* Members below are optional */
+
+        /** Sets whether a bitmap should be plotted opaque. */
+        gif_bitmap_cb_set_opaque bitmap_set_opaque;
+        /** Tests whether a bitmap has an opaque alpha channel. */
+        gif_bitmap_cb_test_opaque bitmap_test_opaque;
+        /** The bitmap image has changed, so flush any persistant cache. */
+        gif_bitmap_cb_modified bitmap_modified;
+} gif_bitmap_callback_vt;
+
+/** GIF animation data */
+typedef struct gif_animation {
+        /** LZW decode context */
+        void *lzw_ctx;
+        /** callbacks for bitmap functions */
+        gif_bitmap_callback_vt bitmap_callbacks;
+        /** pointer to GIF data */
+        unsigned char *gif_data;
+        /** width of GIF (may increase during decoding) */
+        unsigned int width;
+        /** heigth of GIF (may increase during decoding) */
+        unsigned int height;
+        /** number of frames decoded */
+        unsigned int frame_count;
+        /** number of frames partially decoded */
+        unsigned int frame_count_partial;
+        /** decoded frames */
+        gif_frame *frames;
+        /** current frame decoded to bitmap */
+        int decoded_frame;
+        /** currently decoded image; stored as bitmap from bitmap_create callback */
+        void *frame_image;
+        /** number of times to loop animation */
+        int loop_count;
+
+        /* Internal members are listed below */
+
+        /** current index into GIF data */
+        unsigned int buffer_position;
+        /** total number of bytes of GIF data available */
+        unsigned int buffer_size;
+        /** current number of frame holders */
+        unsigned int frame_holders;
+        /** index in the colour table for the background colour */
+        unsigned int background_index;
+        /** image aspect ratio (ignored) */
+        unsigned int aspect_ratio;
+        /** size of colour table (in entries) */
+        unsigned int colour_table_size;
+        /** whether the GIF has a global colour table */
+        bool global_colours;
+        /** global colour table */
+        unsigned int *global_colour_table;
+        /** local colour table */
+        unsigned int *local_colour_table;
+} gif_animation;
+
+/**
+ * Initialises necessary gif_animation members.
+ */
+void gif_create(gif_animation *gif, gif_bitmap_callback_vt *bitmap_callbacks);
+
+/**
+ * Initialises any workspace held by the animation and attempts to decode
+ * any information that hasn't already been decoded.
+ * If an error occurs, all previously decoded frames are retained.
+ *
+ * @return Error return value.
+ *         - GIF_FRAME_DATA_ERROR for GIF frame data error
+ *         - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to process
+ *                                     any more frames
+ *         - GIF_INSUFFICIENT_MEMORY for memory error
+ *         - GIF_DATA_ERROR for GIF error
+ *         - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ *         - GIF_OK for successful decoding
+ *         - GIF_WORKING for successful decoding if more frames are expected
+ */
+gif_result gif_initialise(gif_animation *gif, size_t size, unsigned char *data);
+
+/**
+ * Decodes a GIF frame.
+ *
+ * @return Error return value. If a frame does not contain any image data,
+ *             GIF_OK is returned and gif->current_error is set to
+ *             GIF_FRAME_NO_DISPLAY
+ *         - GIF_FRAME_DATA_ERROR for GIF frame data error
+ *         - GIF_INSUFFICIENT_FRAME_DATA for insufficient data to complete the frame
+ *         - GIF_DATA_ERROR for GIF error (invalid frame header)
+ *         - GIF_INSUFFICIENT_DATA for insufficient data to do anything
+ *         - GIF_INSUFFICIENT_MEMORY for insufficient memory to process
+ *         - GIF_OK for successful decoding
+ */
+gif_result gif_decode_frame(gif_animation *gif, unsigned int frame);
+
+/**
+ * Releases any workspace held by a gif
+ */
+void gif_finalise(gif_animation *gif);
+
+#endif
diff --git a/subprojects/libnsgif/libnsgif.pc.in b/subprojects/libnsgif/libnsgif.pc.in
new file mode 100644
index 000000000..0255bf9df
--- /dev/null
+++ b/subprojects/libnsgif/libnsgif.pc.in
@@ -0,0 +1,10 @@
+prefix=PREFIX
+exec_prefix=${prefix}
+libdir=${exec_prefix}/LIBDIR
+includedir=${prefix}/INCLUDEDIR
+
+Name: libnsgif
+Description: Provides gif loading and conversion
+Version: VERSION
+Libs: -L${libdir} -lnsgif
+Cflags: -I${includedir}
diff --git a/subprojects/libnsgif/log.h b/subprojects/libnsgif/log.h
new file mode 100644
index 000000000..1413374cb
--- /dev/null
+++ b/subprojects/libnsgif/log.h
@@ -0,0 +1,21 @@
+/*
+ * Copyright 2003 James Bursa <bursa users sourceforge net>
+ * Copyright 2004 John Tytgat <John Tytgat aaug net>
+ *
+ * This file is part of NetSurf, http://www.netsurf-browser.org/
+ * Licenced under the MIT License,
+ *                http://www.opensource.org/licenses/mit-license.php
+ */
+
+#include <stdio.h>
+
+#ifndef _LIBNSGIF_LOG_H_
+#define _LIBNSGIF_LOG_H_
+
+#ifdef NDEBUG
+#  define LOG(x) ((void) 0)
+#else
+#  define LOG(x) do { fprintf(stderr, x), fputc('\n', stderr); } while (0)
+#endif /* NDEBUG */
+
+#endif /* _LIBNSGIF_LOG_H_ */
diff --git a/subprojects/libnsgif/lzw.c b/subprojects/libnsgif/lzw.c
new file mode 100644
index 000000000..31cf7d4e9
--- /dev/null
+++ b/subprojects/libnsgif/lzw.c
@@ -0,0 +1,377 @@
+/*
+ * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
+ * Licensed under the MIT License,
+ *                http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2017 Michael Drake <michael drake codethink co uk>
+ */
+
+#include <assert.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <stdbool.h>
+
+#include "lzw.h"
+
+/**
+ * \file
+ * \brief LZW decompression (implementation)
+ *
+ * Decoder for GIF LZW data.
+ */
+
+
+/**
+ * Context for reading LZW data.
+ *
+ * LZW data is split over multiple sub-blocks.  Each sub-block has a
+ * byte at the start, which says the sub-block size, and then the data.
+ * Zero-size sub-blocks have no data, and the biggest sub-block size is
+ * 255, which means there are 255 bytes of data following the sub-block
+ * size entry.
+ *
+ * Note that an individual LZW code can be split over up to three sub-blocks.
+ */
+struct lzw_read_ctx {
+       const uint8_t *data;    /**< Pointer to start of input data */
+       uint32_t data_len;      /**< Input data length */
+       uint32_t data_sb_next;  /**< Offset to sub-block size */
+
+       const uint8_t *sb_data; /**< Pointer to current sub-block in data */
+       uint32_t sb_bit;        /**< Current bit offset in sub-block */
+       uint32_t sb_bit_count;  /**< Bit count in sub-block */
+};
+
+/**
+ * LZW dictionary entry.
+ *
+ * Records in the dictionary are composed of 1 or more entries.
+ * Entries point to previous entries which can be followed to compose
+ * the complete record.  To compose the record in reverse order, take
+ * the `last_value` from each entry, and move to the previous entry.
+ * If the previous_entry's index is < the current clear_code, then it
+ * is the last entry in the record.
+ */
+struct lzw_dictionary_entry {
+       uint8_t last_value;      /**< Last value for record ending at entry. */
+       uint8_t first_value;     /**< First value for entry's record. */
+       uint16_t previous_entry; /**< Offset in dictionary to previous entry. */
+};
+
+/**
+ * LZW decompression context.
+ */
+struct lzw_ctx {
+       /** Input reading context */
+       struct lzw_read_ctx input;
+
+       uint32_t previous_code;       /**< Code read from input previously. */
+       uint32_t previous_code_first; /**< First value of previous code. */
+
+       uint32_t initial_code_size;     /**< Starting LZW code size. */
+       uint32_t current_code_size;     /**< Current LZW code size. */
+       uint32_t current_code_size_max; /**< Max code value for current size. */
+
+       uint32_t clear_code; /**< Special Clear code value */
+       uint32_t eoi_code;   /**< Special End of Information code value */
+
+       uint32_t current_entry; /**< Next position in table to fill. */
+
+       /** Output value stack. */
+       uint8_t stack_base[1 << LZW_CODE_MAX];
+
+       /** LZW decode dictionary. Generated during decode. */
+       struct lzw_dictionary_entry table[1 << LZW_CODE_MAX];
+};
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_context_create(struct lzw_ctx **ctx)
+{
+       struct lzw_ctx *c = malloc(sizeof(*c));
+       if (c == NULL) {
+               return LZW_NO_MEM;
+       }
+
+       *ctx = c;
+       return LZW_OK;
+}
+
+
+/* Exported function, documented in lzw.h */
+void lzw_context_destroy(struct lzw_ctx *ctx)
+{
+       free(ctx);
+}
+
+
+/**
+ * Advance the context to the next sub-block in the input data.
+ *
+ * \param[in] ctx  LZW reading context, updated on success.
+ * \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
+ */
+static lzw_result lzw__block_advance(struct lzw_read_ctx *ctx)
+{
+       uint32_t block_size;
+       uint32_t next_block_pos = ctx->data_sb_next;
+       const uint8_t *data_next = ctx->data + next_block_pos;
+
+       if (next_block_pos >= ctx->data_len) {
+               return LZW_NO_DATA;
+       }
+
+       block_size = *data_next;
+
+       if ((next_block_pos + block_size) >= ctx->data_len) {
+               return LZW_NO_DATA;
+       }
+
+       ctx->sb_bit = 0;
+       ctx->sb_bit_count = block_size * 8;
+
+       if (block_size == 0) {
+               ctx->data_sb_next += 1;
+               return LZW_OK_EOD;
+       }
+
+       ctx->sb_data = data_next + 1;
+       ctx->data_sb_next += block_size + 1;
+
+       return LZW_OK;
+}
+
+
+/**
+ * Get the next LZW code of given size from the raw input data.
+ *
+ * Reads codes from the input data stream coping with GIF data sub-blocks.
+ *
+ * \param[in]  ctx        LZW reading context, updated.
+ * \param[in]  code_size  Size of LZW code to get from data.
+ * \param[out] code_out   Returns an LZW code on success.
+ * \return LZW_OK or LZW_OK_EOD on success, appropriate error otherwise.
+ */
+static inline lzw_result lzw__next_code(
+               struct lzw_read_ctx *ctx,
+               uint8_t code_size,
+               uint32_t *code_out)
+{
+       uint32_t code = 0;
+       uint8_t current_bit = ctx->sb_bit & 0x7;
+       uint8_t byte_advance = (current_bit + code_size) >> 3;
+
+       assert(byte_advance <= 2);
+
+       if (ctx->sb_bit + code_size <= ctx->sb_bit_count) {
+               /* Fast path: code fully inside this sub-block */
+               const uint8_t *data = ctx->sb_data + (ctx->sb_bit >> 3);
+               switch (byte_advance) {
+                       case 2: code |= data[2] << 16; /* Fall through */
+                       case 1: code |= data[1] <<  8; /* Fall through */
+                       case 0: code |= data[0] <<  0;
+               }
+               ctx->sb_bit += code_size;
+       } else {
+               /* Slow path: code spans sub-blocks */
+               uint8_t byte = 0;
+               uint8_t bits_remaining_0 = (code_size < (8 - current_bit)) ?
+                               code_size : (8 - current_bit);
+               uint8_t bits_remaining_1 = code_size - bits_remaining_0;
+               uint8_t bits_used[3] = {
+                       [0] = bits_remaining_0,
+                       [1] = bits_remaining_1 < 8 ? bits_remaining_1 : 8,
+                       [2] = bits_remaining_1 - 8,
+               };
+
+               while (true) {
+                       const uint8_t *data = ctx->sb_data;
+                       lzw_result res;
+
+                       /* Get any data from end of this sub-block */
+                       while (byte <= byte_advance &&
+                                       ctx->sb_bit < ctx->sb_bit_count) {
+                               code |= data[ctx->sb_bit >> 3] << (byte << 3);
+                               ctx->sb_bit += bits_used[byte];
+                               byte++;
+                       }
+
+                       /* Check if we have all we need */
+                       if (byte > byte_advance) {
+                               break;
+                       }
+
+                       /* Move to next sub-block */
+                       res = lzw__block_advance(ctx);
+                       if (res != LZW_OK) {
+                               return res;
+                       }
+               }
+       }
+
+       *code_out = (code >> current_bit) & ((1 << code_size) - 1);
+       return LZW_OK;
+}
+
+
+/**
+ * Clear LZW code dictionary.
+ *
+ * \param[in]  ctx            LZW reading context, updated.
+ * \param[out] stack_pos_out  Returns current stack position.
+ * \return LZW_OK or error code.
+ */
+static lzw_result lzw__clear_codes(
+               struct lzw_ctx *ctx,
+               const uint8_t ** const stack_pos_out)
+{
+       uint32_t code;
+       uint8_t *stack_pos;
+
+       /* Reset dictionary building context */
+       ctx->current_code_size = ctx->initial_code_size + 1;
+       ctx->current_code_size_max = (1 << ctx->current_code_size) - 1;;
+       ctx->current_entry = (1 << ctx->initial_code_size) + 2;
+
+       /* There might be a sequence of clear codes, so process them all */
+       do {
+               lzw_result res = lzw__next_code(&ctx->input,
+                               ctx->current_code_size, &code);
+               if (res != LZW_OK) {
+                       return res;
+               }
+       } while (code == ctx->clear_code);
+
+       /* The initial code must be from the initial dictionary. */
+       if (code > ctx->clear_code) {
+               return LZW_BAD_ICODE;
+       }
+
+       /* Record this initial code as "previous" code, needed during decode. */
+       ctx->previous_code = code;
+       ctx->previous_code_first = code;
+
+       /* Reset the stack, and add first non-clear code added as first item. */
+       stack_pos = ctx->stack_base;
+       *stack_pos++ = code;
+
+       *stack_pos_out = stack_pos;
+       return LZW_OK;
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode_init(
+               struct lzw_ctx *ctx,
+               const uint8_t *compressed_data,
+               uint32_t compressed_data_len,
+               uint32_t compressed_data_pos,
+               uint8_t code_size,
+               const uint8_t ** const stack_base_out,
+               const uint8_t ** const stack_pos_out)
+{
+       struct lzw_dictionary_entry *table = ctx->table;
+
+       /* Initialise the input reading context */
+       ctx->input.data = compressed_data;
+       ctx->input.data_len = compressed_data_len;
+       ctx->input.data_sb_next = compressed_data_pos;
+
+       ctx->input.sb_bit = 0;
+       ctx->input.sb_bit_count = 0;
+
+       /* Initialise the dictionary building context */
+       ctx->initial_code_size = code_size;
+
+       ctx->clear_code = (1 << code_size) + 0;
+       ctx->eoi_code   = (1 << code_size) + 1;
+
+       /* Initialise the standard dictionary entries */
+       for (uint32_t i = 0; i < ctx->clear_code; ++i) {
+               table[i].first_value = i;
+               table[i].last_value  = i;
+       }
+
+       *stack_base_out = ctx->stack_base;
+       return lzw__clear_codes(ctx, stack_pos_out);
+}
+
+
+/* Exported function, documented in lzw.h */
+lzw_result lzw_decode(struct lzw_ctx *ctx,
+               const uint8_t ** const stack_pos_out)
+{
+       lzw_result res;
+       uint32_t code_new;
+       uint32_t code_out;
+       uint8_t last_value;
+       uint8_t *stack_pos = ctx->stack_base;
+       uint32_t clear_code = ctx->clear_code;
+       uint32_t current_entry = ctx->current_entry;
+       struct lzw_dictionary_entry * const table = ctx->table;
+
+       /* Get a new code from the input */
+       res = lzw__next_code(&ctx->input, ctx->current_code_size, &code_new);
+       if (res != LZW_OK) {
+               return res;
+       }
+
+       /* Handle the new code */
+       if (code_new == clear_code) {
+               /* Got Clear code */
+               return lzw__clear_codes(ctx, stack_pos_out);
+
+       } else if (code_new == ctx->eoi_code) {
+               /* Got End of Information code */
+               return LZW_EOI_CODE;
+
+       } else if (code_new > current_entry) {
+               /* Code is invalid */
+               return LZW_BAD_CODE;
+
+       } else if (code_new < current_entry) {
+               /* Code is in table */
+               code_out = code_new;
+               last_value = table[code_new].first_value;
+       } else {
+               /* Code not in table */
+               *stack_pos++ = ctx->previous_code_first;
+               code_out = ctx->previous_code;
+               last_value = ctx->previous_code_first;
+       }
+
+       /* Add to the dictionary, only if there's space */
+       if (current_entry < (1 << LZW_CODE_MAX)) {
+               struct lzw_dictionary_entry *entry = table + current_entry;
+               entry->last_value     = last_value;
+               entry->first_value    = ctx->previous_code_first;
+               entry->previous_entry = ctx->previous_code;
+               ctx->current_entry++;
+       }
+
+       /* Ensure code size is increased, if needed. */
+       if (current_entry == ctx->current_code_size_max) {
+               if (ctx->current_code_size < LZW_CODE_MAX) {
+                       ctx->current_code_size++;
+                       ctx->current_code_size_max =
+                                       (1 << ctx->current_code_size) - 1;
+               }
+       }
+
+       /* Store details of this code as "previous code" to the context. */
+       ctx->previous_code_first = table[code_new].first_value;
+       ctx->previous_code = code_new;
+
+       /* Put rest of data for this code on output stack.
+        * Note, in the case of "code not in table", the last entry of the
+        * current code has already been placed on the stack above. */
+       while (code_out > clear_code) {
+               struct lzw_dictionary_entry *entry = table + code_out;
+               *stack_pos++ = entry->last_value;
+               code_out = entry->previous_entry;
+       }
+       *stack_pos++ = table[code_out].last_value;
+
+       *stack_pos_out = stack_pos;
+       return LZW_OK;
+}
diff --git a/subprojects/libnsgif/lzw.h b/subprojects/libnsgif/lzw.h
new file mode 100644
index 000000000..385b42557
--- /dev/null
+++ b/subprojects/libnsgif/lzw.h
@@ -0,0 +1,105 @@
+/*
+ * This file is part of NetSurf's LibNSGIF, http://www.netsurf-browser.org/
+ * Licensed under the MIT License,
+ *                http://www.opensource.org/licenses/mit-license.php
+ *
+ * Copyright 2017 Michael Drake <michael drake codethink co uk>
+ */
+
+#ifndef LZW_H_
+#define LZW_H_
+
+/**
+ * \file
+ * \brief LZW decompression (interface)
+ *
+ * Decoder for GIF LZW data.
+ */
+
+
+/** Maximum LZW code size in bits */
+#define LZW_CODE_MAX 12
+
+
+/* Declare lzw internal context structure */
+struct lzw_ctx;
+
+
+/** LZW decoding response codes */
+typedef enum lzw_result {
+       LZW_OK,        /**< Success */
+       LZW_OK_EOD,    /**< Success; reached zero-length sub-block */
+       LZW_NO_MEM,    /**< Error: Out of memory */
+       LZW_NO_DATA,   /**< Error: Out of data */
+       LZW_EOI_CODE,  /**< Error: End of Information code */
+       LZW_BAD_ICODE, /**< Error: Bad initial LZW code */
+       LZW_BAD_CODE,  /**< Error: Bad LZW code */
+} lzw_result;
+
+
+/**
+ * Create an LZW decompression context.
+ *
+ * \param[out] ctx  Returns an LZW decompression context.  Caller owned,
+ *                  free with lzw_context_destroy().
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_context_create(
+               struct lzw_ctx **ctx);
+
+/**
+ * Destroy an LZW decompression context.
+ *
+ * \param[in] ctx  The LZW decompression context to destroy.
+ */
+void lzw_context_destroy(
+               struct lzw_ctx *ctx);
+
+/**
+ * Initialise an LZW decompression context for decoding.
+ *
+ * Caller owns neither `stack_base_out` or `stack_pos_out`.
+ *
+ * \param[in]  ctx                  The LZW decompression context to initialise.
+ * \param[in]  compressed_data      The compressed data.
+ * \param[in]  compressed_data_len  Byte length of compressed data.
+ * \param[in]  compressed_data_pos  Start position in data.  Must be position
+ *                                  of a size byte at sub-block start.
+ * \param[in]  code_size            The initial LZW code size to use.
+ * \param[out] stack_base_out       Returns base of decompressed data stack.
+ * \param[out] stack_pos_out        Returns current stack position.
+ *                                  There are `stack_pos_out - stack_base_out`
+ *                                  current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode_init(
+               struct lzw_ctx *ctx,
+               const uint8_t *compressed_data,
+               uint32_t compressed_data_len,
+               uint32_t compressed_data_pos,
+               uint8_t code_size,
+               const uint8_t ** const stack_base_out,
+               const uint8_t ** const stack_pos_out);
+
+/**
+ * Fill the LZW stack with decompressed data
+ *
+ * Ensure anything on the stack is used before calling this, as anything
+ * on the stack before this call will be trampled.
+ *
+ * Caller does not own `stack_pos_out`.
+ *
+ * \param[in]  ctx            LZW reading context, updated.
+ * \param[out] stack_pos_out  Returns current stack position.
+ *                            Use with `stack_base_out` value from previous
+ *                            lzw_decode_init() call.
+ *                            There are `stack_pos_out - stack_base_out`
+ *                            current stack entries.
+ * \return LZW_OK on success, or appropriate error code otherwise.
+ */
+lzw_result lzw_decode(
+               struct lzw_ctx *ctx,
+               const uint8_t ** const stack_pos_out);
+
+
+#endif
diff --git a/subprojects/libnsgif/meson.build b/subprojects/libnsgif/meson.build
new file mode 100644
index 000000000..273d4ca26
--- /dev/null
+++ b/subprojects/libnsgif/meson.build
@@ -0,0 +1,16 @@
+project('libnsgif',
+  'c',
+  meson_version: '>=0.0.0',
+)
+
+
+libnsgif_lib = static_library('nsgif',
+  'libnsgif.c',
+  'lzw.c',
+)
+
+
+libnsgif = declare_dependency(
+  link_with: libnsgif_lib,
+  include_directories: '.'
+)
diff --git a/subprojects/poly2tri-c/COPYING b/subprojects/poly2tri-c/COPYING
new file mode 100644
index 000000000..11f143f03
--- /dev/null
+++ b/subprojects/poly2tri-c/COPYING
@@ -0,0 +1,40 @@
+The Poly2Tri-C is a three-part library, where all parts are licensed
+under the same license - The BSD 3-Clause License (also known as "New
+BSD License" or "Modified BSD License"):
+
+  1. Constrained Delaunay Triangulation - Based on the code of Poly2Tri
+     (Poly2Tri is licensed under the same license)
+  2. Delaunay Refinement - New code implemented in this library
+  3. Mesh Rendering - New code implemented in this library
+
+The first part of the library is a port of the Poly2Tri library from C++
+to C (with GLib). This port can be found inside the poly2tri-c/p2t
+directory of this source code distribution.
+
+  Poly2Tri - A 2D constrained Delaunay triangulation library
+  Homepage: http://code.google.com/p/poly2tri/
+  Code License File: LICENSE-Poly2Tri.txt
+
+The second part of the library is an implementaiton of a Delaunay
+Refinement algorithm named "The Terminator" which appears in a paper
+written by Jonathan Richard Shewchuk.
+The implementation of the algorithm in C was done by Barak Itkin, and it
+can be found inside the poly2tri-c/refine directory of this source code
+distribution.
+
+  Algorithm Author:
+    Jonathan Richard Shewchuk
+    http://www.cs.berkeley.edu/~jrs/
+
+  Algorithm Paper:
+    Delaunay Refinement Algorithms for Triangular Mesh Generation
+    http://www.cs.berkeley.edu/~jrs/papers/2dj.pdf
+
+  Code License File: LICENSE-Poly2Tri-C.txt
+
+Finally, the third part of the library contains various techniques to
+render mesh objects as raster/vector images. This part is 'original' and
+is not based on any other reference. This code can be found inside the
+poly2tri-c/render directory of this source code distribution.
+
+  Code License File: LICENSE-Poly2Tri-C.txt
diff --git a/subprojects/poly2tri-c/LICENSE-Poly2Tri-C.txt b/subprojects/poly2tri-c/LICENSE-Poly2Tri-C.txt
new file mode 100644
index 000000000..097ab2403
--- /dev/null
+++ b/subprojects/poly2tri-c/LICENSE-Poly2Tri-C.txt
@@ -0,0 +1,30 @@
+Poly2Tri-C Copyright (c) 2011-2012, Barak Itkin
+http://code.google.com/p/poly2tri-c/
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+ * Neither the name of Poly2Tri-C nor the names of its contributors may
+   be used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/subprojects/poly2tri-c/LICENSE-Poly2Tri.txt b/subprojects/poly2tri-c/LICENSE-Poly2Tri.txt
new file mode 100644
index 000000000..79fa68702
--- /dev/null
+++ b/subprojects/poly2tri-c/LICENSE-Poly2Tri.txt
@@ -0,0 +1,31 @@
+
+Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+http://code.google.com/p/poly2tri/
+
+All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+
+ * Redistributions of source code must retain the above copyright
+   notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above copyright
+   notice, this list of conditions and the following disclaimer in the
+   documentation and/or other materials provided with the distribution.
+ * Neither the name of Poly2Tri nor the names of its contributors may be
+   used to endorse or promote products derived from this software
+   without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS
+IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED
+TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A
+PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER
+OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/subprojects/poly2tri-c/README b/subprojects/poly2tri-c/README
new file mode 100644
index 000000000..3f39b1431
--- /dev/null
+++ b/subprojects/poly2tri-c/README
@@ -0,0 +1,13 @@
+========================================================================
+====  Poly2Tri-C: A library for generating, refining and rendering  ====
+====        2-Dimensional Constrained Delaunay Triangulations       ====
+========================================================================
+
+This is a minimal version of the Poly2Tri-C library, required to compile
+the seamless cloning operation. For more information, and in order to
+get the full library, see http://code.google.com/p/poly2tri-c
+
+Poly2Tri-C Version info:
+
+    CURRENT REVISION: b27c5b79df2ffa4e2cb37f9e5536831f16afb11b
+    CACHED ON: August 11th, 2012
diff --git a/subprojects/poly2tri-c/meson.build b/subprojects/poly2tri-c/meson.build
new file mode 100644
index 000000000..280275235
--- /dev/null
+++ b/subprojects/poly2tri-c/meson.build
@@ -0,0 +1,31 @@
+project('poly2tri-c', 'c',
+  license: 'GPL3+',
+  version: '0.1.0',
+  meson_version: '>=0.40.0',
+  default_options: [
+    'sysconfdir=/etc',
+    'localstatedir=/var',
+    'sharedstatedir=/var/lib'
+  ],
+)
+
+glib = dependency('glib-2.0')
+math = meson.get_compiler('c').find_library('m')
+
+rootInclude = include_directories('.')
+
+subdir('poly2tri-c/p2t')
+subdir('poly2tri-c/refine')
+subdir('poly2tri-c/render')
+
+
+libpoly2tri_c = static_library('poly2tri-c',
+  link_with: [ libp2tc, libp2tc_refine, libp2tc_render, ],
+)
+
+
+poly2tri_c = declare_dependency(
+  include_directories: [ rootInclude, ],
+  link_with: [ libpoly2tri_c, ],
+  version: meson.project_version(),
+)
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/common/cutils.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/common/cutils.h
new file mode 100644
index 000000000..596078e5f
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/common/cutils.h
@@ -0,0 +1,67 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_P2T_CUTILS_H__
+#define        __P2TC_P2T_CUTILS_H__
+
+#include <glib.h>
+#include "poly2tri-private.h"
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#define DEBUG FALSE
+
+  typedef GPtrArray* P2tEdgePtrArray;
+#define edge_index(array,index_) ((P2tEdge*)g_ptr_array_index(array,index_))
+  typedef GPtrArray* P2tPointPtrArray;
+#define point_index(array,index_) ((P2tPoint*)g_ptr_array_index(array,index_))
+  typedef GPtrArray* P2tTrianglePtrArray;
+#define triangle_index(array,index_) ((P2tTriangle*)g_ptr_array_index(array,index_))
+  typedef GPtrArray* P2tNodePtrArray;
+#define node_index(array,index_) ((P2tNode*)g_ptr_array_index(array,index_))
+  typedef GList* P2tTrianglePtrList;
+#define triangle_val(list) ((P2tTriangle*)((list)->data))
+
+#define g_ptr_array_index_cyclic(array,index_) g_ptr_array_index(array,(index_)%((array)->len))
+  
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* CUTILS_H */
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/common/meson.build 
b/subprojects/poly2tri-c/poly2tri-c/p2t/common/meson.build
new file mode 100644
index 000000000..e6cd6c93e
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/common/meson.build
@@ -0,0 +1,5 @@
+
+libp2tc_common_sources = files(
+  'shapes.c',
+  'utils.c'
+)
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/common/poly2tri-private.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/common/poly2tri-private.h
new file mode 100644
index 000000000..0be3193f7
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/common/poly2tri-private.h
@@ -0,0 +1,62 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_P2T_POLY2TRI_PRIVATE_H__
+#define        __P2TC_P2T_POLY2TRI_PRIVATE_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+typedef struct _P2tNode P2tNode;
+typedef struct AdvancingFront_ P2tAdvancingFront;
+typedef struct CDT_ P2tCDT;
+typedef struct _P2tEdge P2tEdge;
+typedef struct _P2tPoint P2tPoint;
+typedef struct _P2tTriangle P2tTriangle;
+typedef struct SweepContext_ P2tSweepContext;
+typedef struct Sweep_ P2tSweep;
+
+typedef struct P2tSweepContextBasin_ P2tSweepContextBasin;
+typedef struct P2tSweepContextEdgeEvent_ P2tSweepContextEdgeEvent;
+
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif /* POLY2TRI_PRIVATE_H */
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/common/shapes.c 
b/subprojects/poly2tri-c/poly2tri-c/p2t/common/shapes.c
new file mode 100644
index 000000000..463ac50ea
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/common/shapes.c
@@ -0,0 +1,677 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "shapes.h"
+#include <stdio.h>
+#include <stdlib.h>
+
+/** Default constructor does nothing (for performance). */
+
+void
+p2t_point_init (P2tPoint* THIS)
+{
+  THIS->x = 0;
+  THIS->y = 0;
+  THIS->edge_list = g_ptr_array_new ();
+}
+
+P2tPoint*
+p2t_point_new (void)
+{
+  P2tPoint* THIS = g_slice_new (P2tPoint);
+  p2t_point_init (THIS);
+  return THIS;
+}
+
+/** Construct using coordinates. */
+
+void
+p2t_point_init_dd (P2tPoint* THIS, double x, double y)
+{
+  THIS->x = x;
+  THIS->y = y;
+  THIS->edge_list = g_ptr_array_new ();
+}
+
+P2tPoint*
+p2t_point_new_dd (double x, double y)
+{
+  P2tPoint* THIS = g_slice_new (P2tPoint);
+  p2t_point_init_dd (THIS, x, y);
+  return THIS;
+}
+
+void
+p2t_point_destroy (P2tPoint* THIS)
+{
+  g_ptr_array_free (THIS->edge_list, TRUE);
+}
+
+void
+p2t_point_free (P2tPoint* THIS)
+{
+  p2t_point_destroy (THIS);
+  g_slice_free (P2tPoint, THIS);
+}
+
+/** Constructor */
+
+void
+p2t_edge_init (P2tEdge* THIS, P2tPoint* p1, P2tPoint* p2)
+{
+  THIS->p = p1;
+  THIS->q = p2;
+  if (p1->y > p2->y)
+    {
+      THIS->q = p1;
+      THIS->p = p2;
+    }
+  else if (p1->y == p2->y)
+    {
+      if (p1->x > p2->x)
+        {
+          THIS->q = p1;
+          THIS->p = p2;
+        }
+      else if (p1->x == p2->x)
+        {
+          /* Repeat points */
+          assert (FALSE);
+        }
+    }
+
+  g_ptr_array_add (THIS->q->edge_list, THIS);
+}
+
+P2tEdge*
+p2t_edge_new (P2tPoint* p1, P2tPoint* p2)
+{
+  P2tEdge* THIS = g_slice_new (P2tEdge);
+  p2t_edge_init (THIS, p1, p2);
+  return THIS;
+}
+
+void
+p2t_edge_destroy (P2tEdge* THIS) { }
+
+void
+p2t_edge_free (P2tEdge* THIS)
+{
+  p2t_edge_destroy (THIS);
+  g_slice_free (P2tEdge, THIS);
+}
+
+P2tTriangle*
+p2t_triangle_new (P2tPoint* a, P2tPoint* b, P2tPoint* c)
+{
+  P2tTriangle *tr = g_new (P2tTriangle, 1);
+  p2t_triangle_init (tr, a, b, c);
+  return tr;
+}
+
+void
+p2t_triangle_init (P2tTriangle* THIS, P2tPoint* a, P2tPoint* b, P2tPoint* c)
+{
+  THIS->points_[0] = a;
+  THIS->points_[1] = b;
+  THIS->points_[2] = c;
+  THIS->neighbors_[0] = NULL;
+  THIS->neighbors_[1] = NULL;
+  THIS->neighbors_[2] = NULL;
+  THIS->constrained_edge[0] = THIS->constrained_edge[1] = THIS->constrained_edge[2] = FALSE;
+  THIS->delaunay_edge[0] = THIS->delaunay_edge[1] = THIS->delaunay_edge[2] = FALSE;
+  THIS->interior_ = FALSE;
+
+}
+/* Update neighbor pointers */
+
+void
+p2t_triangle_mark_neighbor_pt_pt_tr (P2tTriangle* THIS, P2tPoint* p1, P2tPoint* p2, P2tTriangle* t)
+{
+  if ((p1 == THIS->points_[2] && p2 == THIS->points_[1]) || (p1 == THIS->points_[1] && p2 == 
THIS->points_[2]))
+    THIS->neighbors_[0] = t;
+  else if ((p1 == THIS->points_[0] && p2 == THIS->points_[2]) || (p1 == THIS->points_[2] && p2 == 
THIS->points_[0]))
+    THIS->neighbors_[1] = t;
+  else if ((p1 == THIS->points_[0] && p2 == THIS->points_[1]) || (p1 == THIS->points_[1] && p2 == 
THIS->points_[0]))
+    THIS->neighbors_[2] = t;
+  else
+    assert (0);
+}
+
+/* Exhaustive search to update neighbor pointers */
+
+void
+p2t_triangle_mark_neighbor_tr (P2tTriangle* THIS, P2tTriangle *t)
+{
+  if (p2t_triangle_contains_pt_pt (t, THIS->points_[1], THIS->points_[2]))
+    {
+      THIS->neighbors_[0] = t;
+      p2t_triangle_mark_neighbor_pt_pt_tr (t, THIS->points_[1], THIS->points_[2], THIS);
+    }
+  else if (p2t_triangle_contains_pt_pt (t, THIS->points_[0], THIS->points_[2]))
+    {
+      THIS->neighbors_[1] = t;
+      p2t_triangle_mark_neighbor_pt_pt_tr (t, THIS->points_[0], THIS->points_[2], THIS);
+    }
+  else if (p2t_triangle_contains_pt_pt (t, THIS->points_[0], THIS->points_[1]))
+    {
+      THIS->neighbors_[2] = t;
+      p2t_triangle_mark_neighbor_pt_pt_tr (t, THIS->points_[0], THIS->points_[1], THIS);
+    }
+}
+
+/**
+ * Clears all references to all other triangles and points
+ */
+void
+p2t_triangle_clear (P2tTriangle* THIS)
+{
+  int i;
+  P2tTriangle *t;
+  for (i = 0; i < 3; i++)
+    {
+      t = THIS->neighbors_[i];
+      if (t != NULL)
+        {
+          p2t_triangle_clear_neighbor_tr (t, THIS);
+        }
+    }
+  p2t_triangle_clear_neighbors (THIS);
+  THIS->points_[0] = THIS->points_[1] = THIS->points_[2] = NULL;
+}
+
+void
+p2t_triangle_clear_neighbor_tr (P2tTriangle* THIS, P2tTriangle *triangle)
+{
+  if (THIS->neighbors_[0] == triangle)
+    {
+      THIS->neighbors_[0] = NULL;
+    }
+  else if (THIS->neighbors_[1] == triangle)
+    {
+      THIS->neighbors_[1] = NULL;
+    }
+  else
+    {
+      THIS->neighbors_[2] = NULL;
+    }
+}
+
+void
+p2t_triangle_clear_neighbors (P2tTriangle* THIS)
+{
+  THIS->neighbors_[0] = NULL;
+  THIS->neighbors_[1] = NULL;
+  THIS->neighbors_[2] = NULL;
+}
+
+void
+p2t_triangle_clear_delunay_edges (P2tTriangle* THIS)
+{
+  THIS->delaunay_edge[0] = THIS->delaunay_edge[1] = THIS->delaunay_edge[2] = FALSE;
+}
+
+P2tPoint*
+p2t_triangle_opposite_point (P2tTriangle* THIS, P2tTriangle* t, P2tPoint* p)
+{
+  P2tPoint *cw = p2t_triangle_point_cw (t, p);
+  /*double x = cw->x;
+  double y = cw->y;
+  x = p->x;
+  y = p->y;
+  P2tPoint* ham = */p2t_triangle_point_cw (THIS, cw);
+  return p2t_triangle_point_cw (THIS, cw);
+}
+
+/* Legalized triangle by rotating clockwise around point(0) */
+
+void
+p2t_triangle_legalize_pt (P2tTriangle* THIS, P2tPoint *point)
+{
+  THIS->points_[1] = THIS->points_[0];
+  THIS->points_[0] = THIS->points_[2];
+  THIS->points_[2] = point;
+}
+
+/* Legalize triagnle by rotating clockwise around oPoint */
+
+void
+p2t_triangle_legalize_pt_pt (P2tTriangle* THIS, P2tPoint *opoint, P2tPoint *npoint)
+{
+  if (opoint == THIS->points_[0])
+    {
+      THIS->points_[1] = THIS->points_[0];
+      THIS->points_[0] = THIS->points_[2];
+      THIS->points_[2] = npoint;
+    }
+  else if (opoint == THIS->points_[1])
+    {
+      THIS->points_[2] = THIS->points_[1];
+      THIS->points_[1] = THIS->points_[0];
+      THIS->points_[0] = npoint;
+    }
+  else if (opoint == THIS->points_[2])
+    {
+      THIS->points_[0] = THIS->points_[2];
+      THIS->points_[2] = THIS->points_[1];
+      THIS->points_[1] = npoint;
+    }
+  else
+    {
+      assert (0);
+    }
+}
+
+int
+p2t_triangle_index (P2tTriangle* THIS, const P2tPoint* p)
+{
+  if (p == THIS->points_[0])
+    {
+      return 0;
+    }
+  else if (p == THIS->points_[1])
+    {
+      return 1;
+    }
+  else if (p == THIS->points_[2])
+    {
+      return 2;
+    }
+  assert (0);
+}
+
+int
+p2t_triangle_edge_index (P2tTriangle* THIS, const P2tPoint* p1, const P2tPoint* p2)
+{
+  if (THIS->points_[0] == p1)
+    {
+      if (THIS->points_[1] == p2)
+        {
+          return 2;
+        }
+      else if (THIS->points_[2] == p2)
+        {
+          return 1;
+        }
+    }
+  else if (THIS->points_[1] == p1)
+    {
+      if (THIS->points_[2] == p2)
+        {
+          return 0;
+        }
+      else if (THIS->points_[0] == p2)
+        {
+          return 2;
+        }
+    }
+  else if (THIS->points_[2] == p1)
+    {
+      if (THIS->points_[0] == p2)
+        {
+          return 1;
+        }
+      else if (THIS->points_[1] == p2)
+        {
+          return 0;
+        }
+    }
+  return -1;
+}
+
+void
+p2t_triangle_mark_constrained_edge_i (P2tTriangle* THIS, const int index)
+{
+  THIS->constrained_edge[index] = TRUE;
+}
+
+void
+p2t_triangle_mark_constrained_edge_ed (P2tTriangle* THIS, P2tEdge* edge)
+{
+  p2t_triangle_mark_constrained_edge_pt_pt (THIS, edge->p, edge->q);
+}
+
+/* Mark edge as constrained */
+
+void
+p2t_triangle_mark_constrained_edge_pt_pt (P2tTriangle* THIS, P2tPoint* p, P2tPoint* q)
+{
+  if ((q == THIS->points_[0] && p == THIS->points_[1]) || (q == THIS->points_[1] && p == THIS->points_[0]))
+    {
+      THIS->constrained_edge[2] = TRUE;
+    }
+  else if ((q == THIS->points_[0] && p == THIS->points_[2]) || (q == THIS->points_[2] && p == 
THIS->points_[0]))
+    {
+      THIS->constrained_edge[1] = TRUE;
+    }
+  else if ((q == THIS->points_[1] && p == THIS->points_[2]) || (q == THIS->points_[2] && p == 
THIS->points_[1]))
+    {
+      THIS->constrained_edge[0] = TRUE;
+    }
+}
+
+/* The point counter-clockwise to given point */
+
+P2tPoint*
+p2t_triangle_point_cw (P2tTriangle* THIS, P2tPoint* point)
+{
+  if (point == THIS->points_[0])
+    {
+      return THIS->points_[2];
+    }
+  else if (point == THIS->points_[1])
+    {
+      return THIS->points_[0];
+    }
+  else if (point == THIS->points_[2])
+    {
+      return THIS->points_[1];
+    }
+  assert (0);
+}
+
+/* The point counter-clockwise to given point */
+
+P2tPoint*
+p2t_triangle_point_ccw (P2tTriangle* THIS, P2tPoint* point)
+{
+  if (point == THIS->points_[0])
+    {
+      return THIS->points_[1];
+    }
+  else if (point == THIS->points_[1])
+    {
+      return THIS->points_[2];
+    }
+  else if (point == THIS->points_[2])
+    {
+      return THIS->points_[0];
+    }
+  assert (0);
+}
+
+/* The neighbor clockwise to given point */
+
+P2tTriangle*
+p2t_triangle_neighbor_cw (P2tTriangle* THIS, P2tPoint* point)
+{
+  if (point == THIS->points_[0])
+    {
+      return THIS->neighbors_[1];
+    }
+  else if (point == THIS->points_[1])
+    {
+      return THIS->neighbors_[2];
+    }
+  return THIS->neighbors_[0];
+}
+
+/* The neighbor counter-clockwise to given point */
+
+P2tTriangle*
+p2t_triangle_neighbor_ccw (P2tTriangle* THIS, P2tPoint* point)
+{
+  if (point == THIS->points_[0])
+    {
+      return THIS->neighbors_[2];
+    }
+  else if (point == THIS->points_[1])
+    {
+      return THIS->neighbors_[0];
+    }
+  return THIS->neighbors_[1];
+}
+
+gboolean
+p2t_triangle_get_constrained_edge_ccw (P2tTriangle* THIS, P2tPoint* p)
+{
+  if (p == THIS->points_[0])
+    {
+      return THIS->constrained_edge[2];
+    }
+  else if (p == THIS->points_[1])
+    {
+      return THIS->constrained_edge[0];
+    }
+  return THIS->constrained_edge[1];
+}
+
+gboolean
+p2t_triangle_get_constrained_edge_cw (P2tTriangle* THIS, P2tPoint* p)
+{
+  if (p == THIS->points_[0])
+    {
+      return THIS->constrained_edge[1];
+    }
+  else if (p == THIS->points_[1])
+    {
+      return THIS->constrained_edge[2];
+    }
+  return THIS->constrained_edge[0];
+}
+
+void
+p2t_triangle_set_constrained_edge_ccw (P2tTriangle* THIS, P2tPoint* p, gboolean ce)
+{
+  if (p == THIS->points_[0])
+    {
+      THIS->constrained_edge[2] = ce;
+    }
+  else if (p == THIS->points_[1])
+    {
+      THIS->constrained_edge[0] = ce;
+    }
+  else
+    {
+      THIS->constrained_edge[1] = ce;
+    }
+}
+
+void
+p2t_triangle_set_constrained_edge_cw (P2tTriangle* THIS, P2tPoint* p, gboolean ce)
+{
+  if (p == THIS->points_[0])
+    {
+      THIS->constrained_edge[1] = ce;
+    }
+  else if (p == THIS->points_[1])
+    {
+      THIS->constrained_edge[2] = ce;
+    }
+  else
+    {
+      THIS->constrained_edge[0] = ce;
+    }
+}
+
+gboolean
+p2t_triangle_get_delunay_edge_ccw (P2tTriangle* THIS, P2tPoint* p)
+{
+  if (p == THIS->points_[0])
+    {
+      return THIS->delaunay_edge[2];
+    }
+  else if (p == THIS->points_[1])
+    {
+      return THIS->delaunay_edge[0];
+    }
+  return THIS->delaunay_edge[1];
+}
+
+gboolean
+p2t_triangle_get_delunay_edge_cw (P2tTriangle* THIS, P2tPoint* p)
+{
+  if (p == THIS->points_[0])
+    {
+      return THIS->delaunay_edge[1];
+    }
+  else if (p == THIS->points_[1])
+    {
+      return THIS->delaunay_edge[2];
+    }
+  return THIS->delaunay_edge[0];
+}
+
+void
+p2t_triangle_set_delunay_edge_ccw (P2tTriangle* THIS, P2tPoint* p, gboolean e)
+{
+  if (p == THIS->points_[0])
+    {
+      THIS->delaunay_edge[2] = e;
+    }
+  else if (p == THIS->points_[1])
+    {
+      THIS->delaunay_edge[0] = e;
+    }
+  else
+    {
+      THIS->delaunay_edge[1] = e;
+    }
+}
+
+void
+p2t_triangle_set_delunay_edge_cw (P2tTriangle* THIS, P2tPoint* p, gboolean e)
+{
+  if (p == THIS->points_[0])
+    {
+      THIS->delaunay_edge[1] = e;
+    }
+  else if (p == THIS->points_[1])
+    {
+      THIS->delaunay_edge[2] = e;
+    }
+  else
+    {
+      THIS->delaunay_edge[0] = e;
+    }
+}
+
+/* The neighbor across to given point */
+
+P2tTriangle*
+p2t_triangle_neighbor_across (P2tTriangle* THIS, P2tPoint* opoint)
+{
+  if (opoint == THIS->points_[0])
+    {
+      return THIS->neighbors_[0];
+    }
+  else if (opoint == THIS->points_[1])
+    {
+      return THIS->neighbors_[1];
+    }
+  return THIS->neighbors_[2];
+}
+
+void
+p2t_triangle_debug_print (P2tTriangle* THIS)
+{
+  printf ("%f,%f ", THIS->points_[0]->x, THIS->points_[0]->y);
+  printf ("%f,%f ", THIS->points_[1]->x, THIS->points_[1]->y);
+  printf ("%f,%f\n", THIS->points_[2]->x, THIS->points_[2]->y);
+}
+
+/* WARNING! the function for sorting a g_ptr_array expects to recieve
+ *          pointers to the pointers (double indirection)! */
+
+gint
+p2t_point_cmp (gconstpointer a, gconstpointer b)
+{
+  P2tPoint *ap = *((P2tPoint**) a), *bp = *((P2tPoint**) b);
+  if (ap->y < bp->y)
+    {
+      return -1;
+    }
+  else if (ap->y == bp->y)
+    {
+      /* Make sure q is point with greater x value */
+      if (ap->x < bp->x)
+        {
+          return -1;
+        }
+      else if (ap->x == bp->x)
+        return 0;
+    }
+  return 1;
+}
+
+/* gboolean operator == (const Point& a, const Point& b) */
+
+gboolean
+p2t_point_equals (const P2tPoint* a, const P2tPoint* b)
+{
+  return a->x == b->x && a->y == b->y;
+}
+
+P2tPoint*
+p2t_triangle_get_point (P2tTriangle* THIS, const int index)
+{
+  return THIS->points_[index];
+}
+
+P2tTriangle*
+p2t_triangle_get_neighbor (P2tTriangle* THIS, const int index)
+{
+  return THIS->neighbors_[index];
+}
+
+gboolean
+p2t_triangle_contains_pt (P2tTriangle* THIS, P2tPoint* p)
+{
+  return p == THIS->points_[0] || p == THIS->points_[1] || p == THIS->points_[2];
+}
+
+gboolean
+p2t_triangle_contains_ed (P2tTriangle* THIS, const P2tEdge* e)
+{
+  return p2t_triangle_contains_pt (THIS, e->p) && p2t_triangle_contains_pt (THIS, e->q);
+}
+
+gboolean
+p2t_triangle_contains_pt_pt (P2tTriangle* THIS, P2tPoint* p, P2tPoint* q)
+{
+  return p2t_triangle_contains_pt (THIS, p) && p2t_triangle_contains_pt (THIS, q);
+}
+
+gboolean
+p2t_triangle_is_interior (P2tTriangle* THIS)
+{
+  return THIS->interior_;
+}
+
+void
+p2t_triangle_is_interior_b (P2tTriangle* THIS, gboolean b)
+{
+  THIS->interior_ = b;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/common/shapes.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/common/shapes.h
new file mode 100644
index 000000000..ee2f1902c
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/common/shapes.h
@@ -0,0 +1,262 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_P2T_SHAPES_H__
+#define __P2TC_P2T_SHAPES_H__
+
+#include <stddef.h>
+#include <assert.h>
+#include <math.h>
+#include "poly2tri-private.h"
+#include "cutils.h"
+
+/**
+ * P2tPoint:
+ * @x: The x coordinate of the point
+ * @y: The y coordinate of the point
+ * @edge_list: The edges this point constitutes an upper ending point
+ *
+ * A struct to represent 2D points with double precision, and to keep track
+ * of the edges this point constitutes an upper ending point
+ */
+struct _P2tPoint
+{
+  /*< public >*/
+  P2tEdgePtrArray edge_list;
+  double x, y;
+};
+
+/**
+ * p2t_point_init_dd:
+ * @THIS: The #P2tPoint to initialize
+ * @x: The desired x coordinate of the point
+ * @y: The desired y coordinate of the point
+ *
+ * A function to initialize a #P2tPoint struct with the given coordinates. The
+ * struct must later be finalized by a call to #p2t_point_destroy
+ */
+void p2t_point_init_dd (P2tPoint* THIS, double x, double y);
+
+/**
+ * p2t_point_new_dd:
+ * @x: The desired x coordinate of the point
+ * @y: The desired y coordinate of the point
+ *
+ * A utility function to alloacte and initialize a #P2tPoint.
+ * See #p2t_point_init_dd. Note that when finished using the point, it must be
+ * freed by a call to #p2t_point_free and can not be freed like regular memory.
+ *
+ * Returns: The allocated and initialized point
+ */
+P2tPoint* p2t_point_new_dd (double x, double y);
+
+/**
+ * p2t_point_init:
+ * @THIS: The #P2tPoint to initialize
+ *
+ * A function to initialize a #P2tPoint struct to (0,0). The struct must later
+ * be finalized by a call to #p2t_point_destroy
+ */
+void p2t_point_init (P2tPoint* THIS);
+
+/**
+ * p2t_point_new:
+ *
+ * A utility function to alloacte and initialize a #P2tPoint.
+ * See #p2t_point_init. Note that when finished using the point, it must be
+ * freed by a call to #p2t_point_free and can not be freed like regular memory.
+ */
+P2tPoint* p2t_point_new (void);
+
+/**
+ * p2t_point_destroy:
+ * @THIS: The #P2tPoint whose resources should be freed
+ *
+ * This function will free all the resources allocated by a #P2tPoint, without
+ * freeing the #P2tPoint pointed by @THIS
+ */
+void p2t_point_destroy (P2tPoint* THIS);
+
+/**
+ * p2t_point_free:
+ * @THIS: The #P2tPoint to free
+ *
+ * This function will free all the resources allocated by a #P2tPoint, and will
+ * also free the #P2tPoint pointed by @THIS
+ */
+void p2t_point_free (P2tPoint* THIS);
+
+/**
+ * P2tEdge:
+ * @p: The top right point of the edge
+ * @q: The bottom left point of the edge
+ *
+ * Represents a simple polygon's edge
+ */
+struct _P2tEdge
+{
+  P2tPoint *p, *q;
+};
+
+/**
+ * p2t_edge_init:
+ * @THIS: The #P2tEdge to initialize
+ * @p1: One of the two points that form the edge
+ * @p2: The other point of the two points that form the edge
+ *
+ * A function to initialize a #P2tEdge struct from the given points. The
+ * struct must later be finalized by a call to #p2t_point_destroy.
+ *
+ * Warning: The points must be geometrically not-equal! This means that they
+ * must differ by at least one of their coordinates. Otherwise, a runtime error
+ * would be raised!
+ */
+void p2t_edge_init (P2tEdge* THIS, P2tPoint* p1, P2tPoint* p2);
+
+/**
+ * p2t_edge_new:
+ *
+ * A utility function to alloacte and initialize a #P2tEdge.
+ * See #p2t_edge_init. Note that when finished using the point, it must be freed
+ * by a call to #p2t_point_free and can not be freed like regular memory.
+ *
+ * Returns: The allocated and initialized edge
+ */
+P2tEdge* p2t_edge_new (P2tPoint* p1, P2tPoint* p2);
+
+/**
+ * p2t_edge_destroy:
+ * @THIS: The #P2tEdge whose resources should be freed
+ *
+ * This function will free all the resources allocated by a #P2tEdge, without
+ * freeing the #P2tPoint pointed by @THIS
+ */
+void p2t_edge_destroy (P2tEdge* THIS);
+
+/**
+ * p2t_edge_free:
+ * @THIS: The #P2tEdge to free
+ *
+ * This function will free all the resources allocated by a #P2tEdge, and will
+ * also free the #P2tEdge pointed by @THIS
+ */
+void p2t_edge_free (P2tEdge* THIS);
+
+
+/* Triangle-based data structures are know to have better performance than quad-edge structures
+ * See: J. Shewchuk, "Triangle: Engineering a 2D Quality Mesh Generator and Delaunay Triangulator"
+ *      "Triangulations in CGAL"
+ */
+
+/**
+ * P2tTriangle:
+ * @constrained_edge: Flags to determine if an edge is a Constrained edge
+ * @delaunay_edg: Flags to determine if an edge is a Delauney edge
+ * @points_: Triangle points
+ * @neighbors_: Neighbor list
+ * @interior_: Has this triangle been marked as an interior triangle?
+ *
+ * A data structure for representing a triangle, while keeping information about
+ * neighbor triangles, etc.
+ */
+struct _P2tTriangle
+{
+  /*< public >*/
+  gboolean constrained_edge[3];
+  gboolean delaunay_edge[3];
+
+  /*< private >*/
+  P2tPoint * points_[3];
+  struct _P2tTriangle * neighbors_[3];
+  gboolean interior_;
+};
+
+P2tTriangle* p2t_triangle_new (P2tPoint* a, P2tPoint* b, P2tPoint* c);
+void p2t_triangle_init (P2tTriangle* THIS, P2tPoint* a, P2tPoint* b, P2tPoint* c);
+P2tPoint* p2t_triangle_get_point (P2tTriangle* THIS, const int index);
+P2tPoint* p2t_triangle_point_cw (P2tTriangle* THIS, P2tPoint* point);
+P2tPoint* p2t_triangle_point_ccw (P2tTriangle* THIS, P2tPoint* point);
+P2tPoint* p2t_triangle_opposite_point (P2tTriangle* THIS, P2tTriangle* t, P2tPoint* p);
+
+P2tTriangle* p2t_triangle_get_neighbor (P2tTriangle* THIS, const int index);
+void p2t_triangle_mark_neighbor_pt_pt_tr (P2tTriangle* THIS, P2tPoint* p1, P2tPoint* p2, P2tTriangle* t);
+void p2t_triangle_mark_neighbor_tr (P2tTriangle* THIS, P2tTriangle *t);
+
+void p2t_triangle_mark_constrained_edge_i (P2tTriangle* THIS, const int index);
+void p2t_triangle_mark_constrained_edge_ed (P2tTriangle* THIS, P2tEdge* edge);
+void p2t_triangle_mark_constrained_edge_pt_pt (P2tTriangle* THIS, P2tPoint* p, P2tPoint* q);
+
+int p2t_triangle_index (P2tTriangle* THIS, const P2tPoint* p);
+int p2t_triangle_edge_index (P2tTriangle* THIS, const P2tPoint* p1, const P2tPoint* p2);
+
+P2tTriangle* p2t_triangle_neighbor_cw (P2tTriangle* THIS, P2tPoint* point);
+P2tTriangle* p2t_triangle_neighbor_ccw (P2tTriangle* THIS, P2tPoint* point);
+gboolean p2t_triangle_get_constrained_edge_ccw (P2tTriangle* THIS, P2tPoint* p);
+gboolean p2t_triangle_get_constrained_edge_cw (P2tTriangle* THIS, P2tPoint* p);
+void p2t_triangle_set_constrained_edge_ccw (P2tTriangle* THIS, P2tPoint* p, gboolean ce);
+void p2t_triangle_set_constrained_edge_cw (P2tTriangle* THIS, P2tPoint* p, gboolean ce);
+gboolean p2t_triangle_get_delunay_edge_ccw (P2tTriangle* THIS, P2tPoint* p);
+gboolean p2t_triangle_get_delunay_edge_cw (P2tTriangle* THIS, P2tPoint* p);
+void p2t_triangle_set_delunay_edge_ccw (P2tTriangle* THIS, P2tPoint* p, gboolean e);
+void p2t_triangle_set_delunay_edge_cw (P2tTriangle* THIS, P2tPoint* p, gboolean e);
+
+gboolean p2t_triangle_contains_pt (P2tTriangle* THIS, P2tPoint* p);
+gboolean p2t_triangle_contains_ed (P2tTriangle* THIS, const P2tEdge* e);
+gboolean p2t_triangle_contains_pt_pt (P2tTriangle* THIS, P2tPoint* p, P2tPoint* q);
+void p2t_triangle_legalize_pt (P2tTriangle* THIS, P2tPoint* point);
+void p2t_triangle_legalize_pt_pt (P2tTriangle* THIS, P2tPoint* opoint, P2tPoint* npoint);
+/**
+ * Clears all references to all other triangles and points
+ */
+void p2t_triangle_clear (P2tTriangle* THIS);
+void p2t_triangle_clear_neighbor_tr (P2tTriangle* THIS, P2tTriangle *triangle);
+void p2t_triangle_clear_neighbors (P2tTriangle* THIS);
+void p2t_triangle_clear_delunay_edges (P2tTriangle* THIS);
+
+gboolean p2t_triangle_is_interior (P2tTriangle* THIS);
+void p2t_triangle_is_interior_b (P2tTriangle* THIS, gboolean b);
+
+P2tTriangle* p2t_triangle_neighbor_across (P2tTriangle* THIS, P2tPoint* opoint);
+
+void p2t_triangle_debug_print (P2tTriangle* THIS);
+
+gint p2t_point_cmp (gconstpointer a, gconstpointer b);
+
+/*  gboolean operator == (const Point& a, const Point& b); */
+gboolean p2t_point_equals (const P2tPoint* a, const P2tPoint* b);
+
+#endif
+
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/common/utils.c 
b/subprojects/poly2tri-c/poly2tri-c/p2t/common/utils.c
new file mode 100644
index 000000000..bc186fc94
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/common/utils.c
@@ -0,0 +1,114 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include "utils.h"
+
+/**
+ * Forumla to calculate signed area<br>
+ * Positive if CCW<br>
+ * Negative if CW<br>
+ * 0 if collinear<br>
+ * <pre>
+ * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * </pre>
+ */
+P2tOrientation
+p2t_orient2d (P2tPoint* pa, P2tPoint* pb, P2tPoint* pc)
+{
+  double detleft = (pa->x - pc->x) * (pb->y - pc->y);
+  double detright = (pa->y - pc->y) * (pb->x - pc->x);
+  double val = detleft - detright;
+  if (val > -EPSILON && val < EPSILON)
+    {
+      return COLLINEAR;
+    }
+  else if (val > 0)
+    {
+      return CCW;
+    }
+  return CW;
+}
+
+gboolean
+p2t_utils_in_scan_area (P2tPoint* pa, P2tPoint* pb, P2tPoint* pc, P2tPoint* pd)
+{
+#if FALSE
+  double pdx = pd->x;
+  double pdy = pd->y;
+  double adx = pa->x - pdx;
+  double ady = pa->y - pdy;
+  double bdx = pb->x - pdx;
+  double bdy = pb->y - pdy;
+
+  double adxbdy = adx * bdy;
+  double bdxady = bdx * ady;
+  double oabd = adxbdy - bdxady;
+
+  double cdx, cdy;
+  double cdxady, adxcdy, ocad;
+
+  if (oabd <= EPSILON)
+    {
+      return FALSE;
+    }
+
+  cdx = pc->x - pdx;
+  cdy = pc->y - pdy;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+  ocad = cdxady - adxcdy;
+
+  if (ocad <= EPSILON)
+    {
+      return FALSE;
+    }
+
+  return TRUE;
+#else
+  gdouble oadc, oadb = (pa->x - pb->x)*(pd->y - pb->y) - (pd->x - pb->x)*(pa->y - pb->y);
+  if (oadb >= -EPSILON) {
+    return FALSE;
+  }
+
+  oadc = (pa->x - pc->x)*(pd->y - pc->y) - (pd->x - pc->x)*(pa->y - pc->y);
+  if (oadc <= EPSILON) {
+    return FALSE;
+  }
+  return TRUE;
+#endif
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/common/utils.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/common/utils.h
new file mode 100644
index 000000000..18688bb16
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/common/utils.h
@@ -0,0 +1,67 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_P2T_UTILS_H__
+#define __P2TC_P2T_UTILS_H__
+
+#include <glib.h>
+#include "poly2tri-private.h"
+#include "cutils.h"
+#include "shapes.h"
+
+#define PI_3div4 (3 * G_PI / 4)
+#define EPSILON  (1e-6)
+
+typedef enum
+{
+  CW, CCW, COLLINEAR
+} P2tOrientation;
+
+/**
+ * Forumla to calculate signed area<br>
+ * Positive if CCW<br>
+ * Negative if CW<br>
+ * 0 if collinear<br>
+ * <pre>
+ * A[P1,P2,P3]  =  (x1*y2 - y1*x2) + (x2*y3 - y2*x3) + (x3*y1 - y3*x1)
+ *              =  (x1-x3)*(y2-y3) - (y1-y3)*(x2-x3)
+ * </pre>
+ */
+P2tOrientation p2t_orient2d (P2tPoint* pa, P2tPoint* pb, P2tPoint* pc);
+
+gboolean p2t_utils_in_scan_area (P2tPoint* pa, P2tPoint* pb, P2tPoint* pc, P2tPoint* pd);
+
+#endif
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/meson.build 
b/subprojects/poly2tri-c/poly2tri-c/p2t/meson.build
new file mode 100644
index 000000000..38a68369b
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/meson.build
@@ -0,0 +1,10 @@
+
+subdir('common')
+subdir('sweep')
+
+libp2tc = static_library('p2tc',
+  libp2tc_common_sources,
+  libp2tc_sweep_sources,
+  dependencies: [ glib, math, ],
+  include_directories: [ rootInclude, ],
+)
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/poly2tri.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/poly2tri.h
new file mode 100644
index 000000000..d34d5f6f3
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/poly2tri.h
@@ -0,0 +1,43 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_P2T_POLY2TRI_H__
+#define __P2TC_P2T_POLY2TRI_H__
+
+#include "common/shapes.h"
+#include "sweep/cdt.h"
+
+#endif
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/advancing_front.c 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/advancing_front.c
new file mode 100644
index 000000000..c9ab8f97d
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/advancing_front.c
@@ -0,0 +1,229 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "advancing_front.h"
+
+void
+p2t_node_init_pt (P2tNode* THIS, P2tPoint* p)
+{
+  THIS->point = p;
+  THIS->triangle = NULL;
+  THIS->value = p->x;
+  THIS->next = NULL;
+  THIS->prev = NULL;
+}
+
+P2tNode*
+p2t_node_new_pt (P2tPoint* p)
+{
+  P2tNode* THIS = g_slice_new (P2tNode);
+  p2t_node_init_pt (THIS, p);
+  return THIS;
+}
+
+void
+p2t_node_init_pt_tr (P2tNode* THIS, P2tPoint* p, P2tTriangle* t)
+{
+  THIS->point = p;
+  THIS->triangle = t;
+  THIS->value = p->x;
+  THIS->next = NULL;
+  THIS->prev = NULL;
+}
+
+P2tNode*
+p2t_node_new_pt_tr (P2tPoint* p, P2tTriangle* t)
+{
+  P2tNode* THIS = g_slice_new (P2tNode);
+  p2t_node_init_pt_tr (THIS, p, t);
+  return THIS;
+}
+
+void p2t_node_destroy (P2tNode* THIS)
+{
+}
+void p2t_node_free (P2tNode* THIS)
+{
+  p2t_node_destroy (THIS);
+  g_slice_free (P2tNode, THIS);
+}
+
+void
+p2t_advancingfront_init (P2tAdvancingFront* THIS, P2tNode* head, P2tNode* tail)
+{
+  THIS->head_ = head;
+  THIS->tail_ = tail;
+  THIS->search_node_ = head;
+}
+
+P2tAdvancingFront*
+p2t_advancingfront_new (P2tNode* head, P2tNode* tail)
+{
+  P2tAdvancingFront* THIS = g_slice_new (P2tAdvancingFront);
+  p2t_advancingfront_init (THIS, head, tail);
+  return THIS;
+}
+
+void
+p2t_advancingfront_destroy (P2tAdvancingFront* THIS) { }
+
+void
+p2t_advancingfront_free (P2tAdvancingFront* THIS)
+{
+  p2t_advancingfront_destroy (THIS);
+  g_slice_free (P2tAdvancingFront, THIS);
+}
+
+P2tNode*
+p2t_advancingfront_locate_node (P2tAdvancingFront *THIS, const double x)
+{
+  P2tNode* node = THIS->search_node_;
+
+  if (x < node->value)
+    {
+      while ((node = node->prev) != NULL)
+        {
+          if (x >= node->value)
+            {
+              THIS->search_node_ = node;
+              return node;
+            }
+        }
+    }
+  else
+    {
+      while ((node = node->next) != NULL)
+        {
+          if (x < node->value)
+            {
+              THIS->search_node_ = node->prev;
+              return node->prev;
+            }
+        }
+    }
+  return NULL;
+}
+
+P2tNode*
+p2t_advancingfront_find_search_node (P2tAdvancingFront *THIS, const double x)
+{
+  /* TODO: implement BST index */
+  return THIS->search_node_;
+}
+
+P2tNode*
+p2t_advancingfront_locate_point (P2tAdvancingFront *THIS, const P2tPoint* point)
+{
+  const double px = point->x;
+  P2tNode* node = p2t_advancingfront_find_search_node (THIS, px);
+  const double nx = node->point->x;
+
+  if (px == nx)
+    {
+      if (point != node->point)
+        {
+          /* We might have two nodes with same x value for a short time */
+          if (point == node->prev->point)
+            {
+              node = node->prev;
+            }
+          else if (point == node->next->point)
+            {
+              node = node->next;
+            }
+          else
+            {
+              assert (0);
+            }
+        }
+    }
+  else if (px < nx)
+    {
+      while ((node = node->prev) != NULL)
+        {
+          if (point == node->point)
+            {
+              break;
+            }
+        }
+    }
+  else
+    {
+      while ((node = node->next) != NULL)
+        {
+          if (point == node->point)
+            break;
+        }
+    }
+  if (node) THIS->search_node_ = node;
+  return node;
+}
+
+P2tNode*
+p2t_advancingfront_head (P2tAdvancingFront *THIS)
+{
+  return THIS->head_;
+}
+
+void
+AdvancingFront_set_head (P2tAdvancingFront *THIS, P2tNode* node)
+{
+  THIS->head_ = node;
+}
+
+P2tNode*
+p2t_advancingfront_tail (P2tAdvancingFront *THIS)
+{
+  return THIS->tail_;
+}
+
+void
+p2t_advancingfront_set_tail (P2tAdvancingFront *THIS, P2tNode* node)
+{
+  THIS->tail_ = node;
+}
+
+P2tNode*
+p2t_advancingfront_search (P2tAdvancingFront *THIS)
+{
+  return THIS->search_node_;
+}
+
+void
+p2t_advancingfront_set_search (P2tAdvancingFront *THIS, P2tNode* node)
+{
+  THIS->search_node_ = node;
+}
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/advancing_front.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/advancing_front.h
new file mode 100644
index 000000000..43f943e8d
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/advancing_front.h
@@ -0,0 +1,92 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_P2T_ADVANCING_FRONT_H__
+#define __P2TC_P2T_ADVANCING_FRONT_H__
+
+#include "../common/poly2tri-private.h"
+#include "../common/shapes.h"
+
+/* Advancing front node */
+
+struct _P2tNode
+{
+  P2tPoint* point;
+  P2tTriangle* triangle;
+
+  struct _P2tNode* next;
+  struct _P2tNode* prev;
+
+  double value;
+};
+
+void p2t_node_init_pt (P2tNode* THIS, P2tPoint* p);
+P2tNode* p2t_node_new_pt (P2tPoint* p);
+void p2t_node_init_pt_tr (P2tNode* THIS, P2tPoint* p, P2tTriangle* t);
+P2tNode* p2t_node_new_pt_tr (P2tPoint* p, P2tTriangle* t);
+void p2t_node_destroy (P2tNode* THIS);
+void p2t_node_free (P2tNode* THIS);
+
+/* Advancing front */
+
+struct AdvancingFront_
+{
+  /* private: */
+
+  P2tNode* head_, *tail_, *search_node_;
+
+};
+
+void p2t_advancingfront_init (P2tAdvancingFront* THIS, P2tNode* head, P2tNode* tail);
+P2tAdvancingFront* p2t_advancingfront_new (P2tNode* head, P2tNode* tail);
+
+void p2t_advancingfront_destroy (P2tAdvancingFront* THIS);
+void p2t_advancingfront_free (P2tAdvancingFront* THIS);
+
+P2tNode* p2t_advancingfront_head (P2tAdvancingFront *THIS);
+void AdvancingFront_set_head (P2tAdvancingFront *THIS, P2tNode* node);
+P2tNode* p2t_advancingfront_tail (P2tAdvancingFront *THIS);
+void p2t_advancingfront_set_tail (P2tAdvancingFront *THIS, P2tNode* node);
+P2tNode* p2t_advancingfront_search (P2tAdvancingFront *THIS);
+void p2t_advancingfront_set_search (P2tAdvancingFront *THIS, P2tNode* node);
+
+/** Locate insertion point along advancing front */
+P2tNode* p2t_advancingfront_locate_node (P2tAdvancingFront *THIS, const double x);
+
+P2tNode* p2t_advancingfront_locate_point (P2tAdvancingFront *THIS, const P2tPoint* point);
+
+P2tNode* p2t_advancingfront_find_search_node (P2tAdvancingFront *THIS, const double x);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/cdt.c 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/cdt.c
new file mode 100644
index 000000000..738d72e3a
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/cdt.c
@@ -0,0 +1,95 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "cdt.h"
+
+void
+p2t_cdt_init (P2tCDT* THIS, P2tPointPtrArray polyline)
+{
+  THIS->sweep_context_ = p2t_sweepcontext_new (polyline);
+  THIS->sweep_ = p2t_sweep_new ();
+}
+
+P2tCDT*
+p2t_cdt_new (P2tPointPtrArray polyline)
+{
+  P2tCDT* THIS = g_slice_new (P2tCDT);
+  p2t_cdt_init (THIS, polyline);
+  return THIS;
+}
+
+void
+p2t_cdt_destroy (P2tCDT* THIS)
+{
+  p2t_sweepcontext_delete (THIS->sweep_context_);
+  p2t_sweep_free (THIS->sweep_);
+}
+
+void
+p2t_cdt_free (P2tCDT* THIS)
+{
+  p2t_cdt_destroy (THIS);
+  g_slice_free (P2tCDT, THIS);
+}
+
+void
+p2t_cdt_add_hole (P2tCDT *THIS, P2tPointPtrArray polyline)
+{
+  p2t_sweepcontext_add_hole (THIS->sweep_context_, polyline);
+}
+
+void
+p2t_cdt_add_point (P2tCDT *THIS, P2tPoint* point)
+{
+  p2t_sweepcontext_add_point (THIS->sweep_context_, point);
+}
+
+void
+p2t_cdt_triangulate (P2tCDT *THIS)
+{
+  p2t_sweep_triangulate (THIS->sweep_, THIS->sweep_context_);
+}
+
+P2tTrianglePtrArray
+p2t_cdt_get_triangles (P2tCDT *THIS)
+{
+  return p2t_sweepcontext_get_triangles (THIS->sweep_context_);
+}
+
+P2tTrianglePtrList
+p2t_cdt_get_map (P2tCDT *THIS)
+{
+  return p2t_sweepcontext_get_map (THIS->sweep_context_);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/cdt.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/cdt.h
new file mode 100644
index 000000000..d21b5ed32
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/cdt.h
@@ -0,0 +1,105 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_P2T_CDT_H__
+#define __P2TC_P2T_CDT_H__
+
+#include "../common/poly2tri-private.h"
+#include "advancing_front.h"
+#include "sweep_context.h"
+#include "sweep.h"
+
+/**
+ * 
+ * @author Mason Green <mason green gmail com>
+ *
+ */
+
+struct CDT_
+{
+  /*private: */
+
+  /**
+   * Internals
+   */
+
+  P2tSweepContext* sweep_context_;
+  P2tSweep* sweep_;
+
+};
+/**
+ * Constructor - add polyline with non repeating points
+ *
+ * @param polyline
+ */
+void p2t_cdt_init (P2tCDT* THIS, P2tPointPtrArray polyline);
+P2tCDT* p2t_cdt_new (P2tPointPtrArray polyline);
+
+/**
+ * Destructor - clean up memory
+ */
+void p2t_cdt_destroy (P2tCDT* THIS);
+void p2t_cdt_free (P2tCDT* THIS);
+
+/**
+ * Add a hole
+ *
+ * @param polyline
+ */
+void p2t_cdt_add_hole (P2tCDT *THIS, P2tPointPtrArray polyline);
+
+/**
+ * Add a steiner point
+ *
+ * @param point
+ */
+void p2t_cdt_add_point (P2tCDT *THIS, P2tPoint* point);
+
+/**
+ * Triangulate - do this AFTER you've added the polyline, holes, and Steiner points
+ */
+void p2t_cdt_triangulate (P2tCDT *THIS);
+
+/**
+ * Get CDT triangles
+ */
+P2tTrianglePtrArray p2t_cdt_get_triangles (P2tCDT *THIS);
+
+/**
+ * Get triangle map
+ */
+P2tTrianglePtrList p2t_cdt_get_map (P2tCDT *THIS);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/meson.build 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/meson.build
new file mode 100644
index 000000000..0d75c372b
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/meson.build
@@ -0,0 +1,7 @@
+
+libp2tc_sweep_sources = files(
+    'advancing_front.c',
+    'cdt.c',
+    'sweep_context.c',
+    'sweep.c',
+)
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep.c 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep.c
new file mode 100644
index 000000000..7c67a19d5
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep.c
@@ -0,0 +1,1024 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+
+#include "sweep.h"
+#include "sweep_context.h"
+#include "advancing_front.h"
+#include "../common/utils.h"
+#include "../common/shapes.h"
+
+void
+p2t_sweep_init (P2tSweep* THIS)
+{
+  THIS->nodes_ = g_ptr_array_new ();
+}
+
+P2tSweep*
+p2t_sweep_new (void)
+{
+  P2tSweep* THIS = g_new (P2tSweep, 1);
+  p2t_sweep_init (THIS);
+  return THIS;
+}
+
+/**
+ * Destructor - clean up memory
+ */
+void
+p2t_sweep_destroy (P2tSweep* THIS)
+{
+  int i;
+  /* Clean up memory */
+  for (i = 0; i < THIS->nodes_->len; i++)
+    {
+      p2t_node_free (node_index (THIS->nodes_, i));
+    }
+
+  g_ptr_array_free (THIS->nodes_, TRUE);
+}
+
+void
+p2t_sweep_free (P2tSweep* THIS)
+{
+  p2t_sweep_destroy (THIS);
+  g_free (THIS);
+}
+
+/* Triangulate simple polygon with holes */
+
+void
+p2t_sweep_triangulate (P2tSweep *THIS, P2tSweepContext *tcx)
+{
+  p2t_sweepcontext_init_triangulation (tcx);
+  p2t_sweepcontext_create_advancingfront (tcx, THIS->nodes_);
+  /* Sweep points; build mesh */
+  p2t_sweep_sweep_points (THIS, tcx);
+  /* Clean up */
+  p2t_sweep_finalization_polygon (THIS, tcx);
+}
+
+void
+p2t_sweep_sweep_points (P2tSweep *THIS, P2tSweepContext *tcx)
+{
+  int i, j;
+  for (i = 1; i < p2t_sweepcontext_point_count (tcx); i++)
+    {
+      P2tPoint* point = p2t_sweepcontext_get_point (tcx, i);
+      P2tNode* node = p2t_sweep_point_event (THIS, tcx, point);
+      for (j = 0; j < point->edge_list->len; j++)
+        {
+          p2t_sweep_edge_event_ed_n (THIS, tcx, edge_index (point->edge_list, j), node);
+        }
+    }
+}
+
+void
+p2t_sweep_finalization_polygon (P2tSweep *THIS, P2tSweepContext *tcx)
+{
+  /* Get an Internal triangle to start with */
+  P2tTriangle* t = p2t_advancingfront_head (p2t_sweepcontext_front (tcx))->next->triangle;
+  P2tPoint* p = p2t_advancingfront_head (p2t_sweepcontext_front (tcx))->next->point;
+  while (!p2t_triangle_get_constrained_edge_cw (t, p))
+    {
+      t = p2t_triangle_neighbor_ccw (t, p);
+    }
+
+  /* Collect interior triangles constrained by edges */
+  p2t_sweepcontext_mesh_clean (tcx, t);
+}
+
+P2tNode*
+p2t_sweep_point_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* point)
+{
+  P2tNode* node = p2t_sweepcontext_locate_node (tcx, point);
+  P2tNode* new_node = p2t_sweep_new_front_triangle (THIS, tcx, point, node);
+
+  /* Only need to check +epsilon since point never have smaller
+   * x value than node due to how we fetch nodes from the front */
+  if (point->x <= node->point->x + EPSILON)
+    {
+      p2t_sweep_fill (THIS, tcx, node);
+    }
+
+  /*tcx.AddNode(new_node); */
+
+  p2t_sweep_fill_advancingfront (THIS, tcx, new_node);
+  return new_node;
+}
+
+void
+p2t_sweep_edge_event_ed_n (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  tcx->edge_event.constrained_edge = edge;
+  tcx->edge_event.right = (edge->p->x > edge->q->x);
+
+  if (p2t_sweep_is_edge_side_of_triangle (THIS, node->triangle, edge->p, edge->q))
+    {
+      return;
+    }
+
+  /* For now we will do all needed filling
+   * TODO: integrate with flip process might give some better performance
+   *       but for now this avoid the issue with cases that needs both flips and fills
+   */
+  p2t_sweep_fill_edge_event (THIS, tcx, edge, node);
+  p2t_sweep_edge_event_pt_pt_tr_pt (THIS, tcx, edge->p, edge->q, node->triangle, edge->q);
+}
+
+void
+p2t_sweep_edge_event_pt_pt_tr_pt (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* ep, P2tPoint* eq, 
P2tTriangle* triangle, P2tPoint* point)
+{
+  P2tPoint *p1, *p2;
+  P2tOrientation o1, o2;
+
+  if (p2t_sweep_is_edge_side_of_triangle (THIS, triangle, ep, eq))
+    {
+      return;
+    }
+
+  p1 = p2t_triangle_point_ccw (triangle, point);
+  o1 = p2t_orient2d (eq, p1, ep);
+  if (o1 == COLLINEAR)
+    {
+      if (p2t_triangle_contains_pt_pt (triangle, eq, p1))
+        {
+          p2t_triangle_mark_constrained_edge_pt_pt (triangle, eq, p1);
+          /* We are modifying the constraint maybe it would be better to
+           * not change the given constraint and just keep a variable for the new constraint
+           */
+          tcx->edge_event.constrained_edge->q = p1;
+          triangle = p2t_triangle_neighbor_across (triangle, point);
+          p2t_sweep_edge_event_pt_pt_tr_pt (THIS, tcx, ep, p1, triangle, p1);
+        }
+      else
+        {
+          g_error ("EdgeEvent - collinear points not supported");
+        }
+      return;
+    }
+
+  p2 = p2t_triangle_point_cw (triangle, point);
+  o2 = p2t_orient2d (eq, p2, ep);
+  if (o2 == COLLINEAR)
+    {
+      if (p2t_triangle_contains_pt_pt (triangle, eq, p2))
+        {
+          p2t_triangle_mark_constrained_edge_pt_pt (triangle, eq, p2);
+          /* We are modifying the constraint maybe it would be better to
+           * not change the given constraint and just keep a variable for the new constraint
+           */
+          tcx->edge_event.constrained_edge->q = p2;
+          triangle = p2t_triangle_neighbor_across (triangle, point);
+          p2t_sweep_edge_event_pt_pt_tr_pt (THIS, tcx, ep, p2, triangle, p2);
+        }
+      else
+        {
+          g_error ("EdgeEvent - collinear points not supported");
+        }
+      return;
+    }
+
+  if (o1 == o2)
+    {
+      /* Need to decide if we are rotating CW or CCW to get to a triangle
+       * that will cross edge */
+      if (o1 == CW)
+        {
+          triangle = p2t_triangle_neighbor_ccw (triangle, point);
+        }
+      else
+        {
+          triangle = p2t_triangle_neighbor_cw (triangle, point);
+        }
+      p2t_sweep_edge_event_pt_pt_tr_pt (THIS, tcx, ep, eq, triangle, point);
+    }
+  else
+    {
+      /* This triangle crosses constraint so lets flippin start! */
+      p2t_sweep_flip_edge_event (THIS, tcx, ep, eq, triangle, point);
+    }
+}
+
+gboolean
+p2t_sweep_is_edge_side_of_triangle (P2tSweep *THIS, P2tTriangle *triangle, P2tPoint* ep, P2tPoint* eq)
+{
+  int index = p2t_triangle_edge_index (triangle, ep, eq);
+
+  if (index != -1)
+    {
+      P2tTriangle *t;
+      p2t_triangle_mark_constrained_edge_i (triangle, index);
+      t = p2t_triangle_get_neighbor (triangle, index);
+      if (t)
+        {
+          p2t_triangle_mark_constrained_edge_pt_pt (t, ep, eq);
+        }
+      return TRUE;
+    }
+  return FALSE;
+}
+
+P2tNode*
+p2t_sweep_new_front_triangle (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* point, P2tNode *node)
+{
+  P2tTriangle* triangle = p2t_triangle_new (point, node->point, node->next->point);
+  P2tNode *new_node;
+
+  p2t_triangle_mark_neighbor_tr (triangle, node->triangle);
+  p2t_sweepcontext_add_to_map (tcx, triangle);
+
+  new_node = p2t_node_new_pt (point);
+  g_ptr_array_add (THIS->nodes_, new_node);
+
+  new_node->next = node->next;
+  new_node->prev = node;
+  node->next->prev = new_node;
+  node->next = new_node;
+
+  if (!p2t_sweep_legalize (THIS, tcx, triangle))
+    {
+      p2t_sweepcontext_map_triangle_to_nodes (tcx, triangle);
+    }
+
+  return new_node;
+}
+
+void
+p2t_sweep_fill (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* node)
+{
+  P2tTriangle* triangle = p2t_triangle_new (node->prev->point, node->point, node->next->point);
+
+  /* TODO: should copy the constrained_edge value from neighbor triangles
+   *       for now constrained_edge values are copied during the legalize */
+  p2t_triangle_mark_neighbor_tr (triangle, node->prev->triangle);
+  p2t_triangle_mark_neighbor_tr (triangle, node->triangle);
+
+  p2t_sweepcontext_add_to_map (tcx, triangle);
+
+  /* Update the advancing front */
+  node->prev->next = node->next;
+  node->next->prev = node->prev;
+
+  /* If it was legalized the triangle has already been mapped */
+  if (!p2t_sweep_legalize (THIS, tcx, triangle))
+    {
+      p2t_sweepcontext_map_triangle_to_nodes (tcx, triangle);
+    }
+
+}
+
+void
+p2t_sweep_fill_advancingfront (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* n)
+{
+
+  /* Fill right holes */
+  P2tNode* node = n->next;
+
+  while (node->next)
+    {
+      /* if HoleAngle exceeds 90 degrees then break. */
+      if (p2t_sweep_large_hole_dont_fill (THIS, node)) break;
+      p2t_sweep_fill (THIS, tcx, node);
+      node = node->next;
+    }
+
+  /* Fill left holes */
+  node = n->prev;
+
+  while (node->prev)
+    {
+      /* if HoleAngle exceeds 90 degrees then break. */
+      if (p2t_sweep_large_hole_dont_fill (THIS, node)) break;
+      p2t_sweep_fill (THIS, tcx, node);
+      node = node->prev;
+    }
+
+  /* Fill right basins */
+  if (n->next && n->next->next)
+    {
+      double angle = p2t_sweep_basin_angle (THIS, n);
+      if (angle < PI_3div4)
+        {
+          p2t_sweep_fill_basin (THIS, tcx, n);
+        }
+    }
+}
+
+/* True if HoleAngle exceeds 90 degrees. */
+gboolean
+p2t_sweep_large_hole_dont_fill (P2tSweep *THIS, P2tNode* node)
+{
+  P2tNode* nextNode = node->next;
+  P2tNode* prevNode = node->prev;
+  P2tNode *next2Node, *prev2Node;
+  if (! p2t_sweep_angle_exceeds_90_degrees (THIS, node->point, nextNode->point, prevNode->point))
+    return FALSE;
+
+  /* Check additional points on front. */
+  next2Node = nextNode->next;
+  /* "..Plus.." because only want angles on same side as point being added. */
+  if ((next2Node != NULL) && !p2t_sweep_angle_exceeds_plus_90_degrees_or_is_negative (THIS, node->point, 
next2Node->point, prevNode->point))
+    return FALSE;
+
+  prev2Node = prevNode->prev;
+  /* "..Plus.." because only want angles on same side as point being added. */
+  if ((prev2Node != NULL) && !p2t_sweep_angle_exceeds_plus_90_degrees_or_is_negative (THIS, node->point, 
nextNode->point, prev2Node->point))
+    return FALSE;
+
+  return TRUE;
+}
+
+gboolean
+p2t_sweep_angle_exceeds_90_degrees(P2tSweep* THIS, P2tPoint* origin, P2tPoint* pa, P2tPoint* pb)
+{
+  gdouble angle = p2t_sweep_angle (THIS, origin, pa, pb);
+  gboolean exceeds90Degrees = ((angle > G_PI_2) || (angle < -G_PI_2));
+  return exceeds90Degrees;
+}
+
+gboolean
+p2t_sweep_angle_exceeds_plus_90_degrees_or_is_negative (P2tSweep* THIS, P2tPoint* origin, P2tPoint* pa, 
P2tPoint* pb)
+{
+  gdouble angle = p2t_sweep_angle (THIS, origin, pa, pb);
+  gboolean exceedsPlus90DegreesOrIsNegative = (angle > G_PI_2) || (angle < 0);
+  return exceedsPlus90DegreesOrIsNegative;
+}
+
+gdouble
+p2t_sweep_angle (P2tSweep* THIS, P2tPoint* origin, P2tPoint* pa, P2tPoint* pb) {
+  /* Complex plane
+   * ab = cosA +i*sinA
+   * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
+   * atan2(y,x) computes the principal value of the argument function
+   * applied to the complex number x+iy
+   * Where x = ax*bx + ay*by
+   *       y = ax*by - ay*bx
+   */
+  double px = origin->x;
+  double py = origin->y;
+  double ax = pa->x - px;
+  double ay = pa->y - py;
+  double bx = pb->x - px;
+  double by = pb->y - py;
+  double x = ax * by - ay * bx;
+  double y = ax * bx + ay * by;
+  double angle = atan2(x, y);
+  return angle;
+}
+
+double
+p2t_sweep_basin_angle (P2tSweep *THIS, P2tNode* node)
+{
+  double ax = node->point->x - node->next->next->point->x;
+  double ay = node->point->y - node->next->next->point->y;
+  return atan2 (ay, ax);
+}
+
+double
+p2t_sweep_hole_angle (P2tSweep *THIS, P2tNode* node)
+{
+  /* Complex plane
+   * ab = cosA +i*sinA
+   * ab = (ax + ay*i)(bx + by*i) = (ax*bx + ay*by) + i(ax*by-ay*bx)
+   * atan2(y,x) computes the principal value of the argument function
+   * applied to the complex number x+iy
+   * Where x = ax*bx + ay*by
+   *       y = ax*by - ay*bx
+   */
+  double ax = node->next->point->x - node->point->x;
+  double ay = node->next->point->y - node->point->y;
+  double bx = node->prev->point->x - node->point->x;
+  double by = node->prev->point->y - node->point->y;
+  return atan2 (ax * by - ay * bx, ax * bx + ay * by);
+}
+
+gboolean
+p2t_sweep_legalize (P2tSweep *THIS, P2tSweepContext *tcx, P2tTriangle *t)
+{
+  int i;
+  /* To legalize a triangle we start by finding if any of the three edges
+   * violate the Delaunay condition */
+  for (i = 0; i < 3; i++)
+    {
+      P2tTriangle *ot;
+
+      if (t->delaunay_edge[i])
+        continue;
+
+      ot = p2t_triangle_get_neighbor (t, i);
+
+      if (ot)
+        {
+          P2tPoint* p = p2t_triangle_get_point (t, i);
+          P2tPoint* op = p2t_triangle_opposite_point (ot, t, p);
+          int oi = p2t_triangle_index (ot, op);
+          gboolean inside;
+
+          /* If this is a Constrained Edge or a Delaunay Edge(only during recursive legalization)
+           * then we should not try to legalize */
+          if (ot->constrained_edge[oi] || ot->delaunay_edge[oi])
+            {
+              t->constrained_edge[i] = ot->constrained_edge[oi];
+              continue;
+            }
+
+          inside = p2t_sweep_incircle (THIS, p, p2t_triangle_point_ccw (t, p), p2t_triangle_point_cw (t, p), 
op);
+
+          if (inside)
+            {
+              gboolean not_legalized;
+              /* Lets mark this shared edge as Delaunay */
+              t->delaunay_edge[i] = TRUE;
+              ot->delaunay_edge[oi] = TRUE;
+
+              /* Lets rotate shared edge one vertex CW to legalize it */
+              p2t_sweep_rotate_triangle_pair (THIS, t, p, ot, op);
+
+              /* We now got one valid Delaunay Edge shared by two triangles
+               * This gives us 4 new edges to check for Delaunay */
+
+              /* Make sure that triangle to node mapping is done only one time for a specific triangle */
+              not_legalized = !p2t_sweep_legalize (THIS, tcx, t);
+              if (not_legalized)
+                {
+                  p2t_sweepcontext_map_triangle_to_nodes (tcx, t);
+                }
+
+              not_legalized = !p2t_sweep_legalize (THIS, tcx, ot);
+              if (not_legalized)
+                p2t_sweepcontext_map_triangle_to_nodes (tcx, ot);
+
+              /* Reset the Delaunay edges, since they only are valid Delaunay edges
+               * until we add a new triangle or point.
+               * XXX: need to think about this. Can these edges be tried after we
+               *      return to previous recursive level? */
+              t->delaunay_edge[i] = FALSE;
+              ot->delaunay_edge[oi] = FALSE;
+
+              /* If triangle have been legalized no need to check the other edges since
+               * the recursive legalization will handles those so we can end here.*/
+              return TRUE;
+            }
+        }
+    }
+  return FALSE;
+}
+
+gboolean
+p2t_sweep_incircle (P2tSweep *THIS, P2tPoint* pa, P2tPoint* pb, P2tPoint* pc, P2tPoint* pd)
+{
+  double adx = pa->x - pd->x;
+  double ady = pa->y - pd->y;
+  double bdx = pb->x - pd->x;
+  double bdy = pb->y - pd->y;
+
+  double adxbdy = adx * bdy;
+  double bdxady = bdx * ady;
+  double oabd = adxbdy - bdxady;
+
+  double cdx, cdy;
+  double cdxady, adxcdy, ocad;
+
+  double bdxcdy, cdxbdy;
+  double alift, blift, clift;
+  double det;
+
+  if (oabd <= 0)
+    return FALSE;
+
+  cdx = pc->x - pd->x;
+  cdy = pc->y - pd->y;
+
+  cdxady = cdx * ady;
+  adxcdy = adx * cdy;
+  ocad = cdxady - adxcdy;
+
+  if (ocad <= 0)
+    return FALSE;
+
+  bdxcdy = bdx * cdy;
+  cdxbdy = cdx * bdy;
+
+  alift = adx * adx + ady * ady;
+  blift = bdx * bdx + bdy * bdy;
+  clift = cdx * cdx + cdy * cdy;
+
+  det = alift * (bdxcdy - cdxbdy) + blift * ocad + clift * oabd;
+
+  return det > 0;
+}
+
+void
+p2t_sweep_rotate_triangle_pair (P2tSweep *THIS, P2tTriangle *t, P2tPoint* p, P2tTriangle *ot, P2tPoint* op)
+{
+  P2tTriangle *n1, *n2, *n3, *n4;
+  gboolean ce1, ce2, ce3, ce4;
+  gboolean de1, de2, de3, de4;
+
+  n1 = p2t_triangle_neighbor_ccw (t, p);
+  n2 = p2t_triangle_neighbor_cw (t, p);
+  n3 = p2t_triangle_neighbor_ccw (ot, op);
+  n4 = p2t_triangle_neighbor_cw (ot, op);
+
+  ce1 = p2t_triangle_get_constrained_edge_ccw (t, p);
+  ce2 = p2t_triangle_get_constrained_edge_cw (t, p);
+  ce3 = p2t_triangle_get_constrained_edge_ccw (ot, op);
+  ce4 = p2t_triangle_get_constrained_edge_cw (ot, op);
+
+  de1 = p2t_triangle_get_delunay_edge_ccw (t, p);
+  de2 = p2t_triangle_get_delunay_edge_cw (t, p);
+  de3 = p2t_triangle_get_delunay_edge_ccw (ot, op);
+  de4 = p2t_triangle_get_delunay_edge_cw (ot, op);
+
+  p2t_triangle_legalize_pt_pt (t, p, op);
+  p2t_triangle_legalize_pt_pt (ot, op, p);
+
+  /* Remap delaunay_edge */
+  p2t_triangle_set_delunay_edge_ccw (ot, p, de1);
+  p2t_triangle_set_delunay_edge_cw (t, p, de2);
+  p2t_triangle_set_delunay_edge_ccw (t, op, de3);
+  p2t_triangle_set_delunay_edge_cw (ot, op, de4);
+
+  /* Remap constrained_edge */
+  p2t_triangle_set_constrained_edge_ccw (ot, p, ce1);
+  p2t_triangle_set_constrained_edge_cw (t, p, ce2);
+  p2t_triangle_set_constrained_edge_ccw (t, op, ce3);
+  p2t_triangle_set_constrained_edge_cw (ot, op, ce4);
+
+  /* Remap neighbors
+   * XXX: might optimize the markNeighbor by keeping track of
+   *      what side should be assigned to what neighbor after the
+   *      rotation. Now mark neighbor does lots of testing to find
+   *      the right side. */
+  p2t_triangle_clear_neighbors (t);
+  p2t_triangle_clear_neighbors (ot);
+  if (n1) p2t_triangle_mark_neighbor_tr (ot, n1);
+  if (n2) p2t_triangle_mark_neighbor_tr (t, n2);
+  if (n3) p2t_triangle_mark_neighbor_tr (t, n3);
+  if (n4) p2t_triangle_mark_neighbor_tr (ot, n4);
+  p2t_triangle_mark_neighbor_tr (t, ot);
+}
+
+void
+p2t_sweep_fill_basin (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* node)
+{
+  if (p2t_orient2d (node->point, node->next->point, node->next->next->point) == CCW)
+    {
+      tcx->basin.left_node = node->next->next;
+    }
+  else
+    {
+      tcx->basin.left_node = node->next;
+    }
+
+  /* Find the bottom and right node */
+  tcx->basin.bottom_node = tcx->basin.left_node;
+  while (tcx->basin.bottom_node->next
+         && tcx->basin.bottom_node->point->y >= tcx->basin.bottom_node->next->point->y)
+    {
+      tcx->basin.bottom_node = tcx->basin.bottom_node->next;
+    }
+  if (tcx->basin.bottom_node == tcx->basin.left_node)
+    {
+      /* No valid basin */
+      return;
+    }
+
+  tcx->basin.right_node = tcx->basin.bottom_node;
+  while (tcx->basin.right_node->next
+         && tcx->basin.right_node->point->y < tcx->basin.right_node->next->point->y)
+    {
+      tcx->basin.right_node = tcx->basin.right_node->next;
+    }
+  if (tcx->basin.right_node == tcx->basin.bottom_node)
+    {
+      /* No valid basins */
+      return;
+    }
+
+  tcx->basin.width = tcx->basin.right_node->point->x - tcx->basin.left_node->point->x;
+  tcx->basin.left_highest = tcx->basin.left_node->point->y > tcx->basin.right_node->point->y;
+
+  p2t_sweep_fill_basin_req (THIS, tcx, tcx->basin.bottom_node);
+}
+
+void
+p2t_sweep_fill_basin_req (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* node)
+{
+  /* if shallow stop filling */
+  if (p2t_sweep_is_shallow (THIS, tcx, node))
+    {
+      return;
+    }
+
+  p2t_sweep_fill (THIS, tcx, node);
+
+  if (node->prev == tcx->basin.left_node && node->next == tcx->basin.right_node)
+    {
+      return;
+    }
+  else if (node->prev == tcx->basin.left_node)
+    {
+      P2tOrientation o = p2t_orient2d (node->point, node->next->point, node->next->next->point);
+      if (o == CW)
+        {
+          return;
+        }
+      node = node->next;
+    }
+  else if (node->next == tcx->basin.right_node)
+    {
+      P2tOrientation o = p2t_orient2d (node->point, node->prev->point, node->prev->prev->point);
+      if (o == CCW)
+        {
+          return;
+        }
+      node = node->prev;
+    }
+  else
+    {
+      /* Continue with the neighbor node with lowest Y value */
+      if (node->prev->point->y < node->next->point->y)
+        {
+          node = node->prev;
+        }
+      else
+        {
+          node = node->next;
+        }
+    }
+
+  p2t_sweep_fill_basin_req (THIS, tcx, node);
+}
+
+gboolean
+p2t_sweep_is_shallow (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* node)
+{
+  double height;
+
+  if (tcx->basin.left_highest)
+    {
+      height = tcx->basin.left_node->point->y - node->point->y;
+    }
+  else
+    {
+      height = tcx->basin.right_node->point->y - node->point->y;
+    }
+
+  /* if shallow stop filling */
+  if (tcx->basin.width > height)
+    {
+      return TRUE;
+    }
+  return FALSE;
+}
+
+void
+p2t_sweep_fill_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  if (tcx->edge_event.right)
+    {
+      p2t_sweep_fill_right_above_edge_event (THIS, tcx, edge, node);
+    }
+  else
+    {
+      p2t_sweep_fill_left_above_edge_event (THIS, tcx, edge, node);
+    }
+}
+
+void
+p2t_sweep_fill_right_above_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  while (node->next->point->x < edge->p->x)
+    {
+      /* Check if next node is below the edge */
+      if (p2t_orient2d (edge->q, node->next->point, edge->p) == CCW)
+        {
+          p2t_sweep_fill_right_below_edge_event (THIS, tcx, edge, node);
+        }
+      else
+        {
+          node = node->next;
+        }
+    }
+}
+
+void
+p2t_sweep_fill_right_below_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  if (node->point->x < edge->p->x)
+    {
+      if (p2t_orient2d (node->point, node->next->point, node->next->next->point) == CCW)
+        {
+          /* Concave */
+          p2t_sweep_fill_right_concave_edge_event (THIS, tcx, edge, node);
+        }
+      else
+        {
+          /* Convex */
+          p2t_sweep_fill_right_convex_edge_event (THIS, tcx, edge, node);
+          /* Retry this one */
+          p2t_sweep_fill_right_below_edge_event (THIS, tcx, edge, node);
+        }
+    }
+}
+
+void
+p2t_sweep_fill_right_concave_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  p2t_sweep_fill (THIS, tcx, node->next);
+  if (node->next->point != edge->p)
+    {
+      /* Next above or below edge? */
+      if (p2t_orient2d (edge->q, node->next->point, edge->p) == CCW)
+        {
+          /* Below */
+          if (p2t_orient2d (node->point, node->next->point, node->next->next->point) == CCW)
+            {
+              /* Next is concave */
+              p2t_sweep_fill_right_concave_edge_event (THIS, tcx, edge, node);
+            }
+          else
+            {
+              /* Next is convex */
+            }
+        }
+    }
+
+}
+
+void
+p2t_sweep_fill_right_convex_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  /* Next concave or convex? */
+  if (p2t_orient2d (node->next->point, node->next->next->point, node->next->next->next->point) == CCW)
+    {
+      /* Concave */
+      p2t_sweep_fill_right_concave_edge_event (THIS, tcx, edge, node->next);
+    }
+  else
+    {
+      /* Convex
+       * Next above or below edge? */
+      if (p2t_orient2d (edge->q, node->next->next->point, edge->p) == CCW)
+        {
+          /* Below */
+          p2t_sweep_fill_right_convex_edge_event (THIS, tcx, edge, node->next);
+        }
+      else
+        {
+          /* Above */
+        }
+    }
+}
+
+void
+p2t_sweep_fill_left_above_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  while (node->prev->point->x > edge->p->x)
+    {
+      /* Check if next node is below the edge */
+      if (p2t_orient2d (edge->q, node->prev->point, edge->p) == CW)
+        {
+          p2t_sweep_fill_left_below_edge_event (THIS, tcx, edge, node);
+        }
+      else
+        {
+          node = node->prev;
+        }
+    }
+}
+
+void
+p2t_sweep_fill_left_below_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  if (node->point->x > edge->p->x)
+    {
+      if (p2t_orient2d (node->point, node->prev->point, node->prev->prev->point) == CW)
+        {
+          /* Concave */
+          p2t_sweep_fill_left_concave_edge_event (THIS, tcx, edge, node);
+        }
+      else
+        {
+          /* Convex */
+          p2t_sweep_fill_left_convex_edge_event (THIS, tcx, edge, node);
+          /* Retry this one */
+          p2t_sweep_fill_left_below_edge_event (THIS, tcx, edge, node);
+        }
+    }
+}
+
+void
+p2t_sweep_fill_left_convex_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  /* Next concave or convex? */
+  if (p2t_orient2d (node->prev->point, node->prev->prev->point, node->prev->prev->prev->point) == CW)
+    {
+      /* Concave */
+      p2t_sweep_fill_left_concave_edge_event (THIS, tcx, edge, node->prev);
+    }
+  else
+    {
+      /* Convex
+       * Next above or below edge? */
+      if (p2t_orient2d (edge->q, node->prev->prev->point, edge->p) == CW)
+        {
+          /* Below */
+          p2t_sweep_fill_left_convex_edge_event (THIS, tcx, edge, node->prev);
+        }
+      else
+        {
+          /* Above */
+        }
+    }
+}
+
+void
+p2t_sweep_fill_left_concave_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node)
+{
+  p2t_sweep_fill (THIS, tcx, node->prev);
+  if (node->prev->point != edge->p)
+    {
+      /* Next above or below edge? */
+      if (p2t_orient2d (edge->q, node->prev->point, edge->p) == CW)
+        {
+          /* Below */
+          if (p2t_orient2d (node->point, node->prev->point, node->prev->prev->point) == CW)
+            {
+              /* Next is concave */
+              p2t_sweep_fill_left_concave_edge_event (THIS, tcx, edge, node);
+            }
+          else
+            {
+              /* Next is convex */
+            }
+        }
+    }
+
+}
+
+void
+p2t_sweep_flip_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* ep, P2tPoint* eq, P2tTriangle* t, 
P2tPoint* p)
+{
+  P2tTriangle* ot = p2t_triangle_neighbor_across (t, p);
+  P2tPoint* op = p2t_triangle_opposite_point (ot, t, p);
+
+  if (ot == NULL)
+    {
+      /* If we want to integrate the fillEdgeEvent do it here
+       * With current implementation we should never get here
+       *throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
+       */
+      assert (0);
+    }
+
+  if (p2t_utils_in_scan_area (p, p2t_triangle_point_ccw (t, p), p2t_triangle_point_cw (t, p), op))
+    {
+      /* Lets rotate shared edge one vertex CW */
+      p2t_sweep_rotate_triangle_pair (THIS, t, p, ot, op);
+      p2t_sweepcontext_map_triangle_to_nodes (tcx, t);
+      p2t_sweepcontext_map_triangle_to_nodes (tcx, ot);
+
+      if (p == eq && op == ep)
+        {
+          if (p2t_point_equals (eq, tcx->edge_event.constrained_edge->q) && p2t_point_equals (ep, 
tcx->edge_event.constrained_edge->p))
+            {
+              p2t_triangle_mark_constrained_edge_pt_pt (t, ep, eq);
+              p2t_triangle_mark_constrained_edge_pt_pt (ot, ep, eq);
+              p2t_sweep_legalize (THIS, tcx, t);
+              p2t_sweep_legalize (THIS, tcx, ot);
+            }
+          else
+            {
+              /* XXX: I think one of the triangles should be legalized here? */
+            }
+        }
+      else
+        {
+          P2tOrientation o = p2t_orient2d (eq, op, ep);
+          t = p2t_sweep_next_flip_triangle (THIS, tcx, (int) o, t, ot, p, op);
+          p2t_sweep_flip_edge_event (THIS, tcx, ep, eq, t, p);
+        }
+    }
+  else
+    {
+      P2tPoint* newP = p2t_sweep_next_flip_point (THIS, ep, eq, ot, op);
+      p2t_sweep_flip_scan_edge_event (THIS, tcx, ep, eq, t, ot, newP);
+      p2t_sweep_edge_event_pt_pt_tr_pt (THIS, tcx, ep, eq, t, p);
+    }
+}
+
+P2tTriangle*
+p2t_sweep_next_flip_triangle (P2tSweep *THIS, P2tSweepContext *tcx, int o, P2tTriangle *t, P2tTriangle *ot, 
P2tPoint* p, P2tPoint* op)
+{
+  int edge_index;
+
+  if (o == CCW)
+    {
+      /* ot is not crossing edge after flip */
+      int edge_index = p2t_triangle_edge_index (ot, p, op);
+      ot->delaunay_edge[edge_index] = TRUE;
+      p2t_sweep_legalize (THIS, tcx, ot);
+      p2t_triangle_clear_delunay_edges (ot);
+      return t;
+    }
+
+  /* t is not crossing edge after flip */
+  edge_index = p2t_triangle_edge_index (t, p, op);
+
+  t->delaunay_edge[edge_index] = TRUE;
+  p2t_sweep_legalize (THIS, tcx, t);
+  p2t_triangle_clear_delunay_edges (t);
+  return ot;
+}
+
+P2tPoint*
+p2t_sweep_next_flip_point (P2tSweep *THIS, P2tPoint* ep, P2tPoint* eq, P2tTriangle *ot, P2tPoint* op)
+{
+  P2tOrientation o2d = p2t_orient2d (eq, op, ep);
+  if (o2d == CW)
+    {
+      /* Right */
+      return p2t_triangle_point_ccw (ot, op);
+    }
+  else if (o2d == CCW)
+    {
+      /* Left */
+      return p2t_triangle_point_cw (ot, op);
+    }
+  else
+    {
+      /*throw new RuntimeException("[Unsupported] Opposing point on constrained edge");*/
+      assert (0);
+    }
+}
+
+void
+p2t_sweep_flip_scan_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* ep, P2tPoint* eq, 
P2tTriangle *flip_triangle,
+                                P2tTriangle *t, P2tPoint* p)
+{
+  P2tTriangle* ot = p2t_triangle_neighbor_across (t, p);
+  P2tPoint* op = p2t_triangle_opposite_point (ot, t, p);
+
+  if (p2t_triangle_neighbor_across (t, p) == NULL)
+    {
+      /* If we want to integrate the fillEdgeEvent do it here
+       * With current implementation we should never get here
+       *throw new RuntimeException( "[BUG:FIXME] FLIP failed due to missing triangle");
+       */
+      assert (0);
+    }
+
+  if (p2t_utils_in_scan_area (eq, p2t_triangle_point_ccw (flip_triangle, eq), p2t_triangle_point_cw 
(flip_triangle, eq), op))
+    {
+      /* flip with new edge op->eq */
+      p2t_sweep_flip_edge_event (THIS, tcx, eq, op, ot, op);
+      /* TODO: Actually I just figured out that it should be possible to
+       *       improve this by getting the next ot and op before the the above
+       *       flip and continue the flipScanEdgeEvent here
+       * set new ot and op here and loop back to inScanArea test
+       * also need to set a new flip_triangle first
+       * Turns out at first glance that this is somewhat complicated
+       * so it will have to wait. */
+    }
+  else
+    {
+      P2tPoint* newP = p2t_sweep_next_flip_point (THIS, ep, eq, ot, op);
+      p2t_sweep_flip_scan_edge_event (THIS, tcx, ep, eq, flip_triangle, ot, newP);
+    }
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep.h
new file mode 100644
index 000000000..5e8444fad
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep.h
@@ -0,0 +1,282 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/**
+ * Sweep-line, Constrained Delauney Triangulation (CDT) See: Domiter, V. and
+ * Zalik, B.(2008)'Sweep-line algorithm for constrained Delaunay triangulation',
+ * International Journal of Geographical Information Science
+ *
+ * "FlipScan" Constrained Edge Algorithm invented by Thomas �hl�n, thahlen gmail com
+ */
+
+#ifndef __P2TC_P2T_SWEEP_H__
+#define __P2TC_P2T_SWEEP_H__
+
+#include "../common/poly2tri-private.h"
+#include "../common/shapes.h"
+
+struct Sweep_
+{
+/* private: */
+P2tNodePtrArray nodes_;
+
+};
+
+void p2t_sweep_init (P2tSweep* THIS);
+P2tSweep* p2t_sweep_new (void);
+
+/**
+ * Destructor - clean up memory
+ */
+void p2t_sweep_destroy (P2tSweep* THIS);
+void p2t_sweep_free (P2tSweep* THIS);
+
+/**
+ * Triangulate
+ *
+ * @param tcx
+ */
+void p2t_sweep_triangulate (P2tSweep *THIS, P2tSweepContext *tcx);
+
+/**
+ * Start sweeping the Y-sorted point set from bottom to top
+ *
+ * @param tcx
+ */
+void p2t_sweep_sweep_points (P2tSweep *THIS, P2tSweepContext *tcx);
+
+/**
+ * Find closes node to the left of the new point and
+ * create a new triangle. If needed new holes and basins
+ * will be filled to.
+ *
+ * @param tcx
+ * @param point
+ * @return
+ */
+P2tNode* p2t_sweep_point_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* point);
+
+/**
+ *
+ *
+ * @param tcx
+ * @param edge
+ * @param node
+ */
+void p2t_sweep_edge_event_ed_n (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node);
+
+void p2t_sweep_edge_event_pt_pt_tr_pt (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* ep, P2tPoint* eq, 
P2tTriangle* triangle, P2tPoint* point);
+
+/**
+ * Creates a new front triangle and legalize it
+ *
+ * @param tcx
+ * @param point
+ * @param node
+ * @return
+ */
+P2tNode* p2t_sweep_new_front_triangle (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* point, P2tNode* node);
+
+/**
+ * Adds a triangle to the advancing front to fill a hole.
+ * @param tcx
+ * @param node - middle node, that is the bottom of the hole
+ */
+void p2t_sweep_fill (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* node);
+
+/**
+ * Returns true if triangle was legalized
+ */
+gboolean p2t_sweep_legalize (P2tSweep *THIS, P2tSweepContext *tcx, P2tTriangle *t);
+
+/**
+ * <b>Requirement</b>:<br>
+ * 1. a,b and c form a triangle.<br>
+ * 2. a and d is know to be on opposite side of bc<br>
+ * <pre>
+ *                a
+ *                +
+ *               / \
+ *              /   \
+ *            b/     \c
+ *            +-------+
+ *           /    d    \
+ *          /           \
+ * </pre>
+ * <b>Fact</b>: d has to be in area B to have a chance to be inside the circle formed by
+ *  a,b and c<br>
+ *  d is outside B if orient2d(a,b,d) or orient2d(c,a,d) is CW<br>
+ *  This preknowledge gives us a way to optimize the incircle test
+ * @param a - triangle point, opposite d
+ * @param b - triangle point
+ * @param c - triangle point
+ * @param d - point opposite a
+ * @return true if d is inside circle, false if on circle edge
+ */
+gboolean p2t_sweep_incircle (P2tSweep *THIS, P2tPoint* pa, P2tPoint* pb, P2tPoint* pc, P2tPoint* pd);
+
+/**
+ * Rotates a triangle pair one vertex CW
+ *<pre>
+ *       n2                    n2
+ *  P +-----+             P +-----+
+ *    | t  /|               |\  t |
+ *    |   / |               | \   |
+ *  n1|  /  |n3           n1|  \  |n3
+ *    | /   |    after CW   |   \ |
+ *    |/ oT |               | oT \|
+ *    +-----+ oP            +-----+
+ *       n4                    n4
+ * </pre>
+ */
+void p2t_sweep_rotate_triangle_pair (P2tSweep *THIS, P2tTriangle *t, P2tPoint* p, P2tTriangle *ot, P2tPoint* 
op);
+
+/**
+ * Fills holes in the Advancing Front
+ *
+ *
+ * @param tcx
+ * @param n
+ */
+void p2t_sweep_fill_advancingfront (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* n);
+
+/* Decision-making about when to Fill hole.
+ * Contributed by ToolmakerSteve2 */
+gboolean p2t_sweep_large_hole_dont_fill (P2tSweep* THIS, P2tNode* node);
+gboolean p2t_sweep_angle_exceeds_90_degrees (P2tSweep* THIS, P2tPoint* origin, P2tPoint* pa, P2tPoint* pb);
+gboolean p2t_sweep_angle_exceeds_plus_90_degrees_or_is_negative (P2tSweep* THIS, P2tPoint* origin, P2tPoint* 
pa, P2tPoint* pb);
+gdouble  p2t_sweep_angle (P2tSweep* THIS, P2tPoint* origin, P2tPoint* pa, P2tPoint* pb);
+
+/**
+ *
+ * @param node - middle node
+ * @return the angle between 3 front nodes
+ */
+double p2t_sweep_hole_angle (P2tSweep *THIS, P2tNode* node);
+
+/**
+ * The basin angle is decided against the horizontal line [1,0]
+ */
+double p2t_sweep_basin_angle (P2tSweep *THIS, P2tNode* node);
+
+/**
+ * Fills a basin that has formed on the Advancing Front to the right
+ * of given node.<br>
+ * First we decide a left,bottom and right node that forms the
+ * boundaries of the basin. Then we do a reqursive fill.
+ *
+ * @param tcx
+ * @param node - starting node, this or next node will be left node
+ */
+void p2t_sweep_fill_basin (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* node);
+
+/**
+ * Recursive algorithm to fill a Basin with triangles
+ *
+ * @param tcx
+ * @param node - bottom_node
+ * @param cnt - counter used to alternate on even and odd numbers
+ */
+void p2t_sweep_fill_basin_req (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* node);
+
+gboolean p2t_sweep_is_shallow (P2tSweep *THIS, P2tSweepContext *tcx, P2tNode* node);
+
+gboolean p2t_sweep_is_edge_side_of_triangle (P2tSweep *THIS, P2tTriangle *triangle, P2tPoint* ep, P2tPoint* 
eq);
+
+void p2t_sweep_fill_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* node);
+
+void p2t_sweep_fill_right_above_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* 
node);
+
+void p2t_sweep_fill_right_below_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* 
node);
+
+void p2t_sweep_fill_right_concave_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* 
node);
+
+void p2t_sweep_fill_right_convex_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* 
node);
+
+void p2t_sweep_fill_left_above_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* 
node);
+
+void p2t_sweep_fill_left_below_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* 
node);
+
+void p2t_sweep_fill_left_concave_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* 
node);
+
+void p2t_sweep_fill_left_convex_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tEdge* edge, P2tNode* 
node);
+
+void p2t_sweep_flip_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* ep, P2tPoint* eq, 
P2tTriangle* t, P2tPoint* p);
+
+/**
+ * After a flip we have two triangles and know that only one will still be
+ * intersecting the edge. So decide which to contiune with and legalize the other
+ *
+ * @param tcx
+ * @param o - should be the result of an orient2d( eq, op, ep )
+ * @param t - triangle 1
+ * @param ot - triangle 2
+ * @param p - a point shared by both triangles
+ * @param op - another point shared by both triangles
+ * @return returns the triangle still intersecting the edge
+ */
+P2tTriangle* p2t_sweep_next_flip_triangle (P2tSweep *THIS, P2tSweepContext *tcx, int o, P2tTriangle *t, 
P2tTriangle *ot, P2tPoint* p, P2tPoint* op);
+
+/**
+ * When we need to traverse from one triangle to the next we need
+ * the point in current triangle that is the opposite point to the next
+ * triangle.
+ *
+ * @param ep
+ * @param eq
+ * @param ot
+ * @param op
+ * @return
+ */
+P2tPoint* p2t_sweep_next_flip_point (P2tSweep *THIS, P2tPoint* ep, P2tPoint* eq, P2tTriangle *ot, P2tPoint* 
op);
+
+/**
+ * Scan part of the FlipScan algorithm<br>
+ * When a triangle pair isn't flippable we will scan for the next
+ * point that is inside the flip triangle scan area. When found
+ * we generate a new flipEdgeEvent
+ *
+ * @param tcx
+ * @param ep - last point on the edge we are traversing
+ * @param eq - first point on the edge we are traversing
+ * @param flipTriangle - the current triangle sharing the point eq with edge
+ * @param t
+ * @param p
+ */
+void p2t_sweep_flip_scan_edge_event (P2tSweep *THIS, P2tSweepContext *tcx, P2tPoint* ep, P2tPoint* eq, 
P2tTriangle *flip_triangle, P2tTriangle *t, P2tPoint* p);
+
+void p2t_sweep_finalization_polygon (P2tSweep *THIS, P2tSweepContext *tcx);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep_context.c 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep_context.c
new file mode 100644
index 000000000..9ed52d566
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep_context.c
@@ -0,0 +1,319 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include "sweep_context.h"
+#include "advancing_front.h"
+
+void
+p2t_sweepcontext_basin_init (P2tSweepContextBasin* THIS)
+{
+  p2t_sweepcontext_basin_clear (THIS);
+}
+
+void
+p2t_sweepcontext_basin_clear (P2tSweepContextBasin* THIS)
+{
+  THIS->left_node = NULL;
+  THIS->bottom_node = NULL;
+  THIS->right_node = NULL;
+  THIS->width = 0.0;
+  THIS->left_highest = FALSE;
+}
+
+void
+p2t_sweepcontext_edgeevent_init (P2tSweepContextEdgeEvent* THIS)
+{
+  THIS->constrained_edge = NULL;
+  THIS->right = FALSE;
+}
+
+void
+p2t_sweepcontext_init (P2tSweepContext* THIS, P2tPointPtrArray polyline)
+{
+  int i;
+  THIS->edge_list = g_ptr_array_new ();
+  THIS->triangles_ = g_ptr_array_new ();
+  THIS->map_ = NULL;
+
+  p2t_sweepcontext_basin_init (&THIS->basin);
+  p2t_sweepcontext_edgeevent_init (&THIS->edge_event);
+
+  THIS->points_ = g_ptr_array_sized_new (polyline->len);
+  for (i = 0; i < polyline->len; i++)
+    g_ptr_array_add (THIS->points_, point_index (polyline, i));
+
+  p2t_sweepcontext_init_edges (THIS, THIS->points_);
+}
+
+P2tSweepContext*
+p2t_sweepcontext_new (P2tPointPtrArray polyline)
+{
+  P2tSweepContext* THIS = g_new (P2tSweepContext, 1);
+  p2t_sweepcontext_init (THIS, polyline);
+  return THIS;
+}
+
+void
+p2t_sweepcontext_destroy (P2tSweepContext* THIS)
+{
+  GList* iter;
+  int i;
+  /* Clean up memory */
+
+  p2t_point_free (THIS->head_);
+  p2t_point_free (THIS->tail_);
+  p2t_advancingfront_free (THIS->front_);
+  p2t_node_free (THIS->af_head_);
+  p2t_node_free (THIS->af_middle_);
+  p2t_node_free (THIS->af_tail_);
+
+  g_ptr_array_free (THIS->points_, TRUE);
+  g_ptr_array_free (THIS->triangles_, TRUE);
+
+  for (iter = g_list_first (THIS->map_); iter != NULL; iter = g_list_next (iter))
+    {
+      P2tTriangle* ptr = triangle_val (iter);
+      g_free (ptr);
+    }
+
+  g_list_free (THIS->map_);
+
+  for (i = 0; i < THIS->edge_list->len; i++)
+    {
+      p2t_edge_free (edge_index (THIS->edge_list, i));
+    }
+
+  g_ptr_array_free (THIS->edge_list, TRUE);
+
+}
+
+void
+p2t_sweepcontext_delete (P2tSweepContext* THIS)
+{
+  p2t_sweepcontext_destroy (THIS);
+  g_free(THIS);
+}
+
+void
+p2t_sweepcontext_add_hole (P2tSweepContext *THIS, P2tPointPtrArray polyline)
+{
+  int i;
+  p2t_sweepcontext_init_edges (THIS, polyline);
+  for (i = 0; i < polyline->len; i++)
+    {
+      g_ptr_array_add (THIS->points_, point_index (polyline, i));
+    }
+}
+
+void
+p2t_sweepcontext_add_point (P2tSweepContext *THIS, P2tPoint* point)
+{
+  g_ptr_array_add (THIS->points_, point);
+}
+
+P2tTrianglePtrArray
+p2t_sweepcontext_get_triangles (P2tSweepContext *THIS)
+{
+  return THIS->triangles_;
+}
+
+P2tTrianglePtrList
+p2t_sweepcontext_get_map (P2tSweepContext *THIS)
+{
+  return THIS->map_;
+}
+
+void
+p2t_sweepcontext_init_triangulation (P2tSweepContext *THIS)
+{
+  int i;
+  double xmax = point_index (THIS->points_, 0)->x, xmin = point_index (THIS->points_, 0)->x;
+  double ymax = point_index (THIS->points_, 0)->y, ymin = point_index (THIS->points_, 0)->y;
+  double dx, dy;
+
+  /* Calculate bounds. */
+  for (i = 0; i < THIS->points_->len; i++)
+    {
+      P2tPoint* p = point_index (THIS->points_, i);
+      if (p->x > xmax)
+        xmax = p->x;
+      if (p->x < xmin)
+        xmin = p->x;
+      if (p->y > ymax)
+        ymax = p->y;
+      if (p->y < ymin)
+        ymin = p->y;
+    }
+
+  dx = kAlpha * (xmax - xmin);
+  dy = kAlpha * (ymax - ymin);
+  THIS->head_ = p2t_point_new_dd (xmax + dx, ymin - dy);
+  THIS->tail_ = p2t_point_new_dd (xmin - dx, ymin - dy);
+
+  /* Sort points along y-axis */
+  g_ptr_array_sort (THIS->points_, p2t_point_cmp);
+}
+
+void
+p2t_sweepcontext_init_edges (P2tSweepContext *THIS, P2tPointPtrArray polyline)
+{
+  int i;
+  int num_points = polyline->len;
+  g_ptr_array_set_size (THIS->edge_list, THIS->edge_list->len + num_points); /* C-OPTIMIZATION */
+  for (i = 0; i < num_points; i++)
+    {
+      int j = i < num_points - 1 ? i + 1 : 0;
+      g_ptr_array_add (THIS->edge_list, p2t_edge_new (point_index (polyline, i), point_index (polyline, j)));
+    }
+}
+
+P2tPoint*
+p2t_sweepcontext_get_point (P2tSweepContext *THIS, const int index)
+{
+  return point_index (THIS->points_, index);
+}
+
+void
+p2t_sweepcontext_add_to_map (P2tSweepContext *THIS, P2tTriangle* triangle)
+{
+  THIS->map_ = g_list_append (THIS->map_, triangle);
+}
+
+P2tNode*
+p2t_sweepcontext_locate_node (P2tSweepContext *THIS, P2tPoint* point)
+{
+  /* TODO implement search tree */
+  return p2t_advancingfront_locate_node (THIS->front_, point->x);
+}
+
+void
+p2t_sweepcontext_create_advancingfront (P2tSweepContext *THIS, P2tNodePtrArray nodes)
+{
+  /* Initial triangle */
+  P2tTriangle* triangle = p2t_triangle_new (point_index (THIS->points_, 0), THIS->tail_, THIS->head_);
+
+  THIS->map_ = g_list_append (THIS->map_, triangle);
+
+  THIS->af_head_ = p2t_node_new_pt_tr (p2t_triangle_get_point (triangle, 1), triangle);
+  THIS->af_middle_ = p2t_node_new_pt_tr (p2t_triangle_get_point (triangle, 0), triangle);
+  THIS->af_tail_ = p2t_node_new_pt (p2t_triangle_get_point (triangle, 2));
+  THIS->front_ = p2t_advancingfront_new (THIS->af_head_, THIS->af_tail_);
+
+  /* TODO: More intuitiv if head is middles next and not previous?
+   *       so swap head and tail */
+  THIS->af_head_->next = THIS->af_middle_;
+  THIS->af_middle_->next = THIS->af_tail_;
+  THIS->af_middle_->prev = THIS->af_head_;
+  THIS->af_tail_->prev = THIS->af_middle_;
+}
+
+void
+p2t_sweepcontext_remove_node (P2tSweepContext *THIS, P2tNode* node)
+{
+  g_free (node);
+}
+
+void
+p2t_sweepcontext_map_triangle_to_nodes (P2tSweepContext *THIS, P2tTriangle* t)
+{
+  int i;
+  for (i = 0; i < 3; i++)
+    {
+      if (!p2t_triangle_get_neighbor (t, i))
+        {
+          P2tNode* n = p2t_advancingfront_locate_point (THIS->front_, p2t_triangle_point_cw (t, 
p2t_triangle_get_point (t, i)));
+          if (n)
+            n->triangle = t;
+        }
+    }
+}
+
+void
+p2t_sweepcontext_remove_from_map (P2tSweepContext *THIS, P2tTriangle* triangle)
+{
+  THIS->map_ = g_list_remove (THIS->map_, triangle);
+}
+
+void
+p2t_sweepcontext_mesh_clean (P2tSweepContext *THIS, P2tTriangle* triangle)
+{
+  int i;
+  if (triangle != NULL && !p2t_triangle_is_interior (triangle))
+    {
+      p2t_triangle_is_interior_b (triangle, TRUE);
+      g_ptr_array_add (THIS->triangles_, triangle);
+      for (i = 0; i < 3; i++)
+        {
+          if (!triangle->constrained_edge[i])
+            p2t_sweepcontext_mesh_clean (THIS, p2t_triangle_get_neighbor (triangle, i));
+        }
+    }
+}
+
+P2tAdvancingFront*
+p2t_sweepcontext_front (P2tSweepContext *THIS)
+{
+  return THIS->front_;
+}
+
+int
+p2t_sweepcontext_point_count (P2tSweepContext *THIS)
+{
+  return THIS->points_->len;
+}
+
+void
+p2t_sweepcontext_set_head (P2tSweepContext *THIS, P2tPoint* p1)
+{
+  THIS->head_ = p1;
+}
+
+P2tPoint*
+p2t_sweepcontext_head (P2tSweepContext *THIS)
+{
+  return THIS->head_;
+}
+
+void
+p2t_sweepcontext_set_tail (P2tSweepContext *THIS, P2tPoint* p1)
+{
+  THIS->tail_ = p1;
+}
+
+P2tPoint*
+p2t_sweepcontext_tail (P2tSweepContext *THIS)
+{
+  return THIS->tail_;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep_context.h 
b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep_context.h
new file mode 100644
index 000000000..2c9748f82
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/p2t/sweep/sweep_context.h
@@ -0,0 +1,137 @@
+/*
+ * This file is a part of the C port of the Poly2Tri library
+ * Porting to C done by (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * Poly2Tri Copyright (c) 2009-2010, Poly2Tri Contributors
+ * http://code.google.com/p/poly2tri/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_P2T_SWEEP_CONTEXT_H__
+#define __P2TC_P2T_SWEEP_CONTEXT_H__
+
+#include "../common/poly2tri-private.h"
+#include "../common/shapes.h"
+#include "advancing_front.h"
+
+/* Inital triangle factor, seed triangle will extend 30% of
+ * PointSet width to both left and right. */
+#define kAlpha 0.3
+
+struct P2tSweepContextBasin_
+{
+  P2tNode* left_node;
+  P2tNode* bottom_node;
+  P2tNode* right_node;
+  double width;
+  gboolean left_highest;
+};
+
+void p2t_sweepcontext_basin_init (P2tSweepContextBasin* THIS);
+void p2t_sweepcontext_basin_clear (P2tSweepContextBasin* THIS);
+
+struct P2tSweepContextEdgeEvent_
+{
+  P2tEdge* constrained_edge;
+  gboolean right;
+};
+
+void p2t_sweepcontext_edgeevent_init (P2tSweepContextEdgeEvent* THIS);
+
+struct SweepContext_
+{
+  P2tEdgePtrArray edge_list;
+
+  P2tSweepContextBasin basin;
+  P2tSweepContextEdgeEvent edge_event;
+
+  P2tTrianglePtrArray triangles_;
+  P2tTrianglePtrList map_;
+  P2tPointPtrArray points_;
+
+  /** Advancing front */
+  P2tAdvancingFront* front_;
+  /** head point used with advancing front */
+  P2tPoint* head_;
+  /** tail point used with advancing front */
+  P2tPoint* tail_;
+
+  P2tNode *af_head_, *af_middle_, *af_tail_;
+};
+
+/** Constructor */
+void p2t_sweepcontext_init (P2tSweepContext* THIS, P2tPointPtrArray polyline);
+P2tSweepContext* p2t_sweepcontext_new (P2tPointPtrArray polyline);
+
+/** Destructor */
+void p2t_sweepcontext_destroy (P2tSweepContext* THIS);
+void p2t_sweepcontext_delete (P2tSweepContext* THIS);
+
+void p2t_sweepcontext_set_head (P2tSweepContext *THIS, P2tPoint* p1);
+
+P2tPoint* p2t_sweepcontext_head (P2tSweepContext *THIS);
+
+void p2t_sweepcontext_set_tail (P2tSweepContext *THIS, P2tPoint* p1);
+
+P2tPoint* p2t_sweepcontext_tail (P2tSweepContext *THIS);
+
+int p2t_sweepcontext_point_count (P2tSweepContext *THIS);
+
+P2tNode* p2t_sweepcontext_locate_node (P2tSweepContext *THIS, P2tPoint* point);
+
+void p2t_sweepcontext_remove_node (P2tSweepContext *THIS, P2tNode* node);
+
+void p2t_sweepcontext_create_advancingfront (P2tSweepContext *THIS, P2tNodePtrArray nodes);
+
+/** Try to map a node to all sides of this triangle that don't have a neighbor */
+void p2t_sweepcontext_map_triangle_to_nodes (P2tSweepContext *THIS, P2tTriangle* t);
+
+void p2t_sweepcontext_add_to_map (P2tSweepContext *THIS, P2tTriangle* triangle);
+
+P2tPoint* p2t_sweepcontext_get_point (P2tSweepContext *THIS, const int index);
+
+P2tPoint* SweepContext_GetPoints (P2tSweepContext *THIS);
+
+void p2t_sweepcontext_remove_from_map (P2tSweepContext *THIS, P2tTriangle* triangle);
+
+void p2t_sweepcontext_add_hole (P2tSweepContext *THIS, P2tPointPtrArray polyline);
+
+void p2t_sweepcontext_add_point (P2tSweepContext *THIS, P2tPoint* point);
+
+P2tAdvancingFront* p2t_sweepcontext_front (P2tSweepContext *THIS);
+
+void p2t_sweepcontext_mesh_clean (P2tSweepContext *THIS, P2tTriangle* triangle);
+
+P2tTrianglePtrArray p2t_sweepcontext_get_triangles (P2tSweepContext *THIS);
+P2tTrianglePtrList p2t_sweepcontext_get_map (P2tSweepContext *THIS);
+
+void p2t_sweepcontext_init_triangulation (P2tSweepContext *THIS);
+void p2t_sweepcontext_init_edges (P2tSweepContext *THIS, P2tPointPtrArray polyline);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/bounded-line.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/bounded-line.c
new file mode 100644
index 000000000..ad0858068
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/bounded-line.c
@@ -0,0 +1,84 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include "bounded-line.h"
+
+P2trBoundedLine*
+p2tr_bounded_line_new (const P2trVector2 *start,
+                       const P2trVector2 *end)
+{
+  P2trBoundedLine* line = g_slice_new (P2trBoundedLine);
+  p2tr_bounded_line_init (line, start, end);
+  return line;
+}
+
+void
+p2tr_bounded_line_init (P2trBoundedLine   *line,
+                        const P2trVector2 *start,
+                        const P2trVector2 *end)
+{
+  /* Traditional line Equation:
+   * y - mx - n = 0   <==>   y = mx + n
+   * Slope Equation:
+   * m = dy / dx
+   * Slope + Traditional:
+   * dx * y - dy * x - dx * n = 0
+   * And the remaining part can be found as
+   * dx * y0 - dy * x0 = dx * n
+   * So the final equation is:
+   * dx * y - dy * x - (dx * y0 - dy * x0) = 0
+   */
+  gdouble dx = end->x - start->x;
+  gdouble dy = end->y - start->y;
+
+  gdouble dxXn = start->y * dx - start->x * dy;
+
+  p2tr_line_init(&line->infinite, -dy, dx, -dxXn);
+
+  p2tr_vector2_copy(&line->start, start);
+  p2tr_vector2_copy(&line->end, end);
+}
+
+gboolean
+p2tr_bounded_line_intersect (const P2trBoundedLine *l1,
+                             const P2trBoundedLine *l2)
+{
+  return p2tr_line_different_sides (&l1->infinite, &l2->start, &l2->end)
+    && p2tr_line_different_sides (&l2->infinite, &l1->start, &l1->end);
+}
+
+void
+p2tr_bounded_line_free (P2trBoundedLine *line)
+{
+  g_slice_free (P2trBoundedLine, line);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/bounded-line.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/bounded-line.h
new file mode 100644
index 000000000..577259d56
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/bounded-line.h
@@ -0,0 +1,58 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_BOUNDED_LINE_H__
+#define __P2TC_REFINE_BOUNDED_LINE_H__
+
+#include <glib.h>
+#include "vector2.h"
+#include "line.h"
+
+typedef struct
+{
+  P2trLine infinite;
+  P2trVector2 start, end;
+} P2trBoundedLine;
+
+P2trBoundedLine*  p2tr_bounded_line_new       (const P2trVector2 *start,
+                                               const P2trVector2 *end);
+
+void              p2tr_bounded_line_init      (P2trBoundedLine   *line,
+                                               const P2trVector2 *start,
+                                               const P2trVector2 *end);
+
+gboolean          p2tr_bounded_line_intersect (const P2trBoundedLine *l1,
+                                               const P2trBoundedLine *l2);
+
+void              p2tr_bounded_line_free      (P2trBoundedLine *line);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/cdt-flipfix.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/cdt-flipfix.c
new file mode 100644
index 000000000..3efeb0989
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/cdt-flipfix.c
@@ -0,0 +1,159 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+
+#include "point.h"
+#include "edge.h"
+#include "triangle.h"
+#include "vedge.h"
+
+#include "cdt-flipfix.h"
+
+
+static P2trEdge*  p2tr_cdt_try_flip  (P2trCDT   *self,
+                                      P2trEdge  *to_flip);
+
+/* This function implements "Lawson's algorithm", also known as "The
+ * diagonal swapping algorithm". This algorithm takes a CDT, and a list
+ * of triangles that were formed by the insertion of a new point into
+ * the triangular mesh, and makes the triangulation a CDT once more. Its
+ * logic is explained below:
+ *
+ *   If a point is added to an existing triangular mesh then
+ *   circumcircles are formed for all new triangles formed. If any of
+ *   the neighbours lie inside the circumcircle of any triangle, then a
+ *   quadrilateral is formed using the triangle and its neighbour. The
+ *   diagonals of this quadrilateral are swapped to give a new
+ *   triangulation. This process is continued till there are no more
+ *   faulty triangles and no more swaps are required.
+ *
+ * The description above may seem slightly inaccurate, as it does not
+ * consider the case were the diagonals can not be swapped since the
+ * quadrilateral is concave (then swapping the diagonals would result
+ * in a diagonal outside the quad, which is undesired).
+ *
+ * However, the description above is accurate. If the opposite point
+ * is inside the circular cut created by the edge, then since the
+ * circular cut is inside the infinite area created by extending the
+ * two other edges of the triangle, therefor the point is also inside
+ * that area - meaning that the quadrilateral is not concave!
+ */
+
+void
+p2tr_cdt_flip_fix (P2trCDT     *self,
+                   P2trVEdgeSet *candidates)
+{
+  P2trEdge *edge;
+  P2trVEdge *vedge;
+
+  while (p2tr_vedge_set_pop (candidates, &vedge))
+    {
+      if (! p2tr_vedge_try_get_and_unref (vedge, &edge))
+        continue;
+
+      if (! edge->constrained
+          /* TODO: we probably don't need this check... */
+          && ! p2tr_edge_is_removed (edge))
+        {
+          /* If the edge is not constrained, then it should be
+           * a part of two triangles */
+          P2trPoint *A  = P2TR_EDGE_START(edge), *B = edge->end;
+          P2trPoint *C1 = p2tr_triangle_get_opposite_point (edge->tri, edge, FALSE);
+          P2trPoint *C2 = p2tr_triangle_get_opposite_point (edge->mirror->tri, edge->mirror, FALSE);
+
+          P2trEdge *flipped = p2tr_cdt_try_flip (self, edge);
+          if (flipped != NULL)
+            {
+              p2tr_vedge_set_add (candidates, p2tr_point_get_edge_to (A, C1, TRUE));
+              p2tr_vedge_set_add (candidates, p2tr_point_get_edge_to (A, C2, TRUE));
+              p2tr_vedge_set_add (candidates, p2tr_point_get_edge_to (B, C1, TRUE));
+              p2tr_vedge_set_add (candidates, p2tr_point_get_edge_to (B, C2, TRUE));
+              p2tr_edge_unref (flipped);
+            }
+        }
+
+      p2tr_edge_unref (edge);
+    }
+}
+
+/**
+ * Try to flip a given edge, If successfull, return the new edge (reffed!),
+ * otherwise return NULL
+ */
+P2trEdge*
+p2tr_cdt_try_flip (P2trCDT   *self,
+                   P2trEdge  *to_flip)
+{
+  /*    C
+   *  / | \
+   * B-----A    to_flip: A->B
+   *  \ | /     to_flip.Tri: ABC
+   *    D
+   */
+  P2trPoint *A, *B, *C, *D;
+  P2trEdge *AB, *CA, *AD, *DB, *BC, *DC;
+
+  g_assert (! to_flip->constrained && ! to_flip->delaunay);
+
+  A = P2TR_EDGE_START (to_flip);
+  B = to_flip->end;
+  C = p2tr_triangle_get_opposite_point (to_flip->tri, to_flip, FALSE);
+  D = p2tr_triangle_get_opposite_point (to_flip->mirror->tri, to_flip->mirror, FALSE);
+
+  AB = to_flip;
+
+  /* Check if the quadriliteral ADBC is concave (because if it is, we
+   * can't flip the edge) */
+  if (p2tr_triangle_circumcircle_contains_point (AB->tri, &D->c) != P2TR_INCIRCLE_IN)
+    return NULL;
+
+  CA = p2tr_point_get_edge_to (C, A, FALSE);
+  AD = p2tr_point_get_edge_to (A, D, FALSE);
+  DB = p2tr_point_get_edge_to (D, B, FALSE);
+  BC = p2tr_point_get_edge_to (B, C, FALSE);
+
+  p2tr_edge_remove (AB);
+
+  DC = p2tr_mesh_new_edge (self->mesh, D, C, FALSE);
+
+  p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh,
+      CA, AD, DC));
+
+  p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh,
+      DB, BC, DC->mirror));
+
+  return DC;
+}
+
+
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/cdt-flipfix.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/cdt-flipfix.h
new file mode 100644
index 000000000..dad11b2e4
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/cdt-flipfix.h
@@ -0,0 +1,49 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_CDT_FLIPFIX_H__
+#define __P2TC_REFINE_CDT_FLIPFIX_H__
+
+#include <glib.h>
+
+#include "edge.h"
+#include "vedge.h"
+#include "cdt.h"
+#include "utils.h"
+
+/**
+ * Flip-Fix all the virtual edges inside the given set
+ */
+void         p2tr_cdt_flip_fix  (P2trCDT     *self,
+                                 P2trVEdgeSet *candidates);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/cdt.c b/subprojects/poly2tri-c/poly2tri-c/refine/cdt.c
new file mode 100644
index 000000000..4929d344c
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/cdt.c
@@ -0,0 +1,493 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <glib.h>
+
+#include "point.h"
+#include "edge.h"
+#include "triangle.h"
+
+#include "cdt.h"
+#include "visibility.h"
+#include "cdt-flipfix.h"
+
+static gboolean  p2tr_cdt_visible_from_tri        (P2trCDT      *self,
+                                                   P2trTriangle *tri,
+                                                   P2trVector2  *p);
+
+static gboolean  p2tr_cdt_has_empty_circum_circle (P2trCDT      *self,
+                                                   P2trTriangle *tri);
+
+static P2trHashSet* p2tr_cdt_triangulate_fan         (P2trCDT   *self,
+                                                      P2trPoint *center,
+                                                      GList     *edge_pts);
+
+void
+p2tr_cdt_validate_unused (P2trCDT* self)
+{
+  P2trEdge *ed;
+  P2trTriangle *tri;
+  P2trHashSetIter iter;
+
+  p2tr_hash_set_iter_init (&iter, self->mesh->edges);
+  while (p2tr_hash_set_iter_next (&iter, (gpointer*)&ed))
+    {
+      g_assert (ed->mirror != NULL);
+      g_assert (! p2tr_edge_is_removed (ed));
+    }
+
+  p2tr_hash_set_iter_init (&iter, self->mesh->triangles);
+  while (p2tr_hash_set_iter_next (&iter, (gpointer*)&tri))
+    g_assert (! p2tr_triangle_is_removed (tri));
+}
+
+P2trCDT*
+p2tr_cdt_new (P2tCDT *cdt)
+{
+  P2tTrianglePtrArray cdt_tris = p2t_cdt_get_triangles (cdt);
+  GHashTable *point_map = g_hash_table_new (g_direct_hash, g_direct_equal);
+  P2trCDT *rmesh = g_slice_new (P2trCDT);
+  GHashTableIter iter;
+  P2trPoint *pt_iter = NULL;
+
+  P2trVEdgeSet *new_edges = p2tr_vedge_set_new ();
+
+  gint i, j;
+
+  rmesh->mesh = p2tr_mesh_new ();
+  rmesh->outline = p2tr_pslg_new ();
+
+  /* First iteration over the CDT - create all the points */
+  for (i = 0; i < cdt_tris->len; i++)
+  {
+    P2tTriangle *cdt_tri = triangle_index (cdt_tris, i);
+    for (j = 0; j < 3; j++)
+      {
+        P2tPoint *cdt_pt = p2t_triangle_get_point(cdt_tri, j);
+        P2trPoint *new_pt = (P2trPoint*) g_hash_table_lookup (point_map, cdt_pt);
+
+        if (new_pt == NULL)
+          {
+            new_pt = p2tr_mesh_new_point2 (rmesh->mesh, cdt_pt->x, cdt_pt->y);
+            g_hash_table_insert (point_map, cdt_pt, new_pt);
+          }
+      }
+  }
+
+  /* Second iteration over the CDT - create all the edges and find the
+   * outline */
+  for (i = 0; i < cdt_tris->len; i++)
+  {
+    P2tTriangle *cdt_tri = triangle_index (cdt_tris, i);
+
+    for (j = 0; j < 3; j++)
+      {
+        P2tPoint *start = p2t_triangle_get_point (cdt_tri, j);
+        P2tPoint *end = p2t_triangle_get_point (cdt_tri, (j + 1) % 3);
+        int edge_index = p2t_triangle_edge_index (cdt_tri, start, end);
+
+        P2trPoint *start_new = (P2trPoint*) g_hash_table_lookup (point_map, start);
+        P2trPoint *end_new = (P2trPoint*) g_hash_table_lookup (point_map, end);
+
+        if (! p2tr_point_has_edge_to (start_new, end_new))
+          {
+            gboolean constrained = cdt_tri->constrained_edge[edge_index]
+            || cdt_tri->neighbors_[edge_index] == NULL;
+            P2trEdge *edge = p2tr_mesh_new_edge (rmesh->mesh, start_new, end_new, constrained);
+
+            /* If the edge is constrained, we should add it to the
+             * outline */
+            if (constrained)
+              p2tr_pslg_add_new_line(rmesh->outline, &start_new->c,
+                  &end_new->c);
+
+            /* We only wanted to create the edge now. We will use it
+             * later */
+            p2tr_vedge_set_add (new_edges, edge);
+          }
+      }
+  }
+
+  /* Third iteration over the CDT - create all the triangles */
+  for (i = 0; i < cdt_tris->len; i++)
+  {
+    P2tTriangle *cdt_tri = triangle_index (cdt_tris, i);
+
+    P2trPoint *pt1 = (P2trPoint*) g_hash_table_lookup (point_map, p2t_triangle_get_point (cdt_tri, 0));
+    P2trPoint *pt2 = (P2trPoint*) g_hash_table_lookup (point_map, p2t_triangle_get_point (cdt_tri, 1));
+    P2trPoint *pt3 = (P2trPoint*) g_hash_table_lookup (point_map, p2t_triangle_get_point (cdt_tri, 2));
+
+    P2trTriangle *new_tri = p2tr_mesh_new_triangle (rmesh->mesh,
+        p2tr_point_get_edge_to(pt1, pt2, FALSE),
+        p2tr_point_get_edge_to(pt2, pt3, FALSE),
+        p2tr_point_get_edge_to(pt3, pt1, FALSE));
+
+    /* We won't do any usage of the triangle, so just unref it */
+    p2tr_triangle_unref (new_tri);
+  }
+
+  /* And do an extra flip fix */
+  p2tr_cdt_flip_fix (rmesh, new_edges);
+
+  p2tr_vedge_set_free (new_edges);
+
+  /* Now finally unref the points we added into the map */
+  g_hash_table_iter_init (&iter, point_map);
+  while (g_hash_table_iter_next (&iter, NULL, (gpointer*)&pt_iter))
+    p2tr_point_unref (pt_iter);
+  g_hash_table_destroy (point_map);
+
+  return rmesh;
+}
+
+void
+p2tr_cdt_free (P2trCDT *self)
+{
+  p2tr_cdt_free_full (self, TRUE);
+}
+
+void
+p2tr_cdt_free_full (P2trCDT* self, gboolean clear_mesh)
+{
+  p2tr_pslg_free (self->outline);
+  if (clear_mesh)
+    p2tr_mesh_clear (self->mesh);
+  p2tr_mesh_unref (self->mesh);
+
+  g_slice_free (P2trCDT, self);
+}
+
+void
+p2tr_cdt_validate_edges (P2trCDT *self)
+{
+  P2trHashSetIter iter;
+  P2trEdge *e;
+
+  p2tr_hash_set_iter_init (&iter, self->mesh->edges);
+  while (p2tr_hash_set_iter_next (&iter, (gpointer*)&e))
+    {
+      if (! e->constrained && e->tri == NULL)
+        p2tr_exception_geometric ("Found a non constrained edge without a triangle");
+
+      if (e->tri != NULL)
+        {
+          gboolean found = FALSE;
+          gint i = 0;
+
+          for (i = 0; i < 3; i++)
+            if (e->tri->edges[i] == e)
+              {
+                found = TRUE;
+                break;
+              }
+
+          if (! found)
+              p2tr_exception_geometric ("An edge has a triangle to which it does not belong!");
+        }
+    }
+}
+
+gboolean
+p2tr_cdt_visible_from_edge (P2trCDT     *self,
+                            P2trEdge    *e,
+                            P2trVector2 *p)
+{
+  P2trBoundedLine line;
+
+  p2tr_bounded_line_init (&line, &P2TR_EDGE_START(e)->c, &e->end->c);
+
+  return p2tr_visibility_is_visible_from_edges (self->outline, p, &line, 1);
+}
+
+static gboolean
+p2tr_cdt_visible_from_tri (P2trCDT      *self,
+                           P2trTriangle *tri,
+                           P2trVector2  *p)
+{
+  P2trBoundedLine lines[3];
+  gint i;
+
+  for (i = 0; i < 3; i++)
+    p2tr_bounded_line_init (&lines[i],
+        &P2TR_EDGE_START(tri->edges[i])->c,
+        &tri->edges[i]->end->c);
+
+  return p2tr_visibility_is_visible_from_edges (self->outline, p, lines, 3);
+}
+
+static gboolean
+p2tr_cdt_has_empty_circum_circle (P2trCDT      *self,
+                                  P2trTriangle *tri)
+{
+  P2trCircle circum;
+  P2trPoint *p;
+  P2trHashSetIter iter;
+
+  p2tr_triangle_get_circum_circle (tri, &circum);
+
+  p2tr_hash_set_iter_init (&iter, self->mesh->points);
+  while (p2tr_hash_set_iter_next (&iter, (gpointer*)&p))
+    {
+      /** TODO: FIXME - is a point on a constrained edge really not a
+       * problem?! */
+      if (p2tr_point_has_constrained_edge (p)
+          /* The points of a triangle can't violate its own empty
+           * circumcircle property */
+          || p == tri->edges[0]->end
+          || p == tri->edges[1]->end
+          || p == tri->edges[2]->end)
+          continue;
+
+      if (! p2tr_circle_test_point_outside(&circum, &p->c)
+          && p2tr_cdt_visible_from_tri (self, tri, &p->c))
+          return FALSE;
+    }
+  return TRUE;
+}
+
+void
+p2tr_cdt_validate_cdt (P2trCDT *self)
+{
+  P2trHashSetIter iter;
+  P2trTriangle *tri;
+
+  p2tr_hash_set_iter_init (&iter, self->mesh->triangles);
+  while (p2tr_hash_set_iter_next (&iter, (gpointer*)&tri))
+    if (! p2tr_cdt_has_empty_circum_circle(self, tri))
+      p2tr_exception_geometric ("Not a CDT!");
+}
+
+P2trPoint*
+p2tr_cdt_insert_point (P2trCDT           *self,
+                       const P2trVector2 *pc,
+                       P2trTriangle      *point_location_guess)
+{
+  P2trTriangle *tri;
+  P2trPoint    *pt;
+  gboolean      inserted = FALSE;
+  gint          i;
+
+  P2TR_CDT_VALIDATE_UNUSED (self);
+
+  if (point_location_guess == NULL)
+    tri = p2tr_mesh_find_point (self->mesh, pc);
+  else
+    tri = p2tr_mesh_find_point_local (self->mesh, pc, point_location_guess);
+
+  if (tri == NULL)
+    p2tr_exception_geometric ("Tried to add point outside of domain!");
+
+  pt = p2tr_mesh_new_point (self->mesh, pc);
+
+  /* If the point falls on a line, we should split the line */
+  for (i = 0; i < 3; i++)
+    {
+      P2trEdge *edge = tri->edges[i];
+      if (p2tr_math_orient2d (& P2TR_EDGE_START(edge)->c,
+              &edge->end->c, pc) == P2TR_ORIENTATION_LINEAR)
+        {
+          GList *parts = p2tr_cdt_split_edge (self, edge, pt), *eIter;
+          for (eIter = parts; eIter != NULL; eIter = eIter->next)
+            p2tr_edge_unref ((P2trEdge*)eIter->data);
+          g_list_free(parts);
+
+          inserted = TRUE;
+          break;
+        }
+    }
+
+  if (! inserted)
+    /* If we reached this line, then the point is inside the triangle */
+    p2tr_cdt_insert_point_into_triangle (self, pt, tri);
+
+  /* We no longer need the triangle */
+  p2tr_triangle_unref (tri);
+
+  P2TR_CDT_VALIDATE_UNUSED (self);
+  return pt;
+}
+
+/** Insert a point into a triangle. This function assumes the point is
+ * inside the triangle - not on one of its edges and not outside of it.
+ */
+void
+p2tr_cdt_insert_point_into_triangle (P2trCDT      *self,
+                                     P2trPoint    *P,
+                                     P2trTriangle *tri)
+{
+  P2trVEdgeSet *flip_candidates = p2tr_vedge_set_new ();
+
+  P2trPoint *A = tri->edges[0]->end;
+  P2trPoint *B = tri->edges[1]->end;
+  P2trPoint *C = tri->edges[2]->end;
+
+  P2trEdge *CA = tri->edges[0];
+  P2trEdge *AB = tri->edges[1];
+  P2trEdge *BC = tri->edges[2];
+
+  P2trEdge *AP, *BP, *CP;
+
+  p2tr_triangle_remove (tri);
+
+  AP = p2tr_mesh_new_edge (self->mesh, A, P, FALSE);
+  BP = p2tr_mesh_new_edge (self->mesh, B, P, FALSE);
+  CP = p2tr_mesh_new_edge (self->mesh, C, P, FALSE);
+
+  p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, AB, BP, AP->mirror));
+  p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, BC, CP, BP->mirror));
+  p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, CA, AP, CP->mirror));
+
+  p2tr_vedge_set_add (flip_candidates, CP);
+  p2tr_vedge_set_add (flip_candidates, AP);
+  p2tr_vedge_set_add (flip_candidates, BP);
+
+  p2tr_vedge_set_add (flip_candidates, p2tr_edge_ref (CA));
+  p2tr_vedge_set_add (flip_candidates, p2tr_edge_ref (AB));
+  p2tr_vedge_set_add (flip_candidates, p2tr_edge_ref (BC));
+
+  /* Flip fix the newly created triangles to preserve the the
+   * constrained delaunay property. The flip-fix function will unref the
+   * new triangles for us! */
+  p2tr_cdt_flip_fix (self, flip_candidates);
+
+  p2tr_vedge_set_free (flip_candidates);
+}
+
+/**
+ * Triangulate a polygon by creating edges to a center point.
+ * 1. If there is a NULL point in the polygon, two triangles are not
+ *    created (these are the two that would have used it)
+ * 2. THE RETURNED EDGES MUST BE UNREFFED!
+ */
+static P2trVEdgeSet*
+p2tr_cdt_triangulate_fan (P2trCDT   *self,
+                          P2trPoint *center,
+                          GList     *edge_pts)
+{
+  P2trVEdgeSet* fan_edges = p2tr_vedge_set_new ();
+  GList *iter;
+
+  /* We can not triangulate unless at least two points are given */
+  if (edge_pts == NULL || edge_pts->next == NULL)
+    {
+      p2tr_exception_programmatic ("Not enough points to triangulate as"
+          " a star!");
+    }
+
+  for (iter = edge_pts; iter != NULL; iter = iter->next)
+    {
+      P2trPoint *A = (P2trPoint*) iter->data;
+      P2trPoint *B = (P2trPoint*) g_list_cyclic_next (edge_pts, iter)->data;
+      P2trEdge *AB, *BC, *CA;
+
+      if (A == NULL || B == NULL)
+        continue;
+
+      AB = p2tr_point_get_edge_to (A, B, TRUE);
+      BC = p2tr_mesh_new_or_existing_edge (self->mesh, B, center, FALSE);
+      CA = p2tr_mesh_new_or_existing_edge (self->mesh, center, A, FALSE);
+
+      p2tr_triangle_unref (p2tr_mesh_new_triangle (self->mesh, AB, BC, CA));
+
+      p2tr_vedge_set_add (fan_edges, CA);
+      p2tr_vedge_set_add (fan_edges, BC);
+      p2tr_vedge_set_add (fan_edges, AB);
+    }
+
+  return fan_edges;
+}
+
+GList*
+p2tr_cdt_split_edge (P2trCDT   *self,
+                     P2trEdge  *e,
+                     P2trPoint *C)
+{
+  /*      W
+   *     /|\
+   *    / | \
+   *   /  |  \      E.Mirror.Tri: YXW
+   * X*---*---*Y    E: X->Y
+   *   \  |C /      E.Tri: XYV
+   *    \ | /
+   *     \|/
+   *      V
+   */
+  P2trPoint *X = P2TR_EDGE_START (e), *Y = e->end;
+  P2trPoint *V = (e->tri != NULL) ? p2tr_triangle_get_opposite_point(e->tri, e, FALSE) : NULL;
+  P2trPoint *W = (e->mirror->tri != NULL) ? p2tr_triangle_get_opposite_point (e->mirror->tri, e->mirror, 
FALSE) : NULL;
+  gboolean   constrained = e->constrained;
+  P2trEdge  *XC, *CY;
+  GList     *fan = NULL, *new_edges = NULL;
+  P2trHashSet *fan_edges;
+
+  P2TR_CDT_VALIDATE_UNUSED (self);
+
+  p2tr_edge_remove (e);
+
+  XC = p2tr_mesh_new_edge (self->mesh, X, C, constrained);
+  CY = p2tr_mesh_new_edge (self->mesh, C, Y, constrained);
+
+  fan = p2tr_utils_new_reversed_pointer_list (4, W, X, V, Y);
+  fan_edges = p2tr_cdt_triangulate_fan (self, C, fan);
+  g_list_free (fan);
+
+  /* Now make this a CDT again
+   * The new triangles will be unreffed by the flip_fix function, which
+   * is good since we receive them with an extra reference!
+   */
+  p2tr_cdt_flip_fix (self, fan_edges);
+  p2tr_hash_set_free (fan_edges);
+
+  if (constrained)
+    {
+      /* If this was a subsegment, then both parts of the subsegment
+       * should exist */
+      if (p2tr_edge_is_removed (XC) || p2tr_edge_is_removed (CY))
+        p2tr_exception_geometric ("Subsegments gone!");
+      else
+        {
+          new_edges = g_list_prepend (new_edges, CY);
+          new_edges = g_list_prepend (new_edges, XC);
+        }
+    }
+  else
+    {
+      p2tr_edge_unref (XC);
+      p2tr_edge_unref (CY);
+    }
+
+  P2TR_CDT_VALIDATE_UNUSED (self);
+
+  return new_edges;
+}
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/cdt.h b/subprojects/poly2tri-c/poly2tri-c/refine/cdt.h
new file mode 100644
index 000000000..23455a66c
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/cdt.h
@@ -0,0 +1,127 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_CDT_H__
+#define __P2TC_REFINE_CDT_H__
+
+#include <poly2tri-c/p2t/poly2tri.h>
+#include "mesh.h"
+#include "pslg.h"
+
+typedef struct
+{
+  P2trMesh *mesh;
+  P2trPSLG *outline;
+} P2trCDT;
+
+/**
+ * Create a new P2trCDT from an existing P2tCDT. The resulting P2trCDT
+ * does not depend on the original P2tCDT which can be freed
+ * @param cdt A P2tCDT Constrained Delaunay Triangulation
+ * @return A P2trCDT Constrained Delaunay Triangulation
+ */
+P2trCDT*    p2tr_cdt_new       (P2tCDT *cdt);
+
+void        p2tr_cdt_free      (P2trCDT *cdt);
+
+void        p2tr_cdt_free_full (P2trCDT *cdt, gboolean clear_mesh);
+
+/**
+ * Test whether there is a path from the point @ref p to the edge @e
+ * so that the path does not cross any segment of the CDT
+ */
+gboolean    p2tr_cdt_visible_from_edge (P2trCDT     *self,
+                                        P2trEdge    *e,
+                                        P2trVector2 *p);
+
+/**
+ * Make sure that all edges either have two triangles (one on each side)
+ * or that they are constrained. The reason for that is that the
+ * triangulation domain of the CDT is defined by a closed PSLG.
+ */
+void        p2tr_cdt_validate_edges    (P2trCDT *self);
+
+void        p2tr_cdt_validate_unused   (P2trCDT* self);
+
+/**
+ * Make sure the constrained empty circum-circle property holds,
+ * meaning that each triangles circum-scribing circle is either empty
+ * or only contains points which are not "visible" from the edges of
+ * the triangle.
+ */
+void        p2tr_cdt_validate_cdt      (P2trCDT *self);
+
+#if P2TR_CDT_VALIDATE
+#define P2TR_CDT_VALIDATE_EDGES(CDT)  p2tr_cdt_validate_edges(CDT)
+#define P2TR_CDT_VALIDATE_UNUSED(CDT) p2tr_cdt_validate_unused(CDT)
+#define P2TR_CDT_VALIDATE_CDT(CDT)    p2tr_cdt_validate_cdt(CDT)
+#else
+#define P2TR_CDT_VALIDATE_EDGES(CDT)  G_STMT_START { } G_STMT_END
+#define P2TR_CDT_VALIDATE_UNUSED(CDT) G_STMT_START { } G_STMT_END
+#define P2TR_CDT_VALIDATE_CDT(CDT)    G_STMT_START { } G_STMT_END
+#endif
+
+/**
+ * Insert a point into the triangulation while preserving the
+ * constrained delaunay property
+ * @param self The CDT into which the point should be inserted
+ * @param pc The point to insert
+ * @param point_location_guess A triangle which may be near the
+ *        area containing the point (or maybe even contains the point).
+ *        The better the guess is, the faster the insertion will be. If
+ *        such a triangle is unknown, NULL may be passed.
+ */
+P2trPoint*  p2tr_cdt_insert_point      (P2trCDT           *self,
+                                        const P2trVector2 *pc,
+                                        P2trTriangle      *point_location_guess);
+
+/**
+ * Similar to @ref p2tr_cdt_insert_point, but assumes that the point to
+ * insert is located inside the area of the given triangle
+ */
+void        p2tr_cdt_insert_point_into_triangle (P2trCDT      *self,
+                                                 P2trPoint    *pt,
+                                                 P2trTriangle *tri);
+
+/**
+ * Insert a point so that is splits an existing edge, while preserving
+ * the constrained delaunay property. This function assumes that the
+ * point is on the edge itself and between its end-points.
+ * If the edge being split is constrained, then the function returns a
+ * list containing both parts resulted from the splitting. In that case,
+ * THE RETURNED EDGES MUST BE UNREFERENCED!
+ */
+GList*      p2tr_cdt_split_edge (P2trCDT   *self,
+                                 P2trEdge  *e,
+                                 P2trPoint *C);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/circle.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/circle.c
new file mode 100644
index 000000000..affb1f6ac
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/circle.c
@@ -0,0 +1,47 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include "circle.h"
+
+gboolean
+p2tr_circle_test_point_outside (P2trCircle  *circle,
+                                P2trVector2 *pt)
+{
+  gdouble dx = circle->center.x - pt->x;
+  gdouble dy = circle->center.y - pt->y;
+
+  gdouble d_squared = dx * dx + dy * dy;
+  gdouble radius_squared = circle->radius * circle->radius;
+
+  return d_squared > radius_squared;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/circle.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/circle.h
new file mode 100644
index 000000000..06bcdc33d
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/circle.h
@@ -0,0 +1,46 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_CIRCLE_H__
+#define __P2TC_REFINE_CIRCLE_H__
+
+#include <glib.h>
+#include "vector2.h"
+
+typedef struct {
+  P2trVector2 center;
+  gdouble radius;
+} P2trCircle;
+
+gboolean  p2tr_circle_test_point_outside (P2trCircle *circle, P2trVector2 *pt);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/cluster.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/cluster.c
new file mode 100644
index 000000000..ccf921705
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/cluster.c
@@ -0,0 +1,146 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+
+#include <glib.h>
+#include "utils.h"
+#include "point.h"
+#include "edge.h"
+
+#include "cluster.h"
+
+static gboolean p2tr_cluster_cw_tri_between_is_in_domain (P2trEdge *e1,
+                                                          P2trEdge *e2);
+
+gdouble
+p2tr_cluster_shortest_edge_length (P2trCluster *self)
+{
+  gdouble min_length_sq = G_MAXDOUBLE, temp;
+  GList *iter;
+  
+  for (iter = self->edges.head; iter != NULL; iter = iter->next)
+    {
+      temp = p2tr_edge_get_length_squared ((P2trEdge*)iter->data);
+      min_length_sq = MIN(min_length_sq, temp);
+    }
+  return sqrt (min_length_sq);
+}
+
+/**
+ * Return the edge cluster of the specified edge from the specified end
+ * point. THE EDGES IN THE CLUSTER MUST BE UNREFFED!
+ * @param[in] P The point which is shared between all edges of the cluster
+ * @param[in] E The edge whose cluster should be returned
+ * @return The cluster of @ref E from the point @ref P
+ */
+P2trCluster*
+p2tr_cluster_get_for (P2trPoint   *P,
+                      P2trEdge    *E)
+{
+  P2trCluster *cluster = g_slice_new (P2trCluster);
+  gdouble temp_angle;
+  P2trEdge *current, *next;
+  
+  cluster->min_angle = G_MAXDOUBLE;
+  g_queue_init (&cluster->edges);
+
+  if (P == E->end)
+    E = E->mirror;
+  else if (P != P2TR_EDGE_START (E))
+    p2tr_exception_programmatic ("Unexpected point for the edge!");
+
+  g_queue_push_head (&cluster->edges, p2tr_edge_ref (E));
+
+  current = p2tr_edge_ref (E);
+  next = p2tr_point_edge_cw (P, current);
+  
+  while (next != g_queue_peek_head (&cluster->edges)
+      && (temp_angle = p2tr_edge_angle_between (current->mirror, next)) <= P2TR_CLUSTER_LIMIT_ANGLE
+      && p2tr_cluster_cw_tri_between_is_in_domain (current, next))
+    {
+      g_queue_push_tail (&cluster->edges, p2tr_edge_ref (next));
+      p2tr_edge_unref (current);
+      current = next;
+      next = p2tr_point_edge_cw (P, current);
+      cluster->min_angle = MIN (cluster->min_angle, temp_angle);
+    }
+  p2tr_edge_unref (current);
+  p2tr_edge_unref (next);
+
+  current = p2tr_edge_ref (E);
+  next = p2tr_point_edge_ccw (P, current);
+
+  while (next != g_queue_peek_tail (&cluster->edges)
+      && (temp_angle = p2tr_edge_angle_between (current->mirror, next)) <= P2TR_CLUSTER_LIMIT_ANGLE
+      && p2tr_cluster_cw_tri_between_is_in_domain (next, current))
+    {
+      g_queue_push_head (&cluster->edges, p2tr_edge_ref (next));
+      p2tr_edge_unref (current);
+      current = next;
+      next = p2tr_point_edge_ccw (P, current);
+      cluster->min_angle = MIN(cluster->min_angle, temp_angle);
+    }
+  p2tr_edge_unref (current);
+  p2tr_edge_unref (next);
+
+  return cluster;
+}
+
+/*     ^ e1
+ *    /
+ *   /_ e1.Tri (e2.Mirror.Tri)
+ *  /  |
+ * *---------> e2
+ *
+ * Check if the angle marked is a part of the triangulation
+ * domain
+ */
+static gboolean
+p2tr_cluster_cw_tri_between_is_in_domain (P2trEdge *e1, P2trEdge *e2)
+{
+  if (P2TR_EDGE_START(e1) != P2TR_EDGE_START(e2) || e1->tri != e2->mirror->tri)
+    p2tr_exception_programmatic ("Non clockwise adjacent edges!");
+  return e1->tri != NULL;
+}
+
+void
+p2tr_cluster_free (P2trCluster *self)
+{
+  GList *iter;
+  
+  for (iter = self->edges.head; iter != NULL; iter = iter->next)
+    p2tr_edge_unref ((P2trEdge*)iter->data);
+
+  g_queue_clear (&self->edges);
+  g_slice_free (P2trCluster, self);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/cluster.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/cluster.h
new file mode 100644
index 000000000..6eaed8af4
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/cluster.h
@@ -0,0 +1,55 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_CLUSTER_H__
+#define __P2TC_REFINE_CLUSTER_H__
+
+#include <glib.h>
+#include "point.h"
+#include "edge.h"
+
+#define P2TR_CLUSTER_LIMIT_ANGLE (G_PI / 6)
+
+typedef struct
+{
+  GQueue   edges;
+  gdouble  min_angle;
+} P2trCluster;
+
+P2trCluster*  p2tr_cluster_get_for              (P2trPoint *P,
+                                                 P2trEdge  *E);
+
+gdouble       p2tr_cluster_shortest_edge_length (P2trCluster *self);
+
+void          p2tr_cluster_free                 (P2trCluster *self);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/delaunay-terminator.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/delaunay-terminator.c
new file mode 100644
index 000000000..1de1fea5d
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/delaunay-terminator.c
@@ -0,0 +1,534 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdio.h>
+#include <math.h>
+#include <glib.h>
+
+#include "utils.h"
+#include "rmath.h"
+
+#include "point.h"
+#include "edge.h"
+#include "triangle.h"
+
+#include "mesh.h"
+#include "cdt.h"
+#include "cluster.h"
+
+#include "vedge.h"
+#include "vtriangle.h"
+
+#include "delaunay-terminator.h"
+
+/* The code in this file is based on the "Delaunay Terminator" algorithm
+ * - an algorithm for refining constrained delaunay triangulations. The
+ * algorithm itself appears in a paper by Jonathan Richard Shewchuk as
+ * described below:
+ *
+ *   Delaunay Refinement Algorithms for Triangular Mesh Generation
+ *   Computational Geometry: Theory and Applications 22(1–3):21–74, May 2002
+ *   Jonathan Richard Shewchuk
+ *   http://www.cs.berkeley.edu/~jrs/papers/2dj.pdf
+ */
+
+gboolean
+p2tr_cdt_test_encroachment_ignore_visibility (const P2trVector2 *w,
+                                              P2trEdge          *e)
+{
+  return p2tr_math_diametral_circle_contains (&P2TR_EDGE_START(e)->c,
+      &e->end->c, w);
+}
+
+gboolean
+p2tr_cdt_is_encroached_by (P2trCDT     *self,
+                           P2trEdge    *e,
+                           P2trVector2 *p)
+{
+  if (! e->constrained)
+      return FALSE;
+
+  return p2tr_cdt_test_encroachment_ignore_visibility (p, e)
+      && p2tr_cdt_visible_from_edge (self, e, p);
+}
+
+
+P2trVEdgeSet*
+p2tr_cdt_get_segments_encroached_by (P2trCDT   *self,
+                                     P2trPoint *v)
+{
+  P2trVEdgeSet *encroached = p2tr_vedge_set_new ();
+  GList *iter;
+
+  for (iter = v->outgoing_edges; iter != NULL; iter = iter->next)
+    {
+      P2trEdge *outEdge = (P2trEdge*) iter->data;
+      P2trTriangle *t = outEdge->tri;
+      P2trEdge *e;
+
+      if (t == NULL)
+          continue;
+
+      e = p2tr_triangle_get_opposite_edge (t, v);
+
+      /* we want the fast check and for new points we don't
+       * use that check... So let's go on the full check
+       * since it's still faster */
+      if (e->constrained && p2tr_cdt_is_encroached (e))
+        p2tr_vedge_set_add2 (encroached, p2tr_vedge_new2 (e));
+
+      p2tr_edge_unref(e);
+    }
+
+  return encroached;
+}
+
+gboolean
+p2tr_cdt_is_encroached (P2trEdge *E)
+{
+  P2trTriangle *T1 = E->tri;
+  P2trTriangle *T2 = E->mirror->tri;
+
+  if (! E->constrained)
+      return FALSE;
+  
+  return (T1 != NULL && p2tr_cdt_test_encroachment_ignore_visibility (&p2tr_triangle_get_opposite_point (T1, 
E, FALSE)->c, E))
+      || (T2 != NULL && p2tr_cdt_test_encroachment_ignore_visibility (&p2tr_triangle_get_opposite_point (T2, 
E, FALSE)->c, E));
+}
+
+/* ****************************************************************** */
+/* Now for the algorithm itself                                       */
+/* ****************************************************************** */
+
+static gboolean
+SplitPermitted (P2trDelaunayTerminator *self, P2trEdge *s, gdouble d);
+
+static void
+SplitEncroachedSubsegments (P2trDelaunayTerminator *self, gdouble theta, P2trTriangleTooBig delta);
+
+static void
+NewVertex (P2trDelaunayTerminator *self, P2trPoint *v, gdouble theta, P2trTriangleTooBig delta);
+
+static gdouble
+ShortestEdgeLength (P2trTriangle *tri);
+
+static gboolean
+TolerantIsShorter (P2trEdge *toTest, P2trEdge *reference);
+
+static inline gdouble
+LOG2 (gdouble v);
+
+static gboolean
+TolerantIsPowerOfTwoLength (gdouble length);
+
+static void
+ChooseSplitVertex(P2trEdge *e, P2trVector2 *dst);
+
+
+
+static inline gint
+vtriangle_quality_compare (P2trVTriangle *t1, P2trVTriangle *t2)
+{
+  gdouble a1, a2;
+  P2trTriangle *r1, *r2;
+
+  r1 = p2tr_vtriangle_is_real (t1);
+  r2 = p2tr_vtriangle_is_real (t2);
+
+  /* TODO: untill we make sure that removed triangles will get out
+   * of Qt, we will make the comparision treat removed triangles as
+   * triangles with "less" quality (meaning they are "smaller")
+   */
+  if (!r1 || !r2)
+    return (!r1) ? -1 : (!r2 ? 1 : 0);
+
+  a1 = p2tr_triangle_smallest_non_constrained_angle (r1);
+  a2 = p2tr_triangle_smallest_non_constrained_angle (r2);
+  
+  return (a1 < a2) ? -1 : ((a1 == a2) ? 0 : 1);
+}
+
+P2trDelaunayTerminator*
+p2tr_dt_new (gdouble theta, P2trTriangleTooBig delta, P2trCDT *cdt)
+{
+  P2trDelaunayTerminator *self = g_slice_new (P2trDelaunayTerminator);
+  self->Qt = g_sequence_new (NULL);
+  g_queue_init (&self->Qs);
+  self->delta = delta;
+  self->theta = theta;
+  self->cdt = cdt;
+  return self;
+}
+
+void
+p2tr_dt_free (P2trDelaunayTerminator *self)
+{
+  g_queue_clear (&self->Qs);
+  g_sequence_free (self->Qt);
+  g_slice_free (P2trDelaunayTerminator, self);
+}
+
+static void
+p2tr_dt_enqueue_tri (P2trDelaunayTerminator *self,
+                     P2trTriangle           *tri)
+{
+  g_sequence_insert_sorted (self->Qt, p2tr_vtriangle_new (tri), (GCompareDataFunc)vtriangle_quality_compare, 
NULL);
+}
+
+static inline gboolean
+p2tr_dt_tri_queue_is_empty (P2trDelaunayTerminator *self)
+{
+  return g_sequence_iter_is_end (g_sequence_get_begin_iter (self->Qt));
+}
+
+static P2trVTriangle*
+p2tr_dt_dequeue_tri (P2trDelaunayTerminator *self)
+{
+  GSequenceIter *first = g_sequence_get_begin_iter (self->Qt);
+
+  /* If we have an empty sequence, return NULL */
+  if (p2tr_dt_tri_queue_is_empty (self))
+    return NULL;
+  else
+    {
+      P2trVTriangle *ret = (P2trVTriangle*) g_sequence_get (first);
+      g_sequence_remove (first);
+      return ret;
+    }
+}
+
+static void
+p2tr_dt_enqueue_segment (P2trDelaunayTerminator *self,
+                         P2trEdge               *E)
+{
+  if (! E->constrained)
+    p2tr_exception_programmatic ("Tried to append a non-segment!");
+
+  g_queue_push_tail (&self->Qs, p2tr_edge_ref (E));
+}
+
+static P2trEdge*
+p2tr_dt_dequeue_segment (P2trDelaunayTerminator *self)
+{
+  if (g_queue_is_empty (&self->Qs))
+    return NULL;
+  else
+    return (P2trEdge*) g_queue_pop_head (&self->Qs);
+}
+
+static gboolean
+p2tr_dt_segment_queue_is_empty (P2trDelaunayTerminator *self)
+{
+  return g_queue_is_empty (&self->Qs);
+}
+
+void
+p2tr_dt_refine (P2trDelaunayTerminator   *self,
+                gint                      max_steps,
+                P2trRefineProgressNotify  on_progress)
+{
+  P2trHashSetIter hs_iter;
+  P2trEdge *s;
+  P2trTriangle *t;
+  P2trVTriangle *vt;
+  gint steps = 0;
+
+  P2TR_CDT_VALIDATE_CDT (self->cdt);
+
+  if (steps++ >= max_steps)
+    return;
+
+  p2tr_hash_set_iter_init (&hs_iter, self->cdt->mesh->edges);
+    while (p2tr_hash_set_iter_next (&hs_iter, (gpointer*)&s))
+    if (s->constrained && p2tr_cdt_is_encroached (s))
+      p2tr_dt_enqueue_segment (self, s);
+
+  SplitEncroachedSubsegments (self, 0, p2tr_refiner_false_too_big);
+  P2TR_CDT_VALIDATE_CDT (self->cdt);
+
+  p2tr_hash_set_iter_init (&hs_iter, self->cdt->mesh->triangles);
+  while (p2tr_hash_set_iter_next (&hs_iter, (gpointer*)&t))
+    if (p2tr_triangle_smallest_non_constrained_angle (t) < self->theta)
+      p2tr_dt_enqueue_tri (self, t);
+
+  if (on_progress != NULL) on_progress ((P2trRefiner*) self, steps, max_steps);
+
+  while (! p2tr_dt_tri_queue_is_empty (self))
+    {
+      vt = p2tr_dt_dequeue_tri (self);
+      t = p2tr_vtriangle_is_real (vt);
+
+      if (t && steps++ < max_steps)
+        {
+          P2trCircle tCircum;
+          P2trVector2 *c;
+          P2trTriangle *triContaining_c;
+          P2trVEdgeSet *E;
+          P2trPoint *cPoint;
+
+          P2TR_CDT_VALIDATE_CDT (self->cdt);
+          p2tr_triangle_get_circum_circle (t, &tCircum);
+          c = &tCircum.center;
+
+          triContaining_c = p2tr_mesh_find_point_local (self->cdt->mesh, c, t);
+
+          /* If no edge is encroached, then this must be
+           * inside the triangulation domain!!! */
+          if (triContaining_c == NULL)
+            p2tr_exception_geometric ("Should not happen! (%f, %f) (Center of (%f,%f)->(%f,%f)->(%f,%f)) is 
outside the domain!", c->x, c->y,
+            vt->points[0]->c.x, vt->points[0]->c.y,
+            vt->points[1]->c.x, vt->points[1]->c.y,
+            vt->points[2]->c.x, vt->points[2]->c.y);
+
+          /* Now, check if this point would encroach any edge
+           * of the triangulation */
+          p2tr_mesh_action_group_begin (self->cdt->mesh);
+
+          cPoint = p2tr_cdt_insert_point (self->cdt, c, triContaining_c);
+          E = p2tr_cdt_get_segments_encroached_by (self->cdt, cPoint);
+
+          if (p2tr_vedge_set_size (E) == 0)
+            {
+              p2tr_mesh_action_group_commit (self->cdt->mesh);
+              NewVertex (self, cPoint, self->theta, self->delta);
+            }
+          else
+            {
+              P2trVEdge *vSegment;
+              gdouble d;
+
+              p2tr_mesh_action_group_undo (self->cdt->mesh);
+              /* The (reverted) changes to the mesh may have eliminated the
+               * original triangle t. We must restore it manually from
+               * the virtual triangle
+               */
+              t = p2tr_vtriangle_is_real (vt);
+              g_assert (t != NULL);
+
+              d = ShortestEdgeLength (t);
+
+              while (p2tr_vedge_set_pop (E, &vSegment))
+                {
+                  s = p2tr_vedge_get (vSegment);
+                  if (self->delta (t) || SplitPermitted(self, s, d))
+                    p2tr_dt_enqueue_segment (self, s);
+                  p2tr_edge_unref (s);
+                  p2tr_vedge_unref (vSegment);
+                }
+
+              if (! p2tr_dt_segment_queue_is_empty (self))
+                {
+                  p2tr_dt_enqueue_tri (self, t);
+                  SplitEncroachedSubsegments(self, self->theta, self->delta);
+                }
+            }
+
+          p2tr_vedge_set_free (E);
+          p2tr_point_unref (cPoint);
+          p2tr_triangle_unref (triContaining_c);
+      }
+
+      p2tr_vtriangle_unref (vt);
+
+      if (on_progress != NULL) on_progress ((P2trRefiner*) self, steps, max_steps);
+    }
+}
+
+static gboolean
+SplitPermitted (P2trDelaunayTerminator *self, P2trEdge *s, gdouble d)
+{
+  P2trCluster *startCluster = p2tr_cluster_get_for (P2TR_EDGE_START(s), s);
+  P2trCluster *endCluster =   p2tr_cluster_get_for (s->end, s);
+  P2trCluster *S_NOREF;
+  GList *iter;
+  
+  gboolean permitted = FALSE;
+  
+  if (! TolerantIsPowerOfTwoLength (p2tr_edge_get_length (s))
+      /* True when different, meaning both null or both exist */
+      || ((startCluster != NULL) ^ (endCluster == NULL)))
+    {
+      permitted = TRUE;
+    }
+  
+  if (! permitted)
+    {
+      S_NOREF = (startCluster != NULL) ? startCluster : endCluster;
+
+      for (iter = g_queue_peek_head_link (&S_NOREF->edges); iter != NULL; iter = iter->next)
+        if (TolerantIsShorter((P2trEdge*) iter->data, s)) /* e shorter than s */
+          {
+            permitted = TRUE;
+            break;
+          }
+    }
+  
+  if (! permitted)
+    {
+      gdouble rmin = p2tr_edge_get_length(s) * sin(S_NOREF->min_angle / 2);
+      if (rmin >= d)
+        permitted = TRUE;
+    }
+
+  if (startCluster) p2tr_cluster_free (startCluster);
+  if (endCluster) p2tr_cluster_free (endCluster);
+
+  return permitted;
+}
+
+static void
+SplitEncroachedSubsegments (P2trDelaunayTerminator *self, gdouble theta, P2trTriangleTooBig delta)
+{
+  while (! p2tr_dt_segment_queue_is_empty (self))
+  {
+    P2trEdge *s = p2tr_dt_dequeue_segment (self);
+    if (p2tr_hash_set_contains (self->cdt->mesh->edges, s))
+      {
+        P2trVector2 v;
+        P2trPoint *Pv;
+        GList *parts, *iter;
+
+        ChooseSplitVertex (s, &v);
+        Pv = p2tr_mesh_new_point (self->cdt->mesh, &v);
+        
+        /* Update here if using diametral lenses */
+
+        parts = p2tr_cdt_split_edge (self->cdt, s, Pv);
+        
+        NewVertex (self, Pv, theta, delta);
+
+        for (iter = parts; iter != NULL; iter = iter->next)
+          {
+            P2trEdge *e = (P2trEdge*)iter->data;
+            if (p2tr_cdt_is_encroached (e))
+              p2tr_dt_enqueue_segment (self, e);
+            p2tr_edge_unref (e);
+          }
+
+        g_list_free(parts);
+        p2tr_point_unref(Pv);
+      }
+    p2tr_edge_unref (s);
+  }
+}
+
+static void
+NewVertex (P2trDelaunayTerminator *self, P2trPoint *v, gdouble theta, P2trTriangleTooBig delta)
+{
+  GList *iter;
+  for (iter = v->outgoing_edges; iter != NULL; iter = iter->next)
+    {
+      P2trEdge *outEdge = (P2trEdge*) iter->data;
+      P2trTriangle *t = outEdge->tri;
+      P2trEdge *e;
+      
+      if (t == NULL)
+          continue;
+
+      e = p2tr_triangle_get_opposite_edge (t, v);
+      
+      /* we want the fast check and for new points we don't
+       * use that check... So let's go on the full check
+       * since it's still faster */
+      if (e->constrained && p2tr_cdt_is_encroached (e))
+        p2tr_dt_enqueue_segment (self, e);
+      else if (delta (t) || p2tr_triangle_smallest_non_constrained_angle (t) < theta)
+        p2tr_dt_enqueue_tri (self, t);
+
+      p2tr_edge_unref (e);
+    }
+}
+
+static gdouble
+ShortestEdgeLength (P2trTriangle *tri)
+{
+  gdouble a1 = p2tr_edge_get_length_squared (tri->edges[0]);
+  gdouble a2 = p2tr_edge_get_length_squared (tri->edges[1]);
+  gdouble a3 = p2tr_edge_get_length_squared (tri->edges[2]);
+  
+  return sqrt (MIN (a1, MIN (a2, a3)));
+}
+
+static gboolean
+TolerantIsShorter (P2trEdge *toTest, P2trEdge *reference)
+{
+  return p2tr_edge_get_length(toTest) < p2tr_edge_get_length(reference) * 1.01;
+}
+
+static inline gdouble
+LOG2 (gdouble v)
+{
+  return log10 (v) / G_LOG_2_BASE_10;
+}
+
+static gboolean
+TolerantIsPowerOfTwoLength (gdouble length)
+{
+  gdouble exp = LOG2 (length);
+  gdouble intpart, frac = modf (exp, &intpart);
+  gdouble distance;
+
+  /* If the length is a negative power of 2, the returned fraction will be
+   * negative */
+  frac = ABS(frac);
+
+  /* Find how close is exp to the closest integer */
+  distance = MIN(frac, 1-frac);
+
+  return distance < 0.05;
+}
+
+static void
+ChooseSplitVertex(P2trEdge *e, P2trVector2 *dst)
+{
+  gdouble sourceLength = p2tr_edge_get_length(e);
+  gdouble newLengthFloor = pow(2, floor(LOG2(sourceLength)));
+  gdouble newLengthCeil = newLengthFloor * 2;
+  gdouble newLength =
+      (sourceLength - newLengthFloor < newLengthCeil - sourceLength)
+      ? newLengthFloor : newLengthCeil;
+  gdouble ratio, resultLength;
+  
+  /* IMPORTANT! DIVIDE BY 2! */
+  newLength /= 2;
+
+  ratio = newLength / sourceLength;
+
+  dst->x = (1 - ratio) * P2TR_EDGE_START(e)->c.x + (ratio) * e->end->c.x;
+  dst->y = (1 - ratio) * P2TR_EDGE_START(e)->c.y + (ratio) * e->end->c.y;
+
+  /* now let's avoid consistency problems */
+  resultLength = sqrt(P2TR_VECTOR2_DISTANCE_SQ(&P2TR_EDGE_START(e)->c, dst));
+  
+  if (! TolerantIsPowerOfTwoLength(resultLength))
+    p2tr_exception_numeric ("Bad rounding!");
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/delaunay-terminator.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/delaunay-terminator.h
new file mode 100644
index 000000000..fe0ef5d50
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/delaunay-terminator.h
@@ -0,0 +1,71 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_DELAUNAY_TERMINATOR_H__
+#define __P2TC_REFINE_DELAUNAY_TERMINATOR_H__
+
+#include <glib.h>
+#include "cdt.h"
+#include "refiner.h"
+#include "vedge.h"
+
+typedef struct
+{
+  P2trCDT            *cdt;
+  GQueue              Qs;
+  GSequence          *Qt;
+  gdouble             theta;
+  P2trTriangleTooBig  delta;
+} P2trDelaunayTerminator;
+
+gboolean  p2tr_cdt_test_encroachment_ignore_visibility (const P2trVector2 *w,
+                                                        P2trEdge          *e);
+
+gboolean  p2tr_cdt_is_encroached_by (P2trCDT     *self,
+                                     P2trEdge    *e,
+                                     P2trVector2 *p);
+
+P2trVEdgeSet*  p2tr_cdt_get_segments_encroached_by (P2trCDT   *self,
+                                                    P2trPoint *v);
+
+gboolean      p2tr_cdt_is_encroached (P2trEdge *E);
+
+P2trDelaunayTerminator*
+p2tr_dt_new (gdouble theta, P2trTriangleTooBig delta, P2trCDT *cdt);
+
+void p2tr_dt_free (P2trDelaunayTerminator *self);
+
+void p2tr_dt_refine (P2trDelaunayTerminator   *self,
+                     gint                      max_steps,
+                     P2trRefineProgressNotify  on_progress);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/edge.c b/subprojects/poly2tri-c/poly2tri-c/refine/edge.c
new file mode 100644
index 000000000..a95a6eec3
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/edge.c
@@ -0,0 +1,247 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <glib.h>
+
+#include "point.h"
+#include "edge.h"
+#include "triangle.h"
+#include "mesh.h"
+
+static void
+p2tr_edge_init (P2trEdge  *self,
+                P2trPoint *start,
+                P2trPoint *end,
+                gboolean   constrained,
+                P2trEdge  *mirror)
+{
+  self->angle       = atan2 (end->c.y - start->c.y,
+                          end->c.x - start->c.x);
+  self->constrained = constrained;
+  self->delaunay    = FALSE;
+  self->end         = end;
+  self->mirror      = mirror;
+  self->refcount    = 0;
+  self->tri         = NULL;
+}
+
+P2trEdge*
+p2tr_edge_new (P2trPoint *start,
+               P2trPoint *end,
+               gboolean   constrained)
+{
+  P2trEdge *self   = g_slice_new (P2trEdge);
+  P2trEdge *mirror = g_slice_new (P2trEdge);
+
+  p2tr_edge_init (self, start, end, constrained, mirror);
+  p2tr_edge_init (mirror, end, start, constrained, self);
+
+  p2tr_point_ref (start);
+  p2tr_point_ref (end);
+  
+  _p2tr_point_insert_edge (start, self);
+  _p2tr_point_insert_edge (end,   mirror);
+
+  return p2tr_edge_ref (self);
+}
+
+P2trEdge*
+p2tr_edge_ref (P2trEdge *self)
+{
+  ++self->refcount;
+  return self;
+}
+
+void
+p2tr_edge_unref (P2trEdge *self)
+{
+  g_assert (self->refcount > 0);
+  if (--self->refcount == 0 && self->mirror->refcount == 0)
+    p2tr_edge_free (self);
+}
+
+gboolean
+p2tr_edge_is_removed (P2trEdge *self)
+{
+  return self->end == NULL; /* This is only true if the edge was removed */
+}
+
+void
+p2tr_edge_remove (P2trEdge *self)
+{
+  P2trMesh *mesh;
+  P2trPoint *start, *end;
+
+  if (p2tr_edge_is_removed (self))
+    return;
+
+  mesh = p2tr_edge_get_mesh (self);
+
+  start = P2TR_EDGE_START(self);
+  end = self->end;
+
+  if (self->tri != NULL)
+    p2tr_triangle_remove (self->tri);
+  if (self->mirror->tri != NULL)
+    p2tr_triangle_remove (self->mirror->tri);
+
+  if (mesh != NULL)
+    {
+      p2tr_mesh_on_edge_removed (mesh, self);
+      p2tr_mesh_unref (mesh); /* The get function reffed it */
+    }
+
+  /* Warning - the code here is not that trivial!
+   * Assuming we would now want to remove `self' and `self->mirror' from
+   * `start' and `end'. If both have exactly one reference, then after
+   * removing the second, the edge struct will be freed before we can
+   * mark it as removed by setting the end points to be NULL! (Marking
+   * it as removed in that case, will be an access to unallocated
+   * memory).
+   * To solve this, we will hold a "ghost" reference to `self' to
+   * prevent freeing from happening until we are done modifying the
+   * struct.
+   */
+   p2tr_edge_ref (self);
+
+  _p2tr_point_remove_edge(start, self);
+  _p2tr_point_remove_edge(end, self->mirror);
+
+  self->end = NULL;
+  self->mirror->end = NULL;
+
+  /* Now release the "ghost" reference */
+  p2tr_edge_unref (self);
+
+  p2tr_point_unref (start);
+  p2tr_point_unref (end);
+}
+
+void
+p2tr_edge_free (P2trEdge *self)
+{
+  g_assert (p2tr_edge_is_removed (self));
+  g_slice_free (P2trEdge, self->mirror);
+  g_slice_free (P2trEdge, self);
+}
+
+void
+p2tr_edge_get_diametral_circle (P2trEdge   *self,
+                                P2trCircle *circle)
+{
+  P2trVector2 radius;
+  
+  p2tr_vector2_center (&self->end->c, &P2TR_EDGE_START(self)->c, &circle->center);
+  p2tr_vector2_sub (&self->end->c, &circle->center, &radius);
+
+  circle->radius = p2tr_vector2_norm (&radius);
+}
+
+P2trMesh*
+p2tr_edge_get_mesh (P2trEdge *self)
+{
+  if (self->end != NULL)
+    return p2tr_point_get_mesh (self->end);
+  else
+    return NULL;
+}
+
+gdouble
+p2tr_edge_get_length (P2trEdge* self)
+{
+  return sqrt (p2tr_math_length_sq2 (&self->end->c, &P2TR_EDGE_START(self)->c));
+}
+
+gdouble
+p2tr_edge_get_length_squared (P2trEdge* self)
+{
+  return p2tr_math_length_sq2 (&self->end->c, &P2TR_EDGE_START(self)->c);
+}
+
+gdouble
+p2tr_edge_angle_between (P2trEdge *e1,
+                         P2trEdge *e2)
+{
+  /* A = E1.angle, a = abs (A)
+   * B = E1.angle, b = abs (B)
+   *
+   * W is the angle we wish to find. Note the fact that we want
+   * to find the angle so that the edges go CLOCKWISE around it.
+   *
+   * Case 1: Signs of A and B agree | Case 2: Signs of A and B disagree
+   *         and A > 0              |         and A > 0
+   *                                |
+   * a = A, b = B                   | a = A, b = -B
+   *             ^^                 |
+   *         E2 //                  |           /
+   *           //\                  |          /
+   *          //b|                  |         /a
+   * - - - - * - |W- - - - - - - -  | - - - - * - - - - 
+   *         ^^a'|                  |       ^^ \\b
+   *         ||_/                   |      // W \\
+   *      E1 ||\                    |  E1 // \_/ \\ E2
+   *        '||a\                   |    //       \\
+   *     - - - - - -                |   //         vv
+   *                                |
+   * W = A' + B = (180 - A) + B     | W = 180 - (a + b) = 180 - (A - B) 
+   * W = 180 - A + B                | W = 180 - A + B
+   * 
+   * By the illustration above, we can see that in general the angle W
+   * can be computed by W = 180 - A + B in every case. The only thing to
+   * note is that the range of the result of the computation is
+   * [180 - 360, 180 + 360] = [-180, +540] so we may need to subtract
+   * 360 to put it back in the range [-180, +180].
+   */
+  gdouble result;
+  
+  if (e1->end != P2TR_EDGE_START(e2))
+    p2tr_exception_programmatic ("The end-point of the first edge isn't"
+        " the end-point of the second edge!");
+
+  result = G_PI - e1->angle + e2->angle;
+  if (result > 2 * G_PI)
+      result -= 2 * G_PI;
+
+  return result;
+}
+
+gdouble
+p2tr_edge_angle_between_positive (P2trEdge *e1,
+                                  P2trEdge *e2)
+{
+  gdouble result = p2tr_edge_angle_between (e1, e2);
+  if (result < 0)
+    return result + 2 * G_PI;
+  else
+    return result;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/edge.h b/subprojects/poly2tri-c/poly2tri-c/refine/edge.h
new file mode 100644
index 000000000..9b56d4b58
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/edge.h
@@ -0,0 +1,107 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_EDGE_H__
+#define __P2TC_REFINE_EDGE_H__
+
+#include <glib.h>
+#include "circle.h"
+#include "triangulation.h"
+
+/**
+ * @struct P2trEdge_
+ * A struct for an edge in a triangular mesh
+ */
+struct P2trEdge_
+{
+  /** The end point of this mesh */
+  P2trPoint    *end;
+
+  /** The edge going in the opposite direction from this edge */
+  P2trEdge     *mirror;
+  
+  /** Is this a constrained edge? */
+  gboolean      constrained;
+  
+  /** The triangle where this edge goes clockwise along its outline */
+  P2trTriangle *tri;
+
+  /**
+   * The angle of the direction of this edge. Although it can be
+   * computed anytime using atan2 on the vector of this edge, we cache
+   * it here since it's heavily used and the computation is expensive.
+   * The angle increases as we go CCW, and it's in the range [-PI,+PI]
+   */
+  gdouble       angle;
+  
+  /**
+   * Is this edge a delaunay edge? This field is used by the refinement
+   * algorithm and should not be used elsewhere!
+   */
+  gboolean      delaunay;
+
+  /** A count of references to the edge */
+  guint         refcount;
+};
+
+#define P2TR_EDGE_START(E) ((E)->mirror->end)
+
+P2trEdge*   p2tr_edge_new                  (P2trPoint *start,
+                                            P2trPoint *end,
+                                            gboolean   constrained);
+
+P2trEdge*   p2tr_edge_ref                  (P2trEdge *self);
+
+void        p2tr_edge_unref                (P2trEdge *self);
+
+void        p2tr_edge_free                 (P2trEdge *self);
+
+void        p2tr_edge_remove               (P2trEdge *self);
+
+void        p2tr_edge_get_diametral_circle (P2trEdge   *self,
+                                            P2trCircle *circle);
+
+P2trMesh*   p2tr_edge_get_mesh             (P2trEdge *self);
+
+gboolean    p2tr_edge_is_removed           (P2trEdge *self);
+
+gdouble     p2tr_edge_get_length           (P2trEdge* self);
+
+gdouble     p2tr_edge_get_length_squared   (P2trEdge* self);
+
+gdouble     p2tr_edge_angle_between        (P2trEdge *e1,
+                                            P2trEdge *e2);
+
+gdouble     p2tr_edge_angle_between_positive (P2trEdge *e1,
+                                              P2trEdge *e2);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/line.c b/subprojects/poly2tri-c/poly2tri-c/refine/line.c
new file mode 100644
index 000000000..d2e0ccf3b
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/line.c
@@ -0,0 +1,102 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include "line.h"
+
+void
+p2tr_line_init (P2trLine    *line,
+                gdouble      a,
+                gdouble      b,
+                gdouble      c)
+{
+  line->a = a;
+  line->b = b;
+  line->c = c;
+}
+
+gboolean
+p2tr_line_different_sides (const P2trLine    *line,
+                           const P2trVector2 *pt1,
+                           const P2trVector2 *pt2)
+{
+  gdouble side1 = line->a * pt1->x + line->b * pt1->y + line->c;
+  gdouble side2 = line->a * pt2->x + line->b * pt2->y + line->c;
+
+  /* Signs are different if the product is negative */
+  return side1 * side2 < 0;
+}
+
+P2trLineRelation
+p2tr_line_intersection (const P2trLine    *l1,
+                        const P2trLine    *l2,
+                        P2trVector2       *out_intersection)
+{
+  /* In order to find the intersection, we intend to solve
+   * the following set of equations:
+   *
+   *   ( A1 B1 ) ( x ) = ( -C1 )
+   *   ( A2 B2 ) ( y ) = ( -C2 )
+   *
+   * We can simplify the solution using Cramers Rule which
+   * gives the following results:
+   *
+   *   x = (-C1 * B2) - (-C2 * B1) / (A1 * B2 - A2 * B1)
+   *   y = (A1 * -C2) - (A2 * -C1) / (A1 * B2 - A2 * B1)
+   */
+  double d = l1->a * l2->b - l2->a * l1->b;
+
+  /* If the denominator in the result of applying Cramers rule
+   * is zero, then the lines have exactly the same slope, meaning
+   * they are either exactly the same or they are parallel and
+   * never intersect */
+  if (d == 0)
+    {
+      /* We want to check if the offsets of boths the lines are the
+       * same, i.e. whether:  C1 / A1 = C2 / A2
+       * This test can be done without zero division errors if we do
+       * it in like this:     C1 * A2 = C2 * A1
+       */
+      if (l1->c * l2->a == l1->a * l2->c)
+        return P2TR_LINE_RELATION_SAME;
+      else
+        return P2TR_LINE_RELATION_PARALLEL;
+    }
+
+  if (out_intersection != NULL)
+    {
+      out_intersection->x = (-l1->c * l2->b + l2->c * l1->b) / d;
+      out_intersection->y = (l1->a * -l2->c + l2->a * l1->c) / d;
+    }
+
+  return P2TR_LINE_RELATION_INTERSECTING;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/line.h b/subprojects/poly2tri-c/poly2tri-c/refine/line.h
new file mode 100644
index 000000000..a72813b0d
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/line.h
@@ -0,0 +1,66 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_LINE_H__
+#define __P2TC_REFINE_LINE_H__
+
+#include <glib.h>
+#include "vector2.h"
+
+/* A line is the equation of the following form:
+ *   a * X + b * Y + c = 0
+ */
+typedef struct {
+  gdouble a, b, c;
+} P2trLine;
+
+typedef enum
+{
+  P2TR_LINE_RELATION_INTERSECTING = 0,
+  P2TR_LINE_RELATION_PARALLEL = 1,
+  P2TR_LINE_RELATION_SAME = 2
+} P2trLineRelation;
+
+void              p2tr_line_init            (P2trLine    *line,
+                                             gdouble      a,
+                                             gdouble      b,
+                                             gdouble      c);
+
+gboolean          p2tr_line_different_sides (const P2trLine    *line,
+                                             const P2trVector2 *pt1,
+                                             const P2trVector2 *pt2);
+
+P2trLineRelation  p2tr_line_intersection    (const P2trLine    *l1,
+                                             const P2trLine    *l2,
+                                             P2trVector2       *out_intersection);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/mesh-action.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/mesh-action.c
new file mode 100644
index 000000000..c2ffd9cda
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/mesh-action.c
@@ -0,0 +1,228 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include "point.h"
+#include "edge.h"
+#include "mesh.h"
+#include "vedge.h"
+#include "vtriangle.h"
+#include "mesh-action.h"
+
+static P2trMeshAction*
+p2tr_mesh_action_point (P2trPoint *point,
+                        gboolean   added)
+{
+  P2trMeshAction *self = g_slice_new (P2trMeshAction);
+  self->type = P2TR_MESH_ACTION_POINT;
+  self->added = added;
+  self->refcount = 1;
+  self->action.action_point.point = p2tr_point_ref (point);
+  return self;
+}
+
+static void
+p2tr_mesh_action_point_free (P2trMeshAction *self)
+{
+  g_assert (self->type == P2TR_MESH_ACTION_POINT);
+  p2tr_point_unref (self->action.action_point.point);
+  g_slice_free (P2trMeshAction, self);
+}
+
+static void
+p2tr_mesh_action_point_undo (P2trMeshAction *self,
+                             P2trMesh       *mesh)
+{
+  g_assert (self->type == P2TR_MESH_ACTION_POINT);
+  if (self->added)
+    p2tr_point_remove (self->action.action_point.point);
+  else
+    p2tr_mesh_add_point (mesh, self->action.action_point.point);
+}
+
+P2trMeshAction*
+p2tr_mesh_action_new_point (P2trPoint *point)
+{
+  return p2tr_mesh_action_point (point, TRUE);
+}
+
+P2trMeshAction*
+p2tr_mesh_action_del_point (P2trPoint *point)
+{
+  return p2tr_mesh_action_point (point, FALSE);
+}
+
+static P2trMeshAction*
+p2tr_mesh_action_edge (P2trEdge *edge,
+                       gboolean  added)
+{
+  P2trMeshAction *self = g_slice_new (P2trMeshAction);
+  self->type = P2TR_MESH_ACTION_EDGE;
+  self->added = added;
+  self->refcount = 1;
+  self->action.action_edge.vedge = p2tr_vedge_new2 (edge);
+  self->action.action_edge.constrained = edge->constrained;
+  return self;
+}
+
+static void
+p2tr_mesh_action_edge_free (P2trMeshAction *self)
+{
+  g_assert (self->type == P2TR_MESH_ACTION_EDGE);
+  p2tr_vedge_unref (self->action.action_edge.vedge);
+  g_slice_free (P2trMeshAction, self);
+}
+
+static void
+p2tr_mesh_action_edge_undo (P2trMeshAction *self,
+                            P2trMesh       *mesh)
+{
+  g_assert (self->type == P2TR_MESH_ACTION_EDGE);
+
+  if (self->added)
+    p2tr_vedge_remove (self->action.action_edge.vedge);
+  else
+    p2tr_vedge_create (self->action.action_edge.vedge);
+}
+
+P2trMeshAction*
+p2tr_mesh_action_new_edge (P2trEdge *edge)
+{
+  return p2tr_mesh_action_edge (edge, TRUE);
+}
+
+P2trMeshAction*
+p2tr_mesh_action_del_edge (P2trEdge *edge)
+{
+  return p2tr_mesh_action_edge (edge, FALSE);
+}
+
+static P2trMeshAction*
+p2tr_mesh_action_triangle (P2trTriangle *tri,
+                           gboolean      added)
+{
+  P2trMeshAction *self = g_slice_new (P2trMeshAction);
+  self->type = P2TR_MESH_ACTION_TRIANGLE;
+  self->added = added;
+  self->refcount = 1;
+  self->action.action_tri.vtri = p2tr_vtriangle_new (tri);
+  return self;
+}
+
+static void
+p2tr_mesh_action_triangle_free (P2trMeshAction *self)
+{
+  g_assert (self->type == P2TR_MESH_ACTION_TRIANGLE);
+  p2tr_vtriangle_unref (self->action.action_tri.vtri);
+  g_slice_free (P2trMeshAction, self);
+}
+
+static void
+p2tr_mesh_action_triangle_undo (P2trMeshAction *self,
+                                P2trMesh       *mesh)
+{
+  g_assert (self->type == P2TR_MESH_ACTION_TRIANGLE);
+
+  if (self->added)
+    p2tr_vtriangle_remove (self->action.action_tri.vtri);
+  else
+    p2tr_vtriangle_create (self->action.action_tri.vtri);
+}
+
+P2trMeshAction*
+p2tr_mesh_action_new_triangle (P2trTriangle *tri)
+{
+  return p2tr_mesh_action_triangle (tri, TRUE);
+}
+
+P2trMeshAction*
+p2tr_mesh_action_del_triangle (P2trTriangle *tri)
+{
+  return p2tr_mesh_action_triangle (tri, FALSE);
+}
+
+P2trMeshAction*
+p2tr_mesh_action_ref (P2trMeshAction *self)
+{
+  ++self->refcount;
+  return self;
+}
+
+void
+p2tr_mesh_action_free (P2trMeshAction *self)
+{
+  switch (self->type)
+    {
+      case P2TR_MESH_ACTION_POINT:
+        p2tr_mesh_action_point_free (self);
+        break;
+      case P2TR_MESH_ACTION_EDGE:
+        p2tr_mesh_action_edge_free (self);
+        break;
+      case P2TR_MESH_ACTION_TRIANGLE:
+        p2tr_mesh_action_triangle_free (self);
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+}
+
+void
+p2tr_mesh_action_unref (P2trMeshAction *self)
+{
+  g_assert (self->refcount > 0);
+  if (--self->refcount == 0)
+    p2tr_mesh_action_free (self);
+}
+
+void
+p2tr_mesh_action_undo (P2trMeshAction *self,
+                       P2trMesh       *mesh)
+{
+  switch (self->type)
+    {
+      case P2TR_MESH_ACTION_POINT:
+        p2tr_mesh_action_point_undo (self, mesh);
+        break;
+      case P2TR_MESH_ACTION_EDGE:
+        p2tr_mesh_action_edge_undo (self, mesh);
+        break;
+      case P2TR_MESH_ACTION_TRIANGLE:
+        p2tr_mesh_action_triangle_undo (self, mesh);
+        break;
+      default:
+        g_assert_not_reached ();
+        break;
+    }
+}
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/mesh-action.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/mesh-action.h
new file mode 100644
index 000000000..cd3e840a5
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/mesh-action.h
@@ -0,0 +1,170 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_MESH_ACTION_H__
+#define __P2TC_REFINE_MESH_ACTION_H__
+
+#include <glib.h>
+
+/**
+ * \defgroup P2trMeshAction P2trMeshAction - Mesh Action Recording
+ * Action recording state objects for mesh objects. These may be
+ * inspected by code outside of the library, but objects of this type
+ * should not be created or manipulated by external code!
+ * @{
+ */
+
+/**
+ * The type of the geometric primitive affected by a mesh action
+ */
+typedef enum
+{
+  P2TR_MESH_ACTION_POINT,
+  P2TR_MESH_ACTION_EDGE,
+  P2TR_MESH_ACTION_TRIANGLE
+} P2trMeshActionType;
+
+/**
+ * A struct representing any single action on a mesh. A single atomic
+ * action may be the insertion/removal of a point/edge/triangle.
+ *
+ * Note that such an action only treats the direct geometric operation
+ * related to the specific geometric primitve, without any of its
+ * dependencies.
+ *
+ * For example, if removing a point requires the removal of several
+ * edges and triangles, then the removal of each one of those should be
+ * recorded in its own action object.
+ */
+typedef struct P2trMeshAction_
+{
+  /** The type of geometric primitive affected by the action */
+  P2trMeshActionType  type;
+  /** A flag specifying whether the primitive was added or removed */
+  gboolean            added;
+  /** A reference count to the action object */
+  gint                refcount;
+  /** Specific additional information which is needed for each type
+   *  of action */
+  union {
+    /** Information required to undo a point action */
+    struct {
+      /** The point that was added/deleted */
+      P2trPoint *point;
+    } action_point;
+
+    /** Information required to undo an edge action */
+    struct {
+      /** A virtual edge representing the added/deleted edge */
+      P2trVEdge *vedge;
+      /** A flag specifying whether the edge is constrained */
+      gboolean   constrained;
+    } action_edge;
+
+    /** Information required to undo a triangle action */
+    struct {
+      /** A virtual triangle representing the added/deleted triangle */
+      P2trVTriangle *vtri;
+    } action_tri;
+  } action;
+} P2trMeshAction;
+
+/**
+ * Create a new mesh action describing the addition of a new point
+ * @param point The point that is added to the mesh
+ * @return An object representing the point addition action
+ */
+P2trMeshAction*  p2tr_mesh_action_new_point      (P2trPoint      *point);
+
+/**
+ * Create a new mesh action describing the deletion of an existing point
+ * @param point The point that is deleted from the mesh
+ * @return An object representing the point deletion action
+ */
+P2trMeshAction*  p2tr_mesh_action_del_point      (P2trPoint      *point);
+
+/**
+ * Create a new mesh action describing the addition of a new edge
+ * @param edge The edge that is added to the mesh
+ * @return An object representing the edge addition action
+ */
+P2trMeshAction*  p2tr_mesh_action_new_edge       (P2trEdge       *edge);
+
+/**
+ * Create a new mesh action describing the deletion of an existing edge
+ * @param edge The edge that is deleted from the mesh
+ * @return An object representing the edge deletion action
+ */
+P2trMeshAction*  p2tr_mesh_action_del_edge       (P2trEdge       *edge);
+
+/**
+ * Create a new mesh action describing the addition of a triangle
+ * @param tri The triangle that is added to the mesh
+ * @return An object representing the triangle addition action
+ */
+P2trMeshAction*  p2tr_mesh_action_new_triangle   (P2trTriangle   *tri);
+
+/**
+ * Create a new mesh action describing the deletion of an existing
+ * triangle
+ * @param tri The triangle that is deleted from the mesh
+ * @return An object representing the triangle deletion action
+ */
+P2trMeshAction*  p2tr_mesh_action_del_triangle   (P2trTriangle   *tri);
+
+/**
+ * Increase the reference count to this mesh action by 1
+ * @param self The mesh action whose reference count should be increased
+ */
+P2trMeshAction*  p2tr_mesh_action_ref            (P2trMeshAction *self);
+
+/**
+ * Decrease the reference count to this mesh action by 1
+ * @param self The mesh action whose reference count should be decreased
+ */
+void             p2tr_mesh_action_unref          (P2trMeshAction *self);
+
+/**
+ * Free the memory used by a mesh action data structure
+ * @param self The mesh action whose memory should be freed
+ */
+void             p2tr_mesh_action_free           (P2trMeshAction *self);
+
+/**
+ * Undo the action described by the given mesh action object
+ * @param self The mesh action to undo
+ * @param mesh The mesh on which this action was applied
+ */
+void             p2tr_mesh_action_undo           (P2trMeshAction *self,
+                                                  P2trMesh       *mesh);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/mesh.c b/subprojects/poly2tri-c/poly2tri-c/refine/mesh.c
new file mode 100644
index 000000000..0b5ca50da
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/mesh.c
@@ -0,0 +1,408 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include "utils.h"
+
+#include "mesh.h"
+#include "point.h"
+#include "edge.h"
+#include "triangle.h"
+#include "mesh-action.h"
+
+P2trMesh*
+p2tr_mesh_new (void)
+{
+  P2trMesh *mesh = g_slice_new (P2trMesh);
+
+  mesh->refcount = 1;
+  mesh->edges = p2tr_hash_set_new_default ();
+  mesh->points = p2tr_hash_set_new_default ();
+  mesh->triangles = p2tr_hash_set_new_default ();
+
+  mesh->record_undo = FALSE;
+  g_queue_init (&mesh->undo);
+
+  return mesh;
+}
+
+P2trPoint*
+p2tr_mesh_add_point (P2trMesh  *self,
+                     P2trPoint *point)
+{
+  g_assert (point->mesh == NULL);
+  point->mesh = self;
+  p2tr_mesh_ref (self);
+  p2tr_hash_set_insert (self->points, point);
+
+  if (self->record_undo)
+    g_queue_push_tail (&self->undo, p2tr_mesh_action_new_point (point));
+
+  return p2tr_point_ref (point);
+}
+
+P2trPoint*
+p2tr_mesh_new_point (P2trMesh          *self,
+                     const P2trVector2 *c)
+{
+  return p2tr_mesh_new_point2 (self, c->x, c->y);
+}
+
+P2trPoint*
+p2tr_mesh_new_point2 (P2trMesh *self,
+                      gdouble   x,
+                      gdouble   y)
+{
+  return p2tr_mesh_add_point (self, p2tr_point_new2 (x, y));
+}
+
+P2trEdge*
+p2tr_mesh_add_edge (P2trMesh *self,
+                    P2trEdge *edge)
+{
+  p2tr_hash_set_insert (self->edges, p2tr_edge_ref (edge->mirror));
+  p2tr_hash_set_insert (self->edges, p2tr_edge_ref (edge));
+
+  if (self->record_undo)
+    g_queue_push_tail (&self->undo, p2tr_mesh_action_new_edge (edge));
+
+  return edge;
+}
+
+P2trEdge*
+p2tr_mesh_new_edge (P2trMesh  *self,
+                    P2trPoint *start,
+                    P2trPoint *end,
+                    gboolean   constrained)
+{
+  return p2tr_mesh_add_edge (self, p2tr_edge_new (start, end, constrained));
+}
+
+P2trEdge*
+p2tr_mesh_new_or_existing_edge (P2trMesh  *self,
+                                P2trPoint *start,
+                                P2trPoint *end,
+                                gboolean   constrained)
+{
+  P2trEdge *result = p2tr_point_has_edge_to (start, end);
+  if (result)
+    p2tr_edge_ref (result);
+  else
+    result = p2tr_mesh_new_edge (self, start, end, constrained);
+  return result;
+}
+
+P2trTriangle*
+p2tr_mesh_add_triangle (P2trMesh     *self,
+                        P2trTriangle *tri)
+{
+  p2tr_hash_set_insert (self->triangles, tri);
+
+  if (self->record_undo)
+    g_queue_push_tail (&self->undo, p2tr_mesh_action_new_triangle (tri));
+
+  return p2tr_triangle_ref (tri);
+}
+
+P2trTriangle*
+p2tr_mesh_new_triangle (P2trMesh *self,
+                        P2trEdge *AB,
+                        P2trEdge *BC,
+                        P2trEdge *CA)
+{
+  return p2tr_mesh_add_triangle (self, p2tr_triangle_new (AB, BC, CA));
+}
+
+void
+p2tr_mesh_on_point_removed (P2trMesh  *self,
+                            P2trPoint *point)
+{
+  if (self != point->mesh)
+    p2tr_exception_programmatic ("Point does not belong to this mesh!");
+
+  point->mesh = NULL;
+  p2tr_mesh_unref (self);
+
+  p2tr_hash_set_remove (self->points, point);
+
+  if (self->record_undo)
+    g_queue_push_tail (&self->undo, p2tr_mesh_action_del_point (point));
+
+  p2tr_point_unref (point);
+}
+
+void
+p2tr_mesh_on_edge_removed (P2trMesh *self,
+                           P2trEdge *edge)
+{
+  p2tr_hash_set_remove (self->edges, edge->mirror);
+  p2tr_edge_unref (edge->mirror);
+  p2tr_hash_set_remove (self->edges, edge);
+
+  if (self->record_undo)
+    g_queue_push_tail (&self->undo, p2tr_mesh_action_del_edge (edge));
+
+  p2tr_edge_unref (edge);
+}
+
+void
+p2tr_mesh_on_triangle_removed (P2trMesh     *self,
+                               P2trTriangle *triangle)
+{
+  p2tr_hash_set_remove (self->triangles, triangle);
+
+  if (self->record_undo)
+    g_queue_push_tail (&self->undo, p2tr_mesh_action_del_triangle (triangle));
+
+  p2tr_triangle_unref (triangle);
+}
+
+void
+p2tr_mesh_action_group_begin (P2trMesh *self)
+{
+  g_assert (! self->record_undo);
+  self->record_undo = TRUE;
+}
+
+void
+p2tr_mesh_action_group_commit (P2trMesh *self)
+{
+  GList *iter;
+
+  g_assert (self->record_undo);
+
+  self->record_undo = FALSE;
+
+  for (iter = self->undo.head; iter != NULL; iter = iter->next)
+    p2tr_mesh_action_unref ((P2trMeshAction*)iter->data);
+  g_queue_clear (&self->undo);
+}
+
+void
+p2tr_mesh_action_group_undo (P2trMesh *self)
+{
+  GList *iter;
+
+  g_assert (self->record_undo);
+
+  /* Set the record_undo flag to FALSE before p2tr_mesh_action_undo, so that
+   * we don't create zombie objects therein. */
+  self->record_undo = FALSE;
+
+  for (iter = self->undo.tail; iter != NULL; iter = iter->prev)
+    {
+      p2tr_mesh_action_undo ((P2trMeshAction*)iter->data, self);
+      p2tr_mesh_action_unref ((P2trMeshAction*)iter->data);
+    }
+  g_queue_clear (&self->undo);
+}
+
+void
+p2tr_mesh_clear (P2trMesh *self)
+{
+  P2trHashSetIter iter;
+  gpointer temp;
+
+  /* While iterating over the sets of points/edges/triangles to remove
+   * all the mesh elements, the sets will be modified by the removal
+   * operation itself. Therefore we can't use a regular iterator -
+   * instead we must look always at the first place */
+  p2tr_hash_set_iter_init (&iter, self->triangles);
+  while (p2tr_hash_set_iter_next (&iter, &temp))
+    {
+      p2tr_triangle_remove ((P2trTriangle*)temp);
+      p2tr_hash_set_iter_init (&iter, self->triangles);
+    }
+
+  p2tr_hash_set_iter_init (&iter, self->edges);
+  while (p2tr_hash_set_iter_next (&iter, &temp))
+    {
+      g_assert (((P2trEdge*)temp)->tri == NULL);
+      p2tr_edge_remove ((P2trEdge*)temp);
+      p2tr_hash_set_iter_init (&iter, self->edges);
+    }
+
+  p2tr_hash_set_iter_init (&iter, self->points);
+  while (p2tr_hash_set_iter_next (&iter, &temp))
+    {
+      g_assert (((P2trPoint*)temp)->outgoing_edges == NULL);
+      p2tr_point_remove ((P2trPoint*)temp);
+      p2tr_hash_set_iter_init (&iter, self->points);
+    }
+}
+
+void
+p2tr_mesh_free (P2trMesh *self)
+{
+  if (self->record_undo)
+    p2tr_mesh_action_group_commit (self);
+
+  p2tr_mesh_clear (self);
+
+  p2tr_hash_set_free (self->points);
+  p2tr_hash_set_free (self->edges);
+  p2tr_hash_set_free (self->triangles);
+
+  g_slice_free (P2trMesh, self);
+}
+
+void
+p2tr_mesh_unref (P2trMesh *self)
+{
+  g_assert (self->refcount > 0);
+  if (--self->refcount == 0)
+    p2tr_mesh_free (self);
+}
+
+P2trMesh*
+p2tr_mesh_ref (P2trMesh *self)
+{
+  ++self->refcount;
+  return self;
+}
+
+P2trTriangle*
+p2tr_mesh_find_point (P2trMesh *self,
+                      const P2trVector2 *pt)
+{
+  gdouble u, v;
+  return p2tr_mesh_find_point2 (self, pt, &u, &v);
+}
+
+P2trTriangle*
+p2tr_mesh_find_point2 (P2trMesh          *self,
+                       const P2trVector2 *pt,
+                       gdouble           *u,
+                       gdouble           *v)
+{
+  P2trHashSetIter iter;
+  P2trTriangle *result;
+  
+  p2tr_hash_set_iter_init (&iter, self->triangles);
+  while (p2tr_hash_set_iter_next (&iter, (gpointer*)&result))
+    if (p2tr_triangle_contains_point2 (result, pt, u, v) != P2TR_INTRIANGLE_OUT)
+      return p2tr_triangle_ref (result);
+
+  return NULL;
+}
+
+P2trTriangle*
+p2tr_mesh_find_point_local (P2trMesh          *self,
+                            const P2trVector2 *pt,
+                            P2trTriangle      *initial_guess)
+{
+  gdouble u, v;
+  return p2tr_mesh_find_point_local2 (self, pt, initial_guess, &u, &v);
+}
+
+P2trTriangle*
+p2tr_mesh_find_point_local2 (P2trMesh          *self,
+                             const P2trVector2 *pt,
+                             P2trTriangle      *initial_guess,
+                             gdouble           *u,
+                             gdouble           *v)
+{
+  P2trHashSet *checked_tris;
+  GQueue to_check;
+  P2trTriangle *result = NULL;
+
+  if (initial_guess == NULL)
+    return p2tr_mesh_find_point2(self, pt, u, v);
+
+  checked_tris = p2tr_hash_set_new_default ();
+  g_queue_init (&to_check);
+  g_queue_push_head (&to_check, initial_guess);
+
+  while (! g_queue_is_empty (&to_check))
+    {
+      P2trTriangle *tri = (P2trTriangle*) g_queue_pop_head (&to_check);
+      
+      p2tr_hash_set_insert (checked_tris, tri);
+      if (p2tr_triangle_contains_point2 (tri, pt, u, v) != P2TR_INTRIANGLE_OUT)
+        {
+          result = tri;
+          break;
+        }
+      else
+        {
+          gint i;
+          for (i = 0; i < 3; i++)
+            {
+              P2trTriangle *new_to_check = tri->edges[i]->mirror->tri;
+              if (new_to_check != NULL
+                  && ! p2tr_hash_set_contains (checked_tris, new_to_check))
+                {
+                  p2tr_hash_set_insert (checked_tris, new_to_check);
+                  g_queue_push_tail (&to_check, new_to_check);
+                }
+            }
+        }
+    }
+
+  p2tr_hash_set_free (checked_tris);
+  g_queue_clear (&to_check);
+
+  if (result != NULL)
+    p2tr_triangle_ref (result);
+
+  return result;
+}
+
+void
+p2tr_mesh_get_bounds (P2trMesh    *self,
+                      gdouble     *min_x,
+                      gdouble     *min_y,
+                      gdouble     *max_x,
+                      gdouble     *max_y)
+{
+  gdouble lmin_x = + G_MAXDOUBLE, lmin_y = + G_MAXDOUBLE;
+  gdouble lmax_x = - G_MAXDOUBLE, lmax_y = - G_MAXDOUBLE;
+
+  P2trHashSetIter iter;
+  P2trPoint *pt;
+
+  p2tr_hash_set_iter_init (&iter, self->points);
+  while (p2tr_hash_set_iter_next (&iter, (gpointer*) &pt))
+    {
+      gdouble x = pt->c.x;
+      gdouble y = pt->c.y;
+
+      lmin_x = MIN (lmin_x, x);
+      lmin_y = MIN (lmin_y, y);
+      lmax_x = MAX (lmax_x, x);
+      lmax_y = MAX (lmax_y, y);
+    }
+  *min_x = lmin_x;
+  *min_y = lmin_y;
+  *max_x = lmax_x;
+  *max_y = lmax_y;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/mesh.h b/subprojects/poly2tri-c/poly2tri-c/refine/mesh.h
new file mode 100644
index 000000000..c814df458
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/mesh.h
@@ -0,0 +1,361 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_MESH_H__
+#define __P2TC_REFINE_MESH_H__
+
+#include <glib.h>
+#include "vector2.h"
+#include "utils.h"
+#include "triangulation.h"
+
+/**
+ * \defgroup P2trMesh P2trMesh - Triangular Meshes
+ * The library is designed to handle triangular meshes which are made
+ * of one continuous region, potentially with holes
+ * @{
+ */
+
+/**
+ * A struct for representing a triangular mesh
+ */
+struct P2trMesh_
+{
+  /**
+   * A hash set containing pointers to all the triangles
+   * (\ref P2trTriangle) in the mesh
+   */
+  P2trHashSet *triangles;
+
+  /**
+   * A hash set containing pointers to all the edges (\ref P2trEdge) in
+   * the mesh
+   */
+  P2trHashSet *edges;
+
+  /**
+   * A hash set containing pointers to all the points (\ref P2trPoint)
+   * in the mesh
+   */
+  P2trHashSet *points;
+
+  /**
+   * A boolean flag specifying whether recording of actions on the
+   * mesh (for allowing to undo them) is taking place right now
+   */
+  gboolean     record_undo;
+
+  /**
+   * A queue stroing all the actions done on the mesh since the begining
+   * of the recording
+   */
+  GQueue       undo;
+
+  /**
+   * Counts the amount of references to the mesh. When this counter
+   * reaches zero, the mesh will be freed
+   */
+  guint        refcount;
+};
+
+/**
+ * Create a new empty mesh
+ * @return The newly created mesh
+ */
+P2trMesh*     p2tr_mesh_new             (void);
+
+/**
+ * Add an existing point to the given mesh
+ * @param self The mesh to add the point to
+ * @param point The point to add
+ * @return The given point
+ */
+P2trPoint*    p2tr_mesh_add_point       (P2trMesh  *self,
+                                         P2trPoint *point);
+
+/**
+ * Create a new point and add it to the given mesh
+ * @param mesh The mesh to add the point to
+ * @param c The coordinates of the point to create
+ * @return The newly created point
+ */
+P2trPoint*    p2tr_mesh_new_point       (P2trMesh          *mesh,
+                                         const P2trVector2 *c);
+
+/**
+ * Create a new point and add it to the given mesh
+ * @param mesh The mesh to add the point to
+ * @param x The X coordinate of the point to create
+ * @param y The Y coordinate of the point to create
+ * @return The newly created point
+ */
+P2trPoint*    p2tr_mesh_new_point2      (P2trMesh  *mesh,
+                                         gdouble    x,
+                                         gdouble    y);
+
+/**
+ * Add an existing edge to the given mesh
+ * @param self The mesh to add the edge to
+ * @param edge The edge to add
+ * @return The given edge
+ */
+P2trEdge*     p2tr_mesh_add_edge        (P2trMesh  *self,
+                                         P2trEdge  *point);
+
+/**
+ * Create a new edge and add it to the given mesh
+ * @param mesh The mesh to add the edge to
+ * @param start The starting point of the edge
+ * @param end The ending point of the edge
+ * @param constrained Specify whether this edge is constrained or not
+ * @return Thew newly created edge
+ */
+P2trEdge*     p2tr_mesh_new_edge        (P2trMesh  *mesh,
+                                         P2trPoint *start,
+                                         P2trPoint *end,
+                                         gboolean   constrained);
+
+/**
+ * This function checks if an edge between two points exists, and if
+ * not it creates it. In both cases the returned edge will be returned
+ * with an extra reference, so it must be unreffed later.
+ * @param self The mesh of the returned edge
+ * @param start The starting point of the returned edge
+ * @param end The ending point of the returned edge
+ * @param constrained Specify whether this edge should be constrained
+ *        or not (in case a new edge is created)
+ * @return An edge between the two points
+ */
+P2trEdge*     p2tr_mesh_new_or_existing_edge (P2trMesh  *self,
+                                              P2trPoint *start,
+                                              P2trPoint *end,
+                                              gboolean   constrained);
+
+/**
+ * Add an existing triangle to the given mesh
+ * @param self The mesh to add the triangle to
+ * @param edge The triangle to add
+ * @return The given triangle
+ */
+P2trTriangle* p2tr_mesh_add_triangle        (P2trMesh     *self,
+                                             P2trTriangle *tri);
+
+/**
+ * Create a new triangle and add it to the given mesh
+ * @param mesh The mesh to add the triangle to
+ * @param AB An edge from the first point of the triangle to the second
+ * @param BC An edge from the second point of the triangle to the third
+ * @param CA An edge from the third point of the triangle to the first
+ */
+P2trTriangle* p2tr_mesh_new_triangle        (P2trMesh *mesh,
+                                             P2trEdge *AB,
+                                             P2trEdge *BC,
+                                             P2trEdge *CA);
+
+/** \internal
+ * This function should be called just before a point is removed from
+ * the mesh. It is used internally to update the mesh and it should not
+ * be called by any code outside of this library.
+ * @param mesh The mesh from which the point is going to be removed
+ * @param point The point which is going to be removed
+*/
+void          p2tr_mesh_on_point_removed    (P2trMesh  *mesh,
+                                             P2trPoint *point);
+
+/** \internal
+ * This function should be called just before an edge is removed from
+ * the mesh. It is used internally to update the mesh and it should not
+ * be called by any code outside of this library.
+ * @param mesh The mesh from which the edge is going to be removed
+ * @param edge The edge which is going to be removed
+*/
+void          p2tr_mesh_on_edge_removed     (P2trMesh *mesh,
+                                             P2trEdge *edge);
+
+/** \internal
+ * This function should be called just before a triangle is removed from
+ * the mesh. It is used internally to update the mesh and it should not
+ * be called by any code outside of this library.
+ * @param mesh The mesh from which the triangle is going to be removed
+ * @param triangle The triangle which is going to be removed
+*/
+void          p2tr_mesh_on_triangle_removed (P2trMesh     *mesh,
+                                             P2trTriangle *triangle);
+
+/**
+ * Begin recording all action performed on a mesh. Recording the
+ * actions performed on a mesh allows choosing later whether to commit
+ * those actions or whether they should be undone.
+ * \warning This function must not be called when recording is already
+ *          taking place!
+ * @param self The mesh whose actions should be recorded
+ */
+void          p2tr_mesh_action_group_begin    (P2trMesh *self);
+
+/**
+ * Terminate the current session of recording mesh actions by
+ * committing all the actions to the mesh
+ * \warning This function must not be called unless recording of
+ *          actions is already taking place!
+ * @param self The mesh whose actions were recorded
+ */
+void          p2tr_mesh_action_group_commit   (P2trMesh *self);
+
+/**
+ * Terminate the current session of recording mesh actions by
+ * undoing all the actions done to the mesh.
+ * \warning This function must not be called unless recording of
+ *          actions is already taking place!
+ * \warning A call to this function may invalidate all references to
+ *          any non virtual geometric primitives! If you plan on using
+ *          this function, consider using virtual data structures!
+ * @param self The mesh whose actions were recorded
+ */
+void          p2tr_mesh_action_group_undo     (P2trMesh *self);
+
+/**
+ * Remove all triangles, edges and points from a mesh
+ * @param mesh The mesh to clear
+ */
+void          p2tr_mesh_clear           (P2trMesh *mesh);
+
+/**
+ * Clear and then free the memory used by a mesh data structure
+ * @param mesh The mesh whose memory should be freed
+ */
+void          p2tr_mesh_free            (P2trMesh *mesh);
+
+/**
+ * Decrease the reference count to this mesh by 1
+ * @param mesh The mesh whose reference count should be decreased
+ */
+void          p2tr_mesh_unref           (P2trMesh *mesh);
+
+/**
+ * Increase the reference count to this mesh by 1
+ * @param mesh The mesh whose reference count should be increased
+ * @return The given mesh (\ref mesh)
+ */
+P2trMesh*     p2tr_mesh_ref             (P2trMesh *mesh);
+
+/**
+ * Find a triangle of the mesh, containing the point at the given
+ * location
+ * @param self The mesh whose triangles should be checked
+ * @param pt The location of the point to test
+ * @return The triangle containing the given point, or NULL if the
+ *         point is outside the triangulation domain
+ */
+P2trTriangle* p2tr_mesh_find_point      (P2trMesh *self,
+                                         const P2trVector2 *pt);
+
+/**
+ * Exactly like \ref p2tr_mesh_find_point, except for the fact that
+ * this variant also returns the UV coordinates of the point inside the
+ * triangle
+ * @param[in] self The mesh whose triangles should be checked
+ * @param[in] pt The location of the point to test
+ * @param[out] u The U coordinate of the point inside the triangle
+ * @param[out] v The V coordinate of the point inside the triangle
+ * @return The triangle containing the given point, or NULL if the
+ *         point is outside the triangulation domain
+ */
+P2trTriangle* p2tr_mesh_find_point2     (P2trMesh          *self,
+                                         const P2trVector2 *pt,
+                                         gdouble           *u,
+                                         gdouble           *v);
+
+/**
+ * Another variant of \ref p2tr_mesh_find_point taking an initial
+ * triangle that the search should begin from its area. The search is
+ * performed first on triangles close to the given triangle, and it
+ * gradually tests farther and farther triangles until it finds the one
+ * containing the given point.
+ *
+ * This way of search should be fast when the approximate area of the
+ * point to find is known, but it will be slower if initial triangle
+ * is not near.
+ *
+ * Note that in this function, triangles are considered near depending
+ * on the length of the chain of neighbor triangles needed to go from
+ * one to the other.
+ *
+ * \warning This function may use memory which is at least linear in
+ *          the amount of triangles to test. Therefor, do not use it
+ *          on large mesh objects unless the initial guess is supposed
+ *          to be good!
+ * @param self The mesh whose triangles should be checked
+ * @param pt The location of the point to test
+ * @param initial_guess An initial guess for which triangle contains
+ *        the point, or is at least near the triangle containing it
+ * @return The triangle containing the given point, or NULL if the
+ *         point is outside the triangulation domain
+ */
+P2trTriangle* p2tr_mesh_find_point_local (P2trMesh *self,
+                                          const P2trVector2 *pt,
+                                          P2trTriangle *initial_guess);
+
+/**
+ * Exactly like \ref p2tr_mesh_find_point_local, except for the fact
+ * that this variant also returns the UV coordinates of the point
+ * inside the triangle
+ * @param[in] self The mesh whose triangles should be checked
+ * @param[in] pt The location of the point to test
+ * @param[in] initial_guess An initial guess for which triangle
+ *            contains the point, or is at least near the triangle
+ *            containing it
+ * @param[out] u The U coordinate of the point inside the triangle
+ * @param[out] v The V coordinate of the point inside the triangle
+ * @return The triangle containing the given point, or NULL if the
+ *         point is outside the triangulation domain
+ */
+P2trTriangle* p2tr_mesh_find_point_local2 (P2trMesh *self,
+                                           const P2trVector2 *pt,
+                                           P2trTriangle *initial_guess,
+                                           gdouble *u,
+                                           gdouble *v);
+
+/**
+ * Find the bounding rectangle containing this mesh.
+ * @param[in] self The mesh whose bounding rectangle should be computed
+ * @param[out] min_x The minimal X coordinate of the mesh
+ * @param[out] min_y The minimal Y coordinate of the mesh
+ * @param[out] max_x The maximal X coordinate of the mesh
+ * @param[out] max_y The maximal Y coordinate of the mesh
+ */
+void          p2tr_mesh_get_bounds        (P2trMesh    *self,
+                                           gdouble     *min_x,
+                                           gdouble     *min_y,
+                                           gdouble     *max_x,
+                                           gdouble     *max_y);
+/** @} */
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/meson.build 
b/subprojects/poly2tri-c/poly2tri-c/refine/meson.build
new file mode 100644
index 000000000..70add0c2b
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/meson.build
@@ -0,0 +1,25 @@
+
+libp2tc_refine = static_library('p2tc_refine',
+  'bounded-line.c',
+  'cdt-flipfix.c',
+  'cdt.c',
+  'circle.c',
+  'cluster.c',
+  'delaunay-terminator.c',
+  'edge.c',
+  'line.c',
+  'mesh-action.c',
+  'mesh.c',
+  'point.c',
+  'pslg.c',
+  'refiner.c',
+  'rmath.c',
+  'triangle.c',
+  'utils.c',
+  'vector2.c',
+  'vedge.c',
+  'visibility.c',
+  'vtriangle.c',
+  dependencies: [ glib, ],
+  include_directories: [ rootInclude, ],
+)
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/point.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/point.c
new file mode 100644
index 000000000..722cab648
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/point.c
@@ -0,0 +1,227 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include "point.h"
+#include "edge.h"
+#include "mesh.h"
+
+P2trPoint*
+p2tr_point_new (const P2trVector2 *c)
+{
+  return p2tr_point_new2 (c->x, c->y);
+}
+
+P2trPoint*
+p2tr_point_new2 (gdouble x, gdouble y)
+{
+  P2trPoint *self = g_slice_new (P2trPoint);
+  
+  self->c.x = x;
+  self->c.y = y;
+  self->mesh = NULL;
+  self->outgoing_edges = NULL;
+  self->refcount = 1;
+
+  return self;
+}
+
+void
+p2tr_point_remove (P2trPoint *self)
+{
+  /* We can not iterate over the list of edges while removing the edges,
+   * because the removal action will modify the list. Instead we will
+   * simply look at the first edge untill the list is emptied. */
+  while (self->outgoing_edges != NULL)
+    p2tr_edge_remove ((P2trEdge*) self->outgoing_edges->data);
+
+  if (self->mesh != NULL)
+    p2tr_mesh_on_point_removed (self->mesh, self);
+}
+
+void
+p2tr_point_free (P2trPoint *self)
+{
+  p2tr_point_remove (self);
+  g_slice_free (P2trPoint, self);
+}
+
+P2trEdge*
+p2tr_point_has_edge_to (P2trPoint *start,
+                        P2trPoint *end)
+{
+  GList *iter;
+  
+  for (iter = start->outgoing_edges; iter != NULL; iter = iter->next)
+    {
+      P2trEdge *edge = (P2trEdge*) iter->data;
+      if (edge->end == end)
+        return edge;
+    }
+  
+  return NULL;
+}
+
+P2trEdge*
+p2tr_point_get_edge_to (P2trPoint *start,
+                        P2trPoint *end,
+                        gboolean   do_ref)
+{
+    P2trEdge* result = p2tr_point_has_edge_to (start, end);
+    if (result == NULL)
+      p2tr_exception_programmatic ("Tried to get an edge that doesn't exist!");
+    else
+      return do_ref ? p2tr_edge_ref (result) : result;
+}
+
+void
+_p2tr_point_insert_edge (P2trPoint *self, P2trEdge *e)
+{
+  GList *iter = self->outgoing_edges;
+
+  /* Remember: Edges are sorted in ASCENDING angle! */
+  while (iter != NULL && ((P2trEdge*)iter->data)->angle < e->angle)
+    iter = iter->next;
+
+  self->outgoing_edges =
+      g_list_insert_before (self->outgoing_edges, iter, e);
+
+  p2tr_edge_ref (e);
+}
+
+void
+_p2tr_point_remove_edge (P2trPoint *self, P2trEdge* e)
+{
+  GList *node;
+  
+  if (P2TR_EDGE_START(e) != self)
+    p2tr_exception_programmatic ("Could not remove the given outgoing "
+        "edge because doesn't start on this point!");
+
+  node = g_list_find (self->outgoing_edges, e);
+  if (node == NULL)
+    p2tr_exception_programmatic ("Could not remove the given outgoing "
+        "edge because it's not present in the outgoing-edges list!");
+
+  self->outgoing_edges = g_list_delete_link (self->outgoing_edges, node);
+
+  p2tr_edge_unref (e);
+}
+
+P2trEdge*
+p2tr_point_edge_ccw (P2trPoint *self,
+                     P2trEdge  *e)
+{
+  GList *node;
+  P2trEdge *result;
+
+  if (P2TR_EDGE_START(e) != self)
+      p2tr_exception_programmatic ("Not an edge of this point!");
+
+  node = g_list_find (self->outgoing_edges, e);
+  if (node == NULL)
+    p2tr_exception_programmatic ("Could not find the CCW sibling edge"
+        "because the edge is not present in the outgoing-edges list!");
+
+  result = (P2trEdge*) g_list_cyclic_next (self->outgoing_edges, node)->data;
+  return p2tr_edge_ref (result);
+}
+
+P2trEdge*
+p2tr_point_edge_cw (P2trPoint* self,
+                    P2trEdge *e)
+{
+  GList *node;
+  P2trEdge *result;
+
+  if (P2TR_EDGE_START(e) != self)
+      p2tr_exception_programmatic ("Not an edge of this point!");
+
+  node = g_list_find (self->outgoing_edges, e);
+  if (node == NULL)
+    p2tr_exception_programmatic ("Could not find the CW sibling edge"
+        "because the edge is not present in the outgoing-edges list!");
+
+  result = (P2trEdge*) g_list_cyclic_prev (self->outgoing_edges, node)->data;
+  return p2tr_edge_ref (result);
+}
+
+gboolean
+p2tr_point_is_fully_in_domain (P2trPoint *self)
+{
+  GList *iter;
+  for (iter = self->outgoing_edges; iter != NULL; iter = iter->next)
+    if (((P2trEdge*) iter->data)->tri == NULL)
+      return FALSE;
+      
+  return TRUE;
+}
+
+gboolean
+p2tr_point_has_constrained_edge (P2trPoint *self)
+{
+  GList *iter;
+  for (iter = self->outgoing_edges; iter != NULL; iter = iter->next)
+    if (((P2trEdge*) iter->data)->constrained)
+      return TRUE;
+      
+  return FALSE;
+}
+
+/**
+ * Increase the reference count of the given input point
+ * @param self - The point to ref
+ * @return The point given
+ */
+P2trPoint*
+p2tr_point_ref (P2trPoint *self)
+{
+  ++self->refcount;
+  return self;
+}
+
+void
+p2tr_point_unref (P2trPoint *self)
+{
+  g_assert (self->refcount > 0);
+  if (--self->refcount == 0)
+    p2tr_point_free (self);
+}
+
+P2trMesh*
+p2tr_point_get_mesh (P2trPoint *self)
+{
+  if (self->mesh)
+    return p2tr_mesh_ref (self->mesh);
+  else
+    return NULL;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/point.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/point.h
new file mode 100644
index 000000000..8b968fb6f
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/point.h
@@ -0,0 +1,99 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_POINT_H__
+#define __P2TC_REFINE_POINT_H__
+
+#include <glib.h>
+#include "vector2.h"
+#include "triangulation.h"
+
+/**
+ * @struct P2trPoint_
+ * A struct for a point in a triangular mesh
+ */
+struct P2trPoint_
+{
+  /** The 2D coordinates of the point */
+  P2trVector2  c;
+
+  /**
+   * A list of edges (@ref P2trEdge) which go out of this point (i.e.
+   * the point is their start point). The edges are sorted by ASCENDING
+   * angle, meaning they are sorted Counter Clockwise */
+  GList       *outgoing_edges;
+
+  /** A count of references to the point */
+  guint        refcount;
+  
+  /** The triangular mesh containing this point */
+  P2trMesh    *mesh;
+};
+
+P2trPoint*  p2tr_point_new                  (const P2trVector2 *c);
+
+P2trPoint*  p2tr_point_new2                 (gdouble x, gdouble y);
+
+P2trPoint*  p2tr_point_ref                  (P2trPoint *self);
+
+void        p2tr_point_unref                (P2trPoint *self);
+
+void        p2tr_point_free                 (P2trPoint *self);
+
+void        p2tr_point_remove               (P2trPoint *self);
+
+P2trEdge*   p2tr_point_has_edge_to          (P2trPoint *start,
+                                             P2trPoint *end);
+
+P2trEdge*   p2tr_point_get_edge_to          (P2trPoint *start,
+                                             P2trPoint *end,
+                                             gboolean   do_ref);
+
+void        _p2tr_point_insert_edge         (P2trPoint *self,
+                                             P2trEdge  *e);
+
+void        _p2tr_point_remove_edge         (P2trPoint *self,
+                                             P2trEdge  *e);
+
+P2trEdge*   p2tr_point_edge_ccw             (P2trPoint *self,
+                                             P2trEdge  *e);
+
+P2trEdge*   p2tr_point_edge_cw              (P2trPoint *self,
+                                             P2trEdge  *e);
+
+gboolean    p2tr_point_is_fully_in_domain   (P2trPoint *self);
+
+gboolean    p2tr_point_has_constrained_edge (P2trPoint *self);
+
+P2trMesh*   p2tr_point_get_mesh             (P2trPoint *self);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/pslg.c b/subprojects/poly2tri-c/poly2tri-c/refine/pslg.c
new file mode 100644
index 000000000..59f4d0dd9
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/pslg.c
@@ -0,0 +1,105 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include "pslg.h"
+
+static void
+free_bounded_line_if_not_null (gpointer line)
+{
+  p2tr_bounded_line_free ((P2trBoundedLine*)line);
+}
+
+/* A PSLG which is essentially a set of lines, will be represented by a
+ * hash table. When it will have an entry where both key and value are
+ * a line, it means that this is a new line and therefore should be
+ * freed. When it has an entry where the key is a line and the value is
+ * NULL, it means this is a line that should not be freed.
+ * This behaiour will be acheived by a NULL key free function and a
+ * real free function for values */
+P2trPSLG*
+p2tr_pslg_new (void)
+{
+  return g_hash_table_new_full (NULL, NULL, NULL, free_bounded_line_if_not_null);
+}
+
+void
+p2tr_pslg_add_new_line (P2trPSLG          *pslg,
+                        const P2trVector2 *start,
+                        const P2trVector2 *end)
+{
+  P2trBoundedLine *line = p2tr_bounded_line_new (start, end);
+  /* We would like to free this line, so also add it as a value */
+  g_hash_table_insert (pslg, line, line);
+}
+
+/* Add a line that needs not to be freed */
+void
+p2tr_pslg_add_existing_line (P2trPSLG              *pslg,
+                             const P2trBoundedLine *line)
+{
+  g_hash_table_insert (pslg, (P2trBoundedLine*) line, NULL);
+}
+
+guint
+p2tr_pslg_size (P2trPSLG *pslg)
+{
+  return g_hash_table_size (pslg);
+}
+
+void
+p2tr_pslg_iter_init (P2trPSLGIter *iter,
+                     P2trPSLG     *pslg)
+{
+  g_hash_table_iter_init (iter, pslg);
+}
+
+gboolean
+p2tr_pslg_iter_next (P2trPSLGIter           *iter,
+                     const P2trBoundedLine **line)
+{
+  /* The values are always stored in the key */
+  return g_hash_table_iter_next (iter, (gpointer*)line, NULL);
+}
+
+gboolean
+p2tr_pslg_contains_line (P2trPSLG              *pslg,
+                         const P2trBoundedLine *line)
+{
+  return g_hash_table_lookup_extended (pslg, line, NULL, NULL);
+}
+
+void
+p2tr_pslg_free (P2trPSLG *pslg)
+{
+  g_hash_table_destroy (pslg);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/pslg.h b/subprojects/poly2tri-c/poly2tri-c/refine/pslg.h
new file mode 100644
index 000000000..ef06bf34c
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/pslg.h
@@ -0,0 +1,115 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_PSLG_H__
+#define __P2TC_REFINE_PSLG_H__
+
+#include "utils.h"
+#include "vector2.h"
+#include "line.h"
+#include "bounded-line.h"
+
+typedef P2trHashSet     P2trPSLG;
+typedef P2trHashSetIter P2trPSLGIter;
+
+/**
+ * Create a new PSLG. After finishing to use this PSLG, it should be
+ * freed by calling @ref p2tr_pslg_free
+ * @return A new empty PSLG
+ */
+P2trPSLG* p2tr_pslg_new               (void);
+
+/**
+ * Add a new line to the PSLG, where the line is defined by two given
+ * points.
+ * @param[in] pslg The PSLG
+ * @param[in] start The first edge-point of the new line to add
+ * @param[in] end The second edge-point of the new line to add
+ */
+void      p2tr_pslg_add_new_line      (P2trPSLG          *pslg,
+                                       const P2trVector2 *start,
+                                       const P2trVector2 *end);
+
+/**
+ * Add an existing P2trBoundedLine to the PSLG, so that the line will
+ * not be freed when the PSLG is freed. This line must not be freed
+ * before the PSLG is freed!
+ * @param[in] pslg The PSLG
+ * @param[in] line The existing line to add
+ */
+void      p2tr_pslg_add_existing_line (P2trPSLG              *pslg,
+                                       const P2trBoundedLine *line);
+
+/**
+ * Count how many lines are there in the PSLG
+ * @param[in] pslg The PSLG
+ * @return The amount of lines in the PSLG
+ */
+guint     p2tr_pslg_size              (P2trPSLG *pslg);
+
+/**
+ * Initialize an iterator to iterate over all the lines of the PSLG. The
+ * iterator will remain valid as long as the PSLG is not modified.
+ * @param[out] iter The iterator for this PSLG
+ * @param[in] pslg The PSLG
+ */
+void      p2tr_pslg_iter_init         (P2trPSLGIter *iter,
+                                       P2trPSLG     *pslg);
+
+/**
+ * Advance the iterator to the next line of the PSLG
+ * @param[in] iter The PSLG iterator
+ * @param[out] line The next line of the PSLG
+ * @return TRUE if there was another line, FALSE if the iteration over
+ *         all the lines was finished
+ */
+gboolean  p2tr_pslg_iter_next         (P2trPSLGIter           *iter,
+                                       const P2trBoundedLine **line);
+
+/**
+ * Test whether the PSLG contains this line. The line comparision is
+ * done *by refrence* and not by value, so this function only test if
+ * the line at the given memory location is inside the PSLG.
+ * @param[in] pslg The PSLG
+ * @param[in] line The line to search for
+ * @return TRUE if the line was found in the PSLG, FALSE otherwise
+ */
+gboolean  p2tr_pslg_contains_line     (P2trPSLG              *pslg,
+                                       const P2trBoundedLine *line);
+
+/**
+ * Free a PSLG and all of the resources allocated for it
+ * @param[in] pslg The PSLG to free
+ */
+void      p2tr_pslg_free              (P2trPSLG *pslg);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/refine.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/refine.h
new file mode 100644
index 000000000..78288872c
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/refine.h
@@ -0,0 +1,59 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_H__
+#define __P2TC_REFINE_H__
+
+#include "utils.h"
+#include "rmath.h"
+
+#include "vector2.h"
+#include "circle.h"
+#include "line.h"
+#include "bounded-line.h"
+#include "pslg.h"
+
+#include "triangulation.h"
+
+#include "point.h"
+#include "edge.h"
+#include "triangle.h"
+#include "mesh.h"
+
+#include "vedge.h"
+#include "vtriangle.h"
+
+#include "cluster.h"
+#include "cdt.h"
+#include "refiner.h"
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/refiner.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/refiner.c
new file mode 100644
index 000000000..92e2c3285
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/refiner.c
@@ -0,0 +1,70 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include "cdt.h"
+#include "delaunay-terminator.h"
+#include "refiner.h"
+
+#define P2TR_REFINER_REAL_TYPE P2trDelaunayTerminator
+
+#define P2T_REFINER_TO_IMP(X) ((P2TR_REFINER_REAL_TYPE*)(X))
+#define P2T_IMP_TO_REFINER(X) ((P2trRefiner*)(X))
+
+gboolean
+p2tr_refiner_false_too_big (P2trTriangle *tri)
+{
+  return FALSE;
+}
+
+P2trRefiner*
+p2tr_refiner_new (gdouble             min_angle,
+                  P2trTriangleTooBig  size_control,
+                  P2trCDT            *cdt)
+{
+  return P2T_IMP_TO_REFINER (p2tr_dt_new (min_angle, size_control, cdt));
+}
+
+void
+p2tr_refiner_free (P2trRefiner *self)
+{
+  p2tr_dt_free (P2T_REFINER_TO_IMP (self));
+}
+
+void
+p2tr_refiner_refine (P2trRefiner             *self,
+                     gint                     max_steps,
+                     P2trRefineProgressNotify on_progress)
+{
+  p2tr_dt_refine (P2T_REFINER_TO_IMP (self), max_steps, on_progress);
+}
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/refiner.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/refiner.h
new file mode 100644
index 000000000..ffeba910b
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/refiner.h
@@ -0,0 +1,61 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_REFINER_H__
+#define __P2TC_REFINE_REFINER_H__
+
+#include <glib.h>
+#include "cdt.h"
+
+typedef struct P2trRefiner_ P2trRefiner;
+
+
+typedef gboolean (*P2trTriangleTooBig)       (P2trTriangle *tri);
+
+gboolean         p2tr_refiner_false_too_big  (P2trTriangle *tri);
+
+
+typedef void     (*P2trRefineProgressNotify) (P2trRefiner  *refiner,
+                                              int           step_number,
+                                              int           max_steps);
+
+P2trRefiner* p2tr_refiner_new    (gdouble                   min_angle,
+                                  P2trTriangleTooBig        size_control,
+                                  P2trCDT                  *cdt);
+
+void         p2tr_refiner_free   (P2trRefiner              *self);
+
+void         p2tr_refiner_refine (P2trRefiner              *self,
+                                  gint                      max_steps,
+                                  P2trRefineProgressNotify  on_progress);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/rmath.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/rmath.c
new file mode 100644
index 000000000..f644eaf61
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/rmath.c
@@ -0,0 +1,301 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <glib.h>
+#include "rmath.h"
+
+gdouble
+p2tr_math_length_sq (gdouble x1, gdouble y1,
+                     gdouble x2, gdouble y2)
+{
+  return P2TR_VECTOR2_DISTANCE_SQ2 (x1, y1, x2, y2);
+}
+
+gdouble
+p2tr_math_length_sq2 (const P2trVector2 *pt1,
+                      const P2trVector2 *pt2)
+{
+  return p2tr_math_length_sq (pt1->x, pt1->y, pt2->x, pt2->y);
+}
+
+static inline gdouble
+p2tr_matrix_det2 (gdouble a00, gdouble a01,
+                  gdouble a10, gdouble a11)
+{
+    return a00 * a11 - a10 * a01;
+}
+
+static inline gdouble
+p2tr_matrix_det3 (gdouble a00, gdouble a01, gdouble a02,
+                  gdouble a10, gdouble a11, gdouble a12,
+                  gdouble a20, gdouble a21, gdouble a22)
+{
+    return
+        + a00 * (a11 * a22 - a21 * a12)
+        - a01 * (a10 * a22 - a20 * a12)
+        + a02 * (a10 * a21 - a20 * a11);
+}
+
+static inline gdouble
+p2tr_matrix_det4 (gdouble a00, gdouble a01, gdouble a02, gdouble a03,
+                  gdouble a10, gdouble a11, gdouble a12, gdouble a13,
+                  gdouble a20, gdouble a21, gdouble a22, gdouble a23,
+                  gdouble a30, gdouble a31, gdouble a32, gdouble a33)
+{
+    return
+        + a00 * p2tr_matrix_det3 (a11, a12, a13,
+                                 a21, a22, a23,
+                                 a31, a32, a33)
+        - a01 * p2tr_matrix_det3 (a10, a12, a13,
+                                  a20, a22, a23,
+                                  a30, a32, a33)
+        + a02 * p2tr_matrix_det3 (a10, a11, a13,
+                                  a20, a21, a23,
+                                  a30, a31, a33)
+        - a03 * p2tr_matrix_det3 (a10, a11, a12,
+                                  a20, a21, a22,
+                                  a30, a31, a32);
+}
+
+void
+p2tr_math_triangle_circumcircle (const P2trVector2 *A,
+                                 const P2trVector2 *B,
+                                 const P2trVector2 *C,
+                                 P2trCircle    *circle)
+{
+  /*       | Ax Bx Cx |
+   * D = + | Ay By Cy | * 2
+   *       | +1 +1 +1 |
+   *
+   *       | Asq Bsq Csq |
+   * X = + | Ay  By  Cy  | / D
+   *       | 1   1   1   |
+   *
+   *       | Asq Bsq Csq |
+   * Y = - | Ax  Bx  Cx  | / D
+   *       | 1   1   1   |
+   */
+  gdouble Asq = P2TR_VECTOR2_LEN_SQ (A);
+  gdouble Bsq = P2TR_VECTOR2_LEN_SQ (B);
+  gdouble Csq = P2TR_VECTOR2_LEN_SQ (C);
+
+  gdouble invD = 1 / (2 * p2tr_matrix_det3 (
+      A->x, B->x, C->x,
+      A->y, B->y, C->y,
+      1,    1,    1));
+
+  circle->center.x = + p2tr_matrix_det3 (
+      Asq,  Bsq,  Csq,
+      A->y, B->y, C->y,
+      1,    1,    1) * invD;
+
+  circle->center.y = - p2tr_matrix_det3 (
+      Asq,  Bsq,  Csq,
+      A->x, B->x, C->x,
+      1,    1,    1) * invD;
+
+  circle->radius = sqrt (P2TR_VECTOR2_DISTANCE_SQ (A, &circle->center));
+}
+
+/* The point in triangle test which is implemented below is based on the
+ * algorithm which appears on:
+ *
+ *    http://www.blackpawn.com/texts/pointinpoly/default.html
+ */
+void
+p2tr_math_triangle_barcycentric (const P2trVector2 *A,
+                                 const P2trVector2 *B,
+                                 const P2trVector2 *C,
+                                 const P2trVector2 *P,
+                                 gdouble           *u,
+                                 gdouble           *v)
+{
+  gdouble dot00, dot01, dot02, dot11, dot12, invDenom;
+
+  /* Compute the vectors offsetted so that A is the origin */
+  P2trVector2 v0, v1, v2;
+  p2tr_vector2_sub(C, A, &v0);
+  p2tr_vector2_sub(B, A, &v1);
+  p2tr_vector2_sub(P, A, &v2);
+
+  /* Compute dot products */
+  dot00 = P2TR_VECTOR2_DOT(&v0, &v0);
+  dot01 = P2TR_VECTOR2_DOT(&v0, &v1);
+  dot02 = P2TR_VECTOR2_DOT(&v0, &v2);
+  dot11 = P2TR_VECTOR2_DOT(&v1, &v1);
+  dot12 = P2TR_VECTOR2_DOT(&v1, &v2);
+
+  /* Compute barycentric coordinates */
+  invDenom = 1 / (dot00 * dot11 - dot01 * dot01);
+  *u = (dot11 * dot02 - dot01 * dot12) * invDenom;
+  *v = (dot00 * dot12 - dot01 * dot02) * invDenom;
+}
+
+#define INTRIANGLE_EPSILON 0e-9
+
+P2trInTriangle
+p2tr_math_intriangle (const P2trVector2 *A,
+                      const P2trVector2 *B,
+                      const P2trVector2 *C,
+                      const P2trVector2 *P)
+{
+  gdouble u, v;
+  return p2tr_math_intriangle2 (A, B, C, P, &u, &v);
+}
+
+P2trInTriangle
+p2tr_math_intriangle2 (const P2trVector2 *A,
+                       const P2trVector2 *B,
+                       const P2trVector2 *C,
+                       const P2trVector2 *P,
+                       gdouble           *u,
+                       gdouble           *v)
+{
+  p2tr_math_triangle_barcycentric (A, B, C, P, u, v);
+  
+  /* Check if point is in triangle - i.e. whether (u + v) < 1 and both
+   * u and v are positive */
+  if ((*u >= INTRIANGLE_EPSILON) && (*v >= INTRIANGLE_EPSILON) && (*u + *v < 1 - INTRIANGLE_EPSILON))
+    return P2TR_INTRIANGLE_IN;
+  else if ((*u >= -INTRIANGLE_EPSILON) && (*v >= -INTRIANGLE_EPSILON) && (*u + *v <= 1 + INTRIANGLE_EPSILON))
+    return P2TR_INTRIANGLE_ON;
+  else
+    return P2TR_INTRIANGLE_OUT;
+}
+
+/* The point in triangle circumcircle test, and the 3-point orientation
+ * test, are both based on the work of Jonathan Richard Shewchuk. The
+ * technique used here is described in his paper "Adaptive Precision
+ * Floating-Point Arithmetic and Fast Robust Geometric Predicates"
+ */
+
+#define ORIENT2D_EPSILON 1e-9
+
+P2trOrientation p2tr_math_orient2d (const P2trVector2 *A,
+                                    const P2trVector2 *B,
+                                    const P2trVector2 *C)
+{
+  /* We are trying to compute this determinant:
+   * |Ax Ay 1|
+   * |Bx By 1|
+   * |Cx Cy 1|
+   */
+  gdouble result = p2tr_matrix_det3 (
+      A->x, A->y, 1,
+      B->x, B->y, 1,
+      C->x, C->y, 1
+  );
+
+  if (result > ORIENT2D_EPSILON)
+    return P2TR_ORIENTATION_CCW;
+  else if (result < -ORIENT2D_EPSILON)
+    return P2TR_ORIENTATION_CW;
+  else
+    return P2TR_ORIENTATION_LINEAR;
+}
+
+#define INCIRCLE_EPSILON 1e-9
+
+/* Points must be given in CCW order!!!!! */
+P2trInCircle
+p2tr_math_incircle (const P2trVector2 *A,
+                    const P2trVector2 *B,
+                    const P2trVector2 *C,
+                    const P2trVector2 *D)
+{
+  /* We are trying to compute this determinant:
+   * |Ax Ay Ax^2+Ay^2 1|
+   * |Bx By Bx^2+By^2 1|
+   * |Cx Cy Cx^2+Cy^2 1|
+   * |Dx Dy Dx^2+Dy^2 1|
+   */
+  gdouble result = p2tr_matrix_det4 (
+      A->x, A->y, P2TR_VECTOR2_LEN_SQ(A), 1,
+      B->x, B->y, P2TR_VECTOR2_LEN_SQ(B), 1,
+      C->x, C->y, P2TR_VECTOR2_LEN_SQ(C), 1,
+      D->x, D->y, P2TR_VECTOR2_LEN_SQ(D), 1
+  );
+
+  if (result > INCIRCLE_EPSILON)
+    return P2TR_INCIRCLE_IN;
+  else if (result < INCIRCLE_EPSILON)
+    return P2TR_INCIRCLE_OUT;
+  else
+    return P2TR_INCIRCLE_ON;
+}
+
+/* The point inside diametral-circle test and the point inside diametral
+ * lens test, are both based on the work of Jonathan Richard Shewchuk.
+ * The techniques used here are partially described in his paper
+ * "Delaunay Refinement Algorithms for Triangular Mesh Generation".
+ *
+ * W is inside the diametral circle (lens) of the line XY if and only if
+ * the angle XWY is larger than 90 (120) degrees. We know how to compute
+ * the cosine of that angle very easily like so:
+ *
+ * cos XWY = (WX * WY) / (|WX| * |WY|)
+ *
+ * Since XWY is larger than 90 (120) degrees, cos XWY <= 0 (-0.5) so:
+ *
+ * Diametral Circle               | Diametral Lens
+ * -------------------------------+-----------------------------------
+ * 0 >= (WX * WY) / (|WX| * |WY|) | - 0.5 >= (WX * WY) / (|WX| * |WY|)
+ * 0 >= WX * WY                   | - 0.5 * |WX| * |WY| >= WX * WY
+ */
+
+gboolean
+p2tr_math_diametral_circle_contains (const P2trVector2 *X,
+                                     const P2trVector2 *Y,
+                                     const P2trVector2 *W)
+{
+  P2trVector2 WX, WY;
+
+  p2tr_vector2_sub(X, W, &WX);
+  p2tr_vector2_sub(Y, W, &WY);
+
+  return P2TR_VECTOR2_DOT(&WX, &WY) <= 0;
+}
+
+gboolean
+p2tr_math_diametral_lens_contains (const P2trVector2 *X,
+                                   const P2trVector2 *Y,
+                                   const P2trVector2 *W)
+{
+  P2trVector2 WX, WY;
+
+  p2tr_vector2_sub(X, W, &WX);
+  p2tr_vector2_sub(Y, W, &WY);
+
+  return P2TR_VECTOR2_DOT(&WX, &WY)
+      <= 0.5 * p2tr_vector2_norm(&WX) * p2tr_vector2_norm(&WY);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/rmath.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/rmath.h
new file mode 100644
index 000000000..68f9e87f6
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/rmath.h
@@ -0,0 +1,132 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_MATH_H__
+#define __P2TC_REFINE_MATH_H__
+
+#include <glib.h>
+#include "vector2.h"
+#include "circle.h"
+
+gdouble   p2tr_math_length_sq  (gdouble x1,
+                                gdouble y1,
+                                gdouble x2,
+                                gdouble y2);
+
+gdouble   p2tr_math_length_sq2 (const P2trVector2 *pt1,
+                                const P2trVector2 *pt2);
+
+
+/**
+ * Find the circumscribing circle of a triangle defined by the given
+ * points.
+ * @param[in] A The first vertex of the triangle
+ * @param[in] B The second vertex of the triangle
+ * @param[in] C The third vertex of the triangle
+ * @param[out] circle The circumscribing circle of the triangle
+ */
+void      p2tr_math_triangle_circumcircle (const P2trVector2 *A,
+                                           const P2trVector2 *B,
+                                           const P2trVector2 *C,
+                                           P2trCircle    *circle);
+
+typedef enum
+{
+  P2TR_INTRIANGLE_OUT = -1,
+  P2TR_INTRIANGLE_ON = 0,
+  P2TR_INTRIANGLE_IN = 1
+} P2trInTriangle;
+
+/**
+ * Return the barycentric coordinates of a point inside a triangle. This
+ * means that the computation returns @ref u and @ref v so that the
+ * following equation is satisfied:
+ * {{{ AP = u * AB + v * AC }}}
+ *
+ * @param[in] A The first point of the triangle
+ * @param[in] B The second point of the triangle
+ * @param[in] C The third point of the triangle
+ * @param[in] P The point whose barycentric coordinates should be
+ *              computed
+ * @param[out] u The first barycentric coordinate
+ * @param[out] v The second barycentric coordinate
+ */
+void p2tr_math_triangle_barcycentric (const P2trVector2 *A,
+                                      const P2trVector2 *B,
+                                      const P2trVector2 *C,
+                                      const P2trVector2 *P,
+                                      gdouble           *u,
+                                      gdouble           *v);
+
+P2trInTriangle p2tr_math_intriangle (const P2trVector2 *A,
+                                     const P2trVector2 *B,
+                                     const P2trVector2 *C,
+                                     const P2trVector2 *P);
+
+P2trInTriangle p2tr_math_intriangle2 (const P2trVector2 *A,
+                                      const P2trVector2 *B,
+                                      const P2trVector2 *C,
+                                      const P2trVector2 *P,
+                                      gdouble           *u,
+                                      gdouble           *v);
+
+typedef enum
+{
+  P2TR_ORIENTATION_CW = -1,
+  P2TR_ORIENTATION_LINEAR = 0,
+  P2TR_ORIENTATION_CCW = 1
+} P2trOrientation;
+
+P2trOrientation p2tr_math_orient2d (const P2trVector2 *A,
+                                    const P2trVector2 *B,
+                                    const P2trVector2 *C);
+
+typedef enum
+{
+  P2TR_INCIRCLE_IN,
+  P2TR_INCIRCLE_ON,
+  P2TR_INCIRCLE_OUT
+} P2trInCircle;
+
+P2trInCircle p2tr_math_incircle (const P2trVector2 *A,
+                                 const P2trVector2 *B,
+                                 const P2trVector2 *C,
+                                 const P2trVector2 *D);
+
+gboolean  p2tr_math_diametral_circle_contains (const P2trVector2 *X,
+                                               const P2trVector2 *Y,
+                                               const P2trVector2 *W);
+
+gboolean  p2tr_math_diametral_lens_contains   (const P2trVector2 *X,
+                                               const P2trVector2 *Y,
+                                               const P2trVector2 *W);
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/triangle.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/triangle.c
new file mode 100644
index 000000000..ef47083a2
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/triangle.c
@@ -0,0 +1,313 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <glib.h>
+
+#include "utils.h"
+#include "rmath.h"
+
+#include "point.h"
+#include "edge.h"
+#include "triangle.h"
+#include "mesh.h"
+
+static void
+p2tr_validate_edges_can_form_tri (P2trEdge *AB,
+                                  P2trEdge *BC,
+                                  P2trEdge *CA)
+{
+  if (AB != AB->mirror->mirror ||
+      BC != BC->mirror->mirror ||
+      CA != CA->mirror->mirror)
+    {
+      p2tr_exception_programmatic ("Bad edge mirroring!");
+    }
+
+  if (AB->end != P2TR_EDGE_START(BC)
+      || BC->end != P2TR_EDGE_START(CA)
+      || CA->end != P2TR_EDGE_START(AB))
+    {
+      p2tr_exception_programmatic ("Unexpected edge sequence for a triangle!");
+    }
+
+  if (AB == BC->mirror || BC == CA->mirror || CA == AB->mirror)
+    {
+      p2tr_exception_programmatic ("Repeated edge in a triangle!");
+    }
+}
+
+P2trTriangle*
+p2tr_triangle_new (P2trEdge *AB,
+                   P2trEdge *BC,
+                   P2trEdge *CA)
+{
+  gint i;
+  P2trTriangle *self = g_slice_new (P2trTriangle);
+
+  self->refcount = 0;
+
+#ifndef P2TC_NO_LOGIC_CHECKS
+  p2tr_validate_edges_can_form_tri (AB, BC, CA);
+#endif
+
+  switch (p2tr_math_orient2d(&CA->end->c, &AB->end->c, &BC->end->c))
+    {
+      case P2TR_ORIENTATION_CCW:
+        self->edges[0] = CA->mirror;
+        self->edges[1] = BC->mirror;
+        self->edges[2] = AB->mirror;
+        break;
+
+      case P2TR_ORIENTATION_CW:
+        self->edges[0] = AB;
+        self->edges[1] = BC;
+        self->edges[2] = CA;
+        break;
+
+      case P2TR_ORIENTATION_LINEAR:
+        p2tr_exception_geometric ("Can't make a triangle of linear points!");
+    }
+
+#ifndef P2TC_NO_LOGIC_CHECKS
+  p2tr_validate_edges_can_form_tri (self->edges[0], self->edges[1], self->edges[2]);
+
+  if (p2tr_math_orient2d (&P2TR_TRIANGLE_GET_POINT(self,0)->c,
+                          &P2TR_TRIANGLE_GET_POINT(self,1)->c,
+                          &P2TR_TRIANGLE_GET_POINT(self,2)->c) != P2TR_ORIENTATION_CW)
+    {
+      p2tr_exception_programmatic ("Bad ordering!");
+    }
+#endif
+
+  for (i = 0; i < 3; i++)
+    {
+#ifndef P2TC_NO_LOGIC_CHECKS
+      if (self->edges[i]->tri != NULL)
+        p2tr_exception_programmatic ("This edge is already in use by "
+            "another triangle!");
+#endif
+      self->edges[i]->tri = self;
+      p2tr_edge_ref (self->edges[i]);
+      p2tr_triangle_ref (self);
+    }
+
+  return p2tr_triangle_ref (self);
+}
+
+P2trTriangle*
+p2tr_triangle_ref (P2trTriangle *self)
+{
+  ++self->refcount;
+  return self;
+}
+
+void
+p2tr_triangle_unref (P2trTriangle *self)
+{
+  g_assert (self->refcount > 0);
+  if (--self->refcount == 0)
+    p2tr_triangle_free (self);
+}
+
+void
+p2tr_triangle_free (P2trTriangle *self)
+{
+  g_assert (p2tr_triangle_is_removed (self));
+  g_slice_free (P2trTriangle, self);
+}
+
+void
+p2tr_triangle_remove (P2trTriangle *self)
+{
+  gint i;
+  P2trMesh *mesh;
+  
+  if (p2tr_triangle_is_removed (self))
+    return;
+
+  mesh = p2tr_triangle_get_mesh (self);
+  
+  if (mesh != NULL)
+    {
+      p2tr_mesh_on_triangle_removed (mesh, self);
+      p2tr_mesh_unref (mesh); /* The get function reffed it */
+    }
+
+  for (i = 0; i < 3; i++)
+  {
+    self->edges[i]->tri = NULL;
+    p2tr_edge_unref (self->edges[i]);
+    self->edges[i] = NULL;
+    /* Although we lost reference to the triangle 3 rows above, we must
+     * not unref it untill here since we still use the struct until this
+     * line! */
+    p2tr_triangle_unref (self);
+  }
+}
+
+P2trMesh*
+p2tr_triangle_get_mesh (P2trTriangle *self)
+{
+  if (self->edges[0] != NULL)
+    return p2tr_edge_get_mesh (self->edges[0]);
+  else
+    return NULL;
+}
+
+gboolean 
+p2tr_triangle_is_removed (P2trTriangle *self)
+{
+  return self->edges[0] == NULL;
+}
+
+P2trPoint*
+p2tr_triangle_get_opposite_point (P2trTriangle *self,
+                                  P2trEdge     *e,
+                                  gboolean      do_ref)
+{
+  P2trPoint *pt;
+  if (self->edges[0] == e || self->edges[0]->mirror == e)
+    pt = self->edges[1]->end;
+  else if (self->edges[1] == e || self->edges[1]->mirror == e)
+    pt = self->edges[2]->end;
+  else if (self->edges[2] == e || self->edges[2]->mirror == e)
+    pt = self->edges[0]->end;
+  else
+    p2tr_exception_programmatic ("The edge is not in the triangle!");
+
+  return do_ref ? p2tr_point_ref (pt) : pt;
+}
+
+P2trEdge*
+p2tr_triangle_get_opposite_edge (P2trTriangle *self,
+                                 P2trPoint    *p)
+{
+  if (self->edges[0]->end == p)
+    return p2tr_edge_ref (self->edges[2]);
+  if (self->edges[1]->end == p)
+    return p2tr_edge_ref (self->edges[0]);
+  if (self->edges[2]->end == p)
+    return p2tr_edge_ref (self->edges[1]);
+
+  p2tr_exception_programmatic ("The point is not in the triangle!");
+}
+
+/**
+ * Angles return by this function are always in the range [0,180]
+ */
+gdouble
+p2tr_triangle_get_angle_at (P2trTriangle *self,
+                            P2trPoint    *p)
+{
+  if (p == self->edges[0]->end)
+    return p2tr_edge_angle_between (self->edges[0], self->edges[1]);
+  else if (p == self->edges[1]->end)
+    return p2tr_edge_angle_between (self->edges[1], self->edges[2]);
+  else if (p == self->edges[2]->end)
+    return p2tr_edge_angle_between (self->edges[2], self->edges[0]);
+
+  p2tr_exception_programmatic ("Can't find the point!");
+}
+
+gdouble
+p2tr_triangle_smallest_non_constrained_angle (P2trTriangle *self)
+{
+    gdouble result = G_MAXDOUBLE, angle;
+    
+    if (! self->edges[0]->constrained || !self->edges[1]->constrained)
+      {
+        angle = p2tr_edge_angle_between(self->edges[0], self->edges[1]);
+        result = MIN(result, angle);
+      }
+
+    if (!self->edges[1]->constrained || !self->edges[2]->constrained)
+      {
+        angle = p2tr_edge_angle_between(self->edges[1], self->edges[2]);
+        result = MIN(result, angle);
+      }
+
+    if (!self->edges[2]->constrained || !self->edges[0]->constrained)
+      {
+        angle = p2tr_edge_angle_between(self->edges[2], self->edges[0]);
+        result = MIN(result, angle);
+      }
+
+    return result;
+}
+
+void
+p2tr_triangle_get_circum_circle (P2trTriangle *self,
+                                 P2trCircle   *circle)
+{
+  p2tr_math_triangle_circumcircle (
+      &P2TR_TRIANGLE_GET_POINT(self,0)->c,
+      &P2TR_TRIANGLE_GET_POINT(self,1)->c,
+      &P2TR_TRIANGLE_GET_POINT(self,2)->c,
+      circle);
+}
+
+P2trInCircle
+p2tr_triangle_circumcircle_contains_point (P2trTriangle      *self,
+                                           const P2trVector2  *pt)
+{
+  /* Points must be given in CCW order! */
+  return p2tr_math_incircle (
+      &P2TR_TRIANGLE_GET_POINT(self,2)->c,
+      &P2TR_TRIANGLE_GET_POINT(self,1)->c,
+      &P2TR_TRIANGLE_GET_POINT(self,0)->c,
+      pt);
+}
+
+P2trInTriangle
+p2tr_triangle_contains_point  (P2trTriangle      *self,
+                               const P2trVector2 *pt)
+{
+  return p2tr_math_intriangle (
+      &P2TR_TRIANGLE_GET_POINT(self,0)->c,
+      &P2TR_TRIANGLE_GET_POINT(self,1)->c,
+      &P2TR_TRIANGLE_GET_POINT(self,2)->c,
+      pt);
+}
+
+P2trInTriangle
+p2tr_triangle_contains_point2 (P2trTriangle      *self,
+                               const P2trVector2 *pt,
+                               gdouble           *u,
+                               gdouble           *v)
+{
+  return p2tr_math_intriangle2 (
+      &P2TR_TRIANGLE_GET_POINT(self,0)->c,
+      &P2TR_TRIANGLE_GET_POINT(self,1)->c,
+      &P2TR_TRIANGLE_GET_POINT(self,2)->c,
+      pt, u, v);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/triangle.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/triangle.h
new file mode 100644
index 000000000..bb18cfa92
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/triangle.h
@@ -0,0 +1,94 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_TRIANGLE_H__
+#define __P2TC_REFINE_TRIANGLE_H__
+
+#include <glib.h>
+#include "rmath.h"
+#include "triangulation.h"
+
+/**
+ * @struct P2trTriangle_
+ * A struct for a triangle in a triangular mesh
+ */
+struct P2trTriangle_
+{
+  P2trEdge* edges[3];
+  
+  guint refcount;
+};
+
+P2trTriangle*   p2tr_triangle_new            (P2trEdge *AB,
+                                              P2trEdge *BC,
+                                              P2trEdge *CA);
+
+P2trTriangle* p2tr_triangle_ref              (P2trTriangle *self);
+
+void        p2tr_triangle_unref              (P2trTriangle *self);
+
+void        p2tr_triangle_free               (P2trTriangle *self);
+
+void        p2tr_triangle_remove             (P2trTriangle *self);
+
+P2trMesh*   p2tr_triangle_get_mesh           (P2trTriangle *self);
+
+gboolean    p2tr_triangle_is_removed         (P2trTriangle *self);
+
+P2trPoint*  p2tr_triangle_get_opposite_point (P2trTriangle *self,
+                                              P2trEdge     *e,
+                                              gboolean      do_ref);
+
+P2trEdge*   p2tr_triangle_get_opposite_edge  (P2trTriangle *self,
+                                              P2trPoint    *p);
+
+gdouble     p2tr_triangle_get_angle_at       (P2trTriangle *self,
+                                              P2trPoint    *p);
+
+gdouble     p2tr_triangle_smallest_non_constrained_angle (P2trTriangle *self);
+
+void        p2tr_triangle_get_circum_circle (P2trTriangle *self,
+                                             P2trCircle   *circle);
+
+P2trInCircle p2tr_triangle_circumcircle_contains_point (P2trTriangle       *self,
+                                                        const P2trVector2  *pt);
+
+P2trInTriangle p2tr_triangle_contains_point  (P2trTriangle      *self,
+                                              const P2trVector2 *pt);
+
+P2trInTriangle p2tr_triangle_contains_point2 (P2trTriangle      *self,
+                                              const P2trVector2 *pt,
+                                              gdouble           *u,
+                                              gdouble           *v);
+
+#define P2TR_TRIANGLE_GET_POINT(tr,index) ((tr)->edges[((index)+3-1)%3]->end)
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/triangulation.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/triangulation.h
new file mode 100644
index 000000000..4a93ac0a9
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/triangulation.h
@@ -0,0 +1,49 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_TRIANGULATION_H__
+#define __P2TC_REFINE_TRIANGULATION_H__
+
+/** \ingroup P2trPoint */
+typedef struct P2trPoint_     P2trPoint;
+/** \ingroup P2trEdge */
+typedef struct P2trEdge_      P2trEdge;
+/** \ingroup P2trTriangle */
+typedef struct P2trTriangle_  P2trTriangle;
+/** \ingroup P2trMesh */
+typedef struct P2trMesh_      P2trMesh;
+
+/** \ingroup P2trVEdge */
+typedef struct P2trVEdge_     P2trVEdge;
+/** \ingroup P2trVTriangle */
+typedef struct P2trVTriangle_ P2trVTriangle;
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/utils.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/utils.c
new file mode 100644
index 000000000..d9d7db421
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/utils.c
@@ -0,0 +1,51 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdarg.h>
+#include <glib.h>
+
+#include "utils.h"
+
+GList*
+p2tr_utils_new_reversed_pointer_list (int count, ...)
+{
+  int i;
+  va_list args;
+  GList *result = NULL;
+  
+  va_start (args, count);
+  for (i = 0; i < count; i++)
+    result = g_list_prepend (result, va_arg (args, gpointer));
+  va_end (args);
+  
+  return result;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/utils.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/utils.h
new file mode 100644
index 000000000..576476fa5
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/utils.h
@@ -0,0 +1,88 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_UTILS_H__
+#define __P2TC_REFINE_UTILS_H__
+
+#ifdef __cplusplus
+extern "C"
+{
+#endif
+
+#include <glib.h>
+
+  /* The code for the Hash Set is partially based on the example given at
+   * http://developer.gnome.org/glib/2.29/glib-Hash-Tables.html
+   */
+
+  typedef GHashTable     P2trHashSet;
+  typedef GHashTableIter P2trHashSetIter;
+
+#define p2tr_hash_set_new_default() g_hash_table_new_full (g_direct_hash, g_direct_equal, NULL, NULL)
+#define p2tr_hash_set_new(hash_func, equal_func, destroy) g_hash_table_new_full ((hash_func), (equal_func), 
(destroy),NULL)
+#define p2tr_hash_set_insert(set,element)                              \
+G_STMT_START                                                           \
+{                                                                      \
+  /* The "obvious" code (presented below) won't work:               */ \
+  /*   g_hash_table_insert ((set), (element), (element))            */ \
+  /* since it will cause double evaluation of (element) which is a  */ \
+  /* problem!                                                       */ \
+  gpointer P2TR_HASH_SET_ELEM = (element);                             \
+  g_hash_table_insert ((set), P2TR_HASH_SET_ELEM, P2TR_HASH_SET_ELEM); \
+}                                                                      \
+G_STMT_END
+#define p2tr_hash_set_contains(set,element) g_hash_table_lookup_extended ((set), (element), NULL, NULL)
+#define p2tr_hash_set_remove(set,element) g_hash_table_remove ((set), (element))
+#define p2tr_hash_set_remove_all(set) g_hash_table_remove_all ((set))
+#define p2tr_hash_set_free(set) g_hash_table_destroy(set)
+#define p2tr_hash_set_size(set) g_hash_table_size(set)
+
+#define p2tr_hash_set_iter_init(iter,hash_set) g_hash_table_iter_init ((iter),(hash_set))
+#define p2tr_hash_set_iter_next(iter,val) g_hash_table_iter_next ((iter),(val),NULL)
+#define p2tr_hash_set_iter_remove(iter) g_hash_table_iter_remove ((iter))
+
+#define g_list_cyclic_prev(list,elem) (((elem)->prev != NULL) ? (elem)->prev : g_list_last ((elem)))
+#define g_list_cyclic_next(list,elem) (((elem)->next != NULL) ? (elem)->next : g_list_first ((elem)))
+
+#define foreach(iter,list) for ((iter) = (list); (iter) != NULL; (iter) = (iter)->next)
+
+#define p2tr_exception_numeric      g_error
+#define p2tr_exception_programmatic g_error
+#define p2tr_exception_geometric    g_error
+
+GList*  p2tr_utils_new_reversed_pointer_list (int count, ...);
+
+#ifdef __cplusplus
+}
+#endif
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/vector2.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/vector2.c
new file mode 100644
index 000000000..b6e335994
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/vector2.c
@@ -0,0 +1,84 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <glib.h>
+#include "vector2.h"
+
+gdouble
+p2tr_vector2_dot (const P2trVector2 *a,
+                  const P2trVector2 *b)
+{
+  return P2TR_VECTOR2_DOT (a, b);
+}
+
+gboolean
+p2tr_vector2_is_same (const P2trVector2 *a,
+                      const P2trVector2 *b)
+{
+  if (a == NULL || b == NULL)
+    return ! ((a == NULL) ^ (b == NULL));
+  else
+    return a->x == b->x && a->y == b->y;
+}
+
+void
+p2tr_vector2_sub (const P2trVector2 *a,
+                  const P2trVector2 *b,
+                  P2trVector2       *dest)
+{
+  dest->x = a->x - b->x;
+  dest->y = a->y - b->y;
+}
+
+void
+p2tr_vector2_center (const P2trVector2 *a,
+                     const P2trVector2 *b,
+                     P2trVector2       *dest)
+{
+  dest->x = (a->x + b->x) * 0.5;
+  dest->y = (a->y + b->y) * 0.5;
+}
+
+gdouble
+p2tr_vector2_norm (const P2trVector2 *v)
+{
+  return sqrt (P2TR_VECTOR2_LEN_SQ (v));
+}
+
+void
+p2tr_vector2_copy (P2trVector2       *dest,
+                   const P2trVector2 *src)
+{
+  dest->x = src->x;
+  dest->y = src->y;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/vector2.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/vector2.h
new file mode 100644
index 000000000..0573da20c
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/vector2.h
@@ -0,0 +1,101 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TR_REFINE_VECTOR2_H__
+#define __P2TR_REFINE_VECTOR2_H__
+
+#include <glib.h>
+
+/**
+ * \struct P2trVector2
+ * A struct for representing a vector with 2 coordinates (a point in 2D)
+ */
+typedef struct {
+  /** The first coordinate of the vector */
+  gdouble x;
+  /** The second coordinate of the vector */
+  gdouble y;
+} P2trVector2;
+
+#define P2TR_VECTOR2_LEN_SQ2(X, Y)              \
+    ((X) * (X) + (Y) * (Y))
+
+#define P2TR_VECTOR2_LEN_SQ(V)                  \
+    (P2TR_VECTOR2_LEN_SQ2((V)->x, (V)->y))
+
+#define P2TR_VECTOR2_DOT(V1,V2)                 \
+    ((V1)->x * (V2)->x + (V1)->y * (V2)->y)
+
+#define P2TR_VECTOR2_DISTANCE_SQ2(X1,Y1,X2,Y2)  \
+    (P2TR_VECTOR2_LEN_SQ2((X1) - (X2), (Y1) - (Y2)))
+
+#define P2TR_VECTOR2_DISTANCE_SQ(V1,V2)         \
+    (P2TR_VECTOR2_DISTANCE_SQ2((V1)->x, (V1)->y, (V2)->x, (V2)->y))
+
+/** Compute the dot product of two vectors */
+gdouble       p2tr_vector2_dot       (const P2trVector2 *a, const P2trVector2 *b);
+
+/** Check if all the coordinates of the two vectors are the same */
+gboolean      p2tr_vector2_is_same   (const P2trVector2 *a, const P2trVector2 *b);
+
+/**
+ * Compute the difference of two vectors
+ * @param[in] a The vector to subtract from
+ * @param[in] b The vector to be subtracted
+ * @param[out] dest The vector in which the result should be stored
+ */
+void          p2tr_vector2_sub       (const P2trVector2 *a, const P2trVector2 *b, P2trVector2 *dest);
+
+/**
+ * Compute the center point of the edge defined between two points
+ * @param[in] a The first side of the edge
+ * @param[in] b The second side of the edge
+ * @param[out] dest The vector in which the result should be stored
+ */
+void          p2tr_vector2_center    (const P2trVector2 *a, const P2trVector2 *b, P2trVector2 *dest);
+
+/**
+ * Compute the norm of a vector (the length of the line from the origin
+ * to the 2D point it represents)
+ * @param[in] v The vector whose norm should be computed
+ * @return The norm of the vector
+ */
+gdouble       p2tr_vector2_norm      (const P2trVector2 *v);
+
+/**
+ * Copy a vector
+ * @param[out] dest The destination of the copy operation
+ * @param[in] src The vector to copy
+ */
+void          p2tr_vector2_copy      (P2trVector2 *dest, const P2trVector2 *src);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/vedge.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/vedge.c
new file mode 100644
index 000000000..404423725
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/vedge.c
@@ -0,0 +1,228 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <math.h>
+#include <glib.h>
+
+#include "point.h"
+#include "edge.h"
+#include "vedge.h"
+#include "mesh.h"
+
+static void
+p2tr_vedge_init (P2trVEdge *self,
+                 P2trPoint *start,
+                 P2trPoint *end,
+                 gboolean   constrained)
+{
+  self->start       = start;
+  self->end         = end;
+  self->constrained = constrained;
+  self->refcount    = 0;
+}
+
+P2trVEdge*
+p2tr_vedge_new (P2trPoint *start,
+                P2trPoint *end,
+                gboolean   constrained)
+{
+  P2trVEdge *self = g_slice_new (P2trVEdge);
+
+  p2tr_vedge_init (self, start, end, constrained);
+
+  p2tr_point_ref (start);
+  p2tr_point_ref (end);
+  
+  ++self->refcount;
+  return self;
+}
+
+P2trVEdge*
+p2tr_vedge_new2 (P2trEdge *real)
+{
+  return p2tr_vedge_new (P2TR_EDGE_START (real), real->end,
+      real->constrained);
+}
+
+P2trVEdge*
+p2tr_vedge_ref (P2trVEdge *self)
+{
+  ++self->refcount;
+  return self;
+}
+
+void
+p2tr_vedge_unref (P2trVEdge *self)
+{
+  g_assert (self->refcount > 0);
+  if (--self->refcount == 0)
+    p2tr_vedge_free (self);
+}
+
+P2trEdge*
+p2tr_vedge_is_real (P2trVEdge *self)
+{
+  return p2tr_point_has_edge_to (self->start, self->end);
+}
+
+void
+p2tr_vedge_create (P2trVEdge *self)
+{
+  P2trMesh *mesh;
+  P2trEdge *edge;
+
+  g_assert (! p2tr_vedge_is_real (self));
+
+  mesh = p2tr_vedge_get_mesh (self);
+  if (mesh != NULL)
+    {
+      edge = p2tr_mesh_new_edge (mesh, self->start, self->end, self->constrained);
+      p2tr_mesh_unref (mesh);
+    }
+  else
+    edge = p2tr_edge_new (self->start, self->end, self->constrained);
+
+  p2tr_edge_unref (edge);
+}
+
+void
+p2tr_vedge_remove (P2trVEdge *self)
+{
+  P2trEdge *edge = p2tr_vedge_is_real (self);
+
+  g_assert (edge != NULL);
+
+  p2tr_edge_remove (edge);
+}
+
+void
+p2tr_vedge_free (P2trVEdge *self)
+{
+  p2tr_point_unref (self->start);
+  p2tr_point_unref (self->end);
+  g_slice_free (P2trVEdge, self);
+}
+
+P2trMesh*
+p2tr_vedge_get_mesh (P2trVEdge *self)
+{
+  return p2tr_point_get_mesh (self->end);
+}
+
+P2trEdge*
+p2tr_vedge_get (P2trVEdge *self)
+{
+  return p2tr_point_get_edge_to (self->start, self->end, TRUE);
+}
+
+gboolean
+p2tr_vedge_try_get_and_unref (P2trVEdge  *self,
+                              P2trEdge  **real)
+{
+  P2trEdge *real_one = p2tr_vedge_is_real (self);
+  if (real_one)
+    p2tr_edge_ref (real_one);
+  p2tr_vedge_unref (self);
+  return (*real = real_one) != NULL;
+}
+
+P2trVEdgeSet*
+p2tr_vedge_set_new (void)
+{
+  return p2tr_hash_set_new (
+      (GHashFunc)      p2tr_vedge_undirected_hash,
+      (GEqualFunc)     p2tr_vedge_undirected_equals,
+      NULL
+  );
+}
+
+void
+p2tr_vedge_set_add (P2trVEdgeSet *self,
+                   P2trEdge    *to_flip)
+{
+  p2tr_vedge_set_add2 (self, p2tr_vedge_new2 (to_flip));
+  p2tr_edge_unref (to_flip);
+}
+
+void
+p2tr_vedge_set_add2 (P2trVEdgeSet *self,
+                    P2trVEdge   *to_flip)
+{
+  if (p2tr_hash_set_contains (self, to_flip))
+    p2tr_vedge_unref (to_flip);
+  else
+    p2tr_hash_set_insert (self, to_flip);
+}
+
+gboolean
+p2tr_vedge_set_pop (P2trVEdgeSet  *self,
+                   P2trVEdge   **value)
+{
+  P2trHashSetIter iter;
+  p2tr_hash_set_iter_init (&iter, self);
+  if (p2tr_hash_set_iter_next (&iter, (gpointer*) value))
+    {
+      p2tr_hash_set_remove (self, *value);
+      return TRUE;
+    }
+  else
+    return FALSE;
+}
+
+guint
+p2tr_vedge_set_size (P2trVEdgeSet *self)
+{
+  return p2tr_hash_set_size (self);
+}
+
+void
+p2tr_vedge_set_free (P2trVEdgeSet *self)
+{
+  g_assert (p2tr_hash_set_size (self) == 0);
+  p2tr_hash_set_free (self);
+}
+
+gboolean
+p2tr_vedge_undirected_equals (const P2trVEdge *e1,
+                              const P2trVEdge *e2)
+{
+  return ((e1 == NULL) == (e2 == NULL)) &&
+      (e1 == e2
+      || (e1->start == e2->start && e1->end == e2->end)
+      || (e1->end == e2->start && e1->start == e2->end));
+}
+
+guint
+p2tr_vedge_undirected_hash (const P2trVEdge *edge)
+{
+  return g_direct_hash (edge->start) ^ g_direct_hash (edge->end);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/vedge.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/vedge.h
new file mode 100644
index 000000000..1623dadbd
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/vedge.h
@@ -0,0 +1,143 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_VEDGE_H__
+#define __P2TC_REFINE_VEDGE_H__
+
+#include <glib.h>
+#include "utils.h"
+#include "triangulation.h"
+
+/**
+ * @struct P2trVEdge_
+ * A struct for representing a potential ("virtual") edge in a
+ * triangular mesh
+ */
+struct P2trVEdge_
+{
+  /** The start point of this virtual edge */
+  P2trPoint *start;
+  /** The end point of this virtual edge */
+  P2trPoint *end;
+  /** A flag to make whether this is a constrained edge */
+  gboolean   constrained;
+  /** A count of references to the virtual edge */
+  guint      refcount;
+};
+
+P2trVEdge*  p2tr_vedge_new       (P2trPoint *start,
+                                  P2trPoint *end,
+                                  gboolean   constrained);
+
+P2trVEdge*  p2tr_vedge_new2      (P2trEdge  *real);
+
+P2trVEdge*  p2tr_vedge_ref       (P2trVEdge *self);
+
+void        p2tr_vedge_unref     (P2trVEdge *self);
+
+void        p2tr_vedge_free      (P2trVEdge *self);
+
+P2trMesh*   p2tr_vedge_get_mesh  (P2trVEdge *self);
+
+P2trEdge*   p2tr_vedge_is_real   (P2trVEdge *self);
+
+void        p2tr_vedge_create    (P2trVEdge *self);
+
+void        p2tr_vedge_remove    (P2trVEdge *self);
+
+P2trEdge*   p2tr_vedge_get       (P2trVEdge *self);
+
+/**
+ * Try get a real edge from a virtual edge, and then
+ * unref the virtual edge. IF A MATCHING REAL EDGE IS
+ * RETURNED, IT MUST BE UNREFFED BY THE CALLER!
+ * @param self The virtual edge to test
+ * @param real The place to store the matching real edge
+ * @return TRUE if a real edge was returned, FALSE if no
+ *         matching edge exists.
+ */
+gboolean    p2tr_vedge_try_get_and_unref (P2trVEdge  *self,
+                                          P2trEdge  **real);
+
+/**
+ * A set of edges to flip is basically a hash set, with unique equality
+ * and hashing function to prevent the same edge from appearing twice
+ * in different directions
+ */
+typedef P2trHashSet P2trVEdgeSet;
+
+/**
+ * Create a new flip-set - a set of virtual edges that should possibly
+ * be flipped to restore the Constrained Delaunay property to a
+ * triangulation
+*/
+P2trVEdgeSet* p2tr_vedge_set_new  (void);
+
+/**
+ * Add the given edge to the flip set. THE EDGE MUST HAVE BEEN REFFED
+ * BEFORE THE CALL TO THIS FUNCTION!
+ */
+void         p2tr_vedge_set_add  (P2trVEdgeSet  *self,
+                                  P2trEdge      *to_flip);
+
+/**
+ * Add the given virtual edge to the flip set. THE VIRTUAL EDGE MUST
+ * HAVE BEEN REFFED BEFORE THE CALL TO THIS FUNCTION!
+ */
+void         p2tr_vedge_set_add2 (P2trVEdgeSet  *self,
+                                  P2trVEdge     *to_flip);
+
+/**
+ * Try popping a virtual edge from the set. If succeeds, THE RETURNED
+ * VIRTUAL EDGE MUST BE UNREFFED!
+ */
+gboolean     p2tr_vedge_set_pop  (P2trVEdgeSet  *self,
+                                  P2trVEdge    **value);
+
+/**
+ * Returns the amount of virtual edges currently in the set
+ */
+guint        p2tr_vedge_set_size (P2trVEdgeSet  *self);
+
+/**
+ * Free the flip set. IT IS THE REPONSIBILITY OF THE CALLER TO MAKE
+ * SURE NO VIRTUAL EDGES WERE LEFT IN THE SET!
+ */
+void         p2tr_vedge_set_free (P2trVEdgeSet  *self);
+
+gboolean     p2tr_vedge_undirected_equals (const P2trVEdge *e1,
+                                           const P2trVEdge *e2);
+
+guint        p2tr_vedge_undirected_hash   (const P2trVEdge *edge);
+
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/visibility.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/visibility.c
new file mode 100644
index 000000000..b0645ca16
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/visibility.c
@@ -0,0 +1,438 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+/* Given a polygon "Poly" (a list of bounded lines), a point "P" and a
+ * a planar straight line graph (pslg) "PSLG", the question is if
+ * there is a straight line from "P" to some point in/on "Poly" so that
+ * the line does not intersect any of the lines of "PSLG".
+ *
+ * In this file is an algorithm for answering the above question, and
+ * it's pseudo-code is hereby presented:
+ *
+ * IsVisible(G, Poly, P):
+ * ----------------------
+ * { G = (V,E) - The PSLG where E is the set of edges in the graph   }
+ * { Poly      - A polygon (a list of edges)                         }
+ * { P         - The point we are checking whether is "visible" from }
+ * {             Poly                                                }
+ *
+ * W <- Some point in T (for example, center of weight)
+ *
+ * # A set of edges from @PSLG known to intersect some potential lines
+ * # from @P to @Poly
+ * KnownBlocks <- {}
+ *
+ * # A set of points on @Poly which should be checked whether a line
+ * # from them to @P is not obstructed by any of the edges of @PSLG
+ * SecondPoint <- {W}
+ *
+ * WHILE SecondPoint is not empty:
+ *
+ *   # Pick some point to check
+ *   S <- Some point from SecondPoint
+ *   SecondPoint <- SecondPoint \ {S}
+ *
+ *   PS <- The infinite line going through P and S
+ *
+ *   IF PS intersects @Poly:
+ *
+ *     IF there is an edge B=(u,v) (from E) that intersects PS so that
+ *        the intersection is between @Poly and @P:
+ *
+ *       IF B is not in KnownBlocks:
+ *         # Try new lines going through the end points of this edge
+ *         SecondPoint <- SecondPoint + {u,v}
+ *         KnownBlocks <- KnownBlocks + {B}
+ *
+ *       ELSE:
+ *         # Nothing to do - we already tried/are trying to handle this
+ *         # blocking edge by going around it
+ *       
+ *     ELSE:
+ *       RETURN "Visible"
+ *
+ *   ELSE:
+ *     # PS doesn't help anyway, no matter if it's blocked or not,
+ *     # since it does not reach the polygon
+ *
+ * RETURN "Ocluded"
+ */
+
+#include <glib.h>
+
+#include "visibility.h"
+
+
+static gboolean
+find_closest_intersection (P2trPSLG    *pslg,
+                           P2trLine    *line,
+                           P2trVector2 *close_to,
+                           P2trVector2 *dest)
+{
+    gdouble distance_sq = G_MAXDOUBLE;
+    gboolean found = FALSE;
+
+    const P2trBoundedLine *pslg_line = NULL;
+    P2trPSLGIter pslg_iter;
+    P2trVector2 temp;
+
+    p2tr_pslg_iter_init (&pslg_iter, pslg);
+
+    while (p2tr_pslg_iter_next (&pslg_iter, &pslg_line))
+    {
+        if (p2tr_line_intersection (&pslg_line->infinite, line, &temp)
+            == P2TR_LINE_RELATION_INTERSECTING)
+        {
+            gdouble test_distance_sq = P2TR_VECTOR2_DISTANCE_SQ (&temp, close_to);
+
+            found = TRUE;
+
+            if (test_distance_sq < distance_sq)
+            {
+                distance_sq = test_distance_sq;
+            }
+        }
+    }
+
+    if (found && dest != NULL)
+        p2tr_vector2_copy (dest, &temp);
+
+    return found;
+}
+
+static void
+find_point_in_polygon (P2trPSLG    *polygon,
+                       P2trVector2 *out_point)
+{
+    P2trPSLGIter iter;
+    const P2trBoundedLine *line = NULL;
+
+    g_assert (p2tr_pslg_size (polygon) >= 1);
+
+    p2tr_pslg_iter_init (&iter, polygon);
+    p2tr_pslg_iter_next (&iter, &line);
+
+    out_point->x = (line->start.x + line->end.x) / 2;
+    out_point->y = (line->start.y + line->end.y) / 2;
+}
+
+#ifdef EXPERIMENTAL_VISIBILITY
+static P2trBoundedLine*
+pslg_line_intersection (P2trPSLG        *pslg,
+                        P2trBoundedLine *line)
+{
+    P2trPSLGIter iter;
+    P2trBoundedLine *pslg_line = NULL;
+
+    p2tr_pslg_iter_init (&iter, pslg);
+    while (p2tr_pslg_iter_next (&iter, &pslg_line))
+        if (p2tr_bounded_line_intersect (line, pslg_line))
+            return pslg_line;
+
+    return NULL;
+}
+
+gboolean
+p2tr_pslg_visibility_check (P2trPSLG    *pslg,
+                            P2trVector2 *point,
+                            P2trPSLG    *polygon)
+{
+    P2trPSLG *known_blocks;
+    GArray   *second_points;
+    gboolean  found_visibility_path = FALSE;
+
+    /* W <- Some point in T (for example, center of weight) */
+    P2trVector2 W;
+    find_point_in_polygon (polygon, &W);
+
+    /* KnownBlocks <- {} */
+    known_blocks = p2tr_pslg_new ();
+
+    /* SecondPoint <- {W} */
+    second_points   = g_array_new (FALSE, FALSE, sizeof(P2trVector2));
+    g_array_append_val (second_points, W);
+
+    while ((! found_visibility_path) && second_points->len > 0)
+    {
+        P2trVector2 S;
+        P2trBoundedLine PS;
+        P2trVector2 poly_intersection;
+
+        /* S <- Some point from SecondPoint */
+        p2tr_vector2_copy (&S, &g_array_index(second_points, P2trVector2, 0));
+        /* SecondPoint <- SecondPoint \ {S} */
+        g_array_remove_index_fast (second_points, 0);
+
+        /* PS <- The infinite line going through P and S */
+        p2tr_bounded_line_init (&PS, &S, point);
+
+        /* IF PS intersects @Poly */
+        if (find_closest_intersection (polygon, &PS.infinite, point, &poly_intersection))
+        {
+            P2trBoundedLine PS_exact, *B;
+
+            /* IF there is an edge B=(u,v) (from E) that intersects PS */
+            p2tr_bounded_line_init (&PS_exact, point, &poly_intersection);
+            B = pslg_line_intersection (pslg, &PS_exact);
+
+            if (B != NULL)
+            {
+                /* IF B is not in KnownBlocks: */
+                if (! p2tr_pslg_contains_line (known_blocks, B))
+                {
+                    /* SecondPoint <- SecondPoint + {u,v} */
+                    g_array_append_val (second_points, B->start);
+                    g_array_append_val (second_points, B->end);
+                    /* KnownBlocks <- KnownBlocks + {B} */
+                    p2tr_pslg_add_existing_line (known_blocks, B);
+                }
+            }
+            else
+            {
+                found_visibility_path = TRUE;
+            }
+        }
+    }
+
+    g_array_free (second_points, TRUE);
+    p2tr_pslg_free (known_blocks);
+
+    return found_visibility_path;
+}
+#else
+
+/* Refer to the "Ray casting algorithm":
+ *   http://en.wikipedia.org/wiki/Point_in_polygon#Ray_casting_algorithm
+ */
+static gboolean
+PointIsInsidePolygon (P2trVector2 *vec,
+                     P2trPSLG    *polygon)
+{
+  P2trHashSetIter iter;
+  const P2trBoundedLine *polyline = NULL;
+  int count = 0;
+
+  /* So, if we work on the X axis, what we want to check is how many
+   * segments of the polygon cross the line (-inf,y)->(x,y) */
+  p2tr_pslg_iter_init (&iter, polygon);
+  while (p2tr_pslg_iter_next (&iter, &polyline))
+    {
+      if ((polyline->start.y - vec->y) * (polyline->end.y - vec->y) >= 0)
+        continue; /* The line doesn't cross the horizontal line Y = vec->y */
+      if (MIN (polyline->start.x, polyline->end.x) <= vec->x)
+        count++;
+    }
+  return (count % 2) == 1;
+}
+
+/* If the bounded line intersects the polygon in more than two places,
+ * then the answer is simply NO - the line is not completly outside the
+ * polygon.
+ * Else, if the bounded line intersects the polygon twice, we analyze
+ * its end points and middle point:
+ * - If both end-points are inside/on:
+ *   - Test the midpoint - it it's inside then the line goes inside the
+ *     polygon, otherwise it goes outside (remember, we said it
+ *     intersects the polygon exactly twice!)
+ * - Otherwise, there must be a subsegment of the poly-line which is
+ *   partially inside!
+ * Else, if it intersects exactly once:
+ * - If both end-points are inside/on then it's partially inside.
+ * - If exactly one end-points is inside/on then decide by the middle
+ *   point (if it's partially inside then so is the line)
+ * - If both end-points are outside - CAN'T HAPPEN WITH EXACTLY ONE INTERSECTION!
+ * Else, if it does not intersect:
+ * - Test any point inside the bounded line (end-points, middle points,
+ *   etc.). If it's inside then the bounded line is inside. Otherwise
+ *   it is outside
+ */
+static gboolean
+LineIsOutsidePolygon (P2trBoundedLine *line,
+                      P2trPSLG        *polygon)
+{
+  P2trHashSetIter iter;
+  const P2trBoundedLine *polyline = NULL;
+  P2trVector2 middle;
+  gint intersection_count = 0, inside_count = 0;
+
+  p2tr_pslg_iter_init (&iter, polygon);
+  while (p2tr_pslg_iter_next (&iter, &polyline))
+    {
+      if (p2tr_bounded_line_intersect (polyline, line))
+        if (++intersection_count > 2)
+          return FALSE;
+    }
+
+  inside_count += (PointIsInsidePolygon (&line->start, polygon) ? 1 : 0); 
+  inside_count += (PointIsInsidePolygon (&line->end, polygon) ? 1 : 0); 
+  
+  /* To decrease the chance of numeric errors when working on the edge points of
+   * the line, the point we will test is the middle of the input line */
+  middle.x = (line->start.x + line->end.x) / 2;
+  middle.y = (line->start.y + line->end.y) / 2;
+
+  if (intersection_count == 2)
+    {
+      if (inside_count == 2) return PointIsInsidePolygon (&middle, polygon);
+      else return TRUE;
+    }
+  else if (intersection_count == 1)
+    {
+      if (inside_count == 2) return TRUE;
+      else return PointIsInsidePolygon (&middle, polygon);
+    }
+  else return inside_count > 0;
+}
+
+static gboolean
+TryVisibilityAroundBlock(P2trPSLG        *PSLG,
+                         P2trVector2     *P,
+                         P2trPSLG        *ToSee,
+                         P2trPSLG        *KnownBlocks,
+                         GQueue          *BlocksForTest,
+                         /* Try on the edges of this block */
+                         const P2trBoundedLine *BlockBeingTested,
+                         const P2trVector2     *SideOfBlock)
+{
+  const P2trVector2 *S = SideOfBlock;
+  P2trVector2 ClosestIntersection;
+  P2trBoundedLine PS;
+  
+  p2tr_bounded_line_init (&PS, P, S);
+
+  if (find_closest_intersection (ToSee, &PS.infinite, P, &ClosestIntersection))
+    {
+      P2trPSLGIter iter;
+      P2trBoundedLine PK;
+      const P2trBoundedLine *Segment = NULL;
+      p2tr_bounded_line_init (&PK, P, &ClosestIntersection);
+
+      /* Now we must make sure that the bounded line PK is inside
+       * the polygon, because otherwise it is not considered as a
+       * valid visibility path */
+
+      p2tr_pslg_iter_init (&iter, PSLG);
+      while (p2tr_pslg_iter_next (&iter, &Segment))
+        {
+          if (Segment == BlockBeingTested)
+              continue;
+
+          /* If we have two segments with a shared point,
+           * the point should not be blocked by any of them
+           */
+          if (p2tr_vector2_is_same (SideOfBlock, &(Segment->start))
+              || p2tr_vector2_is_same (SideOfBlock, &(Segment->end)))
+              continue;
+
+          if (p2tr_bounded_line_intersect (Segment, &PK))
+            {
+              if (g_queue_find (BlocksForTest, Segment))
+                {
+                  g_queue_push_tail (BlocksForTest, (P2trBoundedLine*)Segment);
+                }
+              /* obstruction found! */
+              return FALSE;
+            }
+        }
+
+      if (LineIsOutsidePolygon (&PK, PSLG))
+        return FALSE;
+
+      /* No obstruction! */
+      return TRUE;
+    }
+  /* No intersection for this attempt, continue */
+  return FALSE;
+}
+
+/**
+ * Check if a point is "visible" from any one or more of the edges in a
+ * given group.
+ * Formally: Check if there is a line from @ref P to any of the edges in
+ * @ref Edges so that the line does not cross any of the lines of the
+ * PSLG @ref PSLG
+ */
+static gboolean
+IsVisibleFromEdges (P2trPSLG    *PSLG,
+                   P2trVector2 *P,
+                   P2trPSLG    *Edges)
+{
+    gboolean  found_visibility_path = FALSE;
+    P2trPSLG *KnownBlocks = p2tr_pslg_new ();
+    GQueue   *BlocksForTest = g_queue_new ();
+
+    P2trVector2 W;
+    find_point_in_polygon (Edges, &W);
+
+    if (TryVisibilityAroundBlock(PSLG, P, Edges, KnownBlocks, BlocksForTest, NULL, &W))
+        found_visibility_path = TRUE;
+
+    while (! g_queue_is_empty (BlocksForTest) && ! found_visibility_path)
+      {
+        const P2trBoundedLine *Block = (P2trBoundedLine*)g_queue_pop_head (BlocksForTest);
+
+        if (p2tr_pslg_contains_line (KnownBlocks, Block))
+            continue;
+        else if (TryVisibilityAroundBlock(PSLG, P, Edges, KnownBlocks, BlocksForTest, Block, &Block->start)
+            || TryVisibilityAroundBlock(PSLG, P, Edges, KnownBlocks, BlocksForTest, Block, &Block->end))
+          {
+            found_visibility_path = TRUE;
+          }
+        else
+            p2tr_pslg_add_existing_line (KnownBlocks, Block);
+      }
+
+    p2tr_pslg_free (KnownBlocks);
+    g_queue_free (BlocksForTest);
+
+    return found_visibility_path;
+}
+#endif
+
+gboolean
+p2tr_visibility_is_visible_from_edges (P2trPSLG              *pslg,
+                                       P2trVector2           *p,
+                                       const P2trBoundedLine *lines,
+                                       guint                  line_count)
+{
+  P2trPSLG *edges = p2tr_pslg_new ();
+  gint i;
+  gboolean result;
+  
+  for (i = 0; i < line_count; i++)
+    p2tr_pslg_add_existing_line (edges, &lines[i]);
+  
+  result = IsVisibleFromEdges (pslg, p, edges);
+  
+  p2tr_pslg_free (edges);
+  return result;
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/visibility.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/visibility.h
new file mode 100644
index 000000000..3109b9b65
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/visibility.h
@@ -0,0 +1,46 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_VISIBILITY_H__
+#define __P2TC_REFINE_VISIBILITY_H__
+
+#include <glib.h>
+
+#include "bounded-line.h"
+#include "vector2.h"
+#include "pslg.h"
+
+gboolean  p2tr_visibility_is_visible_from_edges (P2trPSLG              *pslg,
+                                                 P2trVector2           *p,
+                                                 const P2trBoundedLine *lines,
+                                                 guint                  line_count);
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/vtriangle.c 
b/subprojects/poly2tri-c/poly2tri-c/refine/vtriangle.c
new file mode 100644
index 000000000..e213372cf
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/vtriangle.c
@@ -0,0 +1,145 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+
+#include "point.h"
+#include "edge.h"
+#include "triangle.h"
+#include "mesh.h"
+
+#include "vtriangle.h"
+
+P2trVTriangle*
+p2tr_vtriangle_new (P2trTriangle *tri)
+{
+  P2trVTriangle *self = g_slice_new (P2trVTriangle);
+
+  self->points[0] = p2tr_point_ref (tri->edges[0]->end);
+  self->points[1] = p2tr_point_ref (tri->edges[1]->end);
+  self->points[2] = p2tr_point_ref (tri->edges[2]->end);
+
+  self->refcount = 1;
+
+  return self;
+}
+
+P2trVTriangle*
+p2tr_vtriangle_ref (P2trVTriangle *self)
+{
+  ++self->refcount;
+  return self;
+}
+
+void
+p2tr_vtriangle_unref (P2trVTriangle *self)
+{
+  g_assert (self->refcount > 0);
+  if (--self->refcount == 0)
+    p2tr_vtriangle_free (self);
+}
+
+void
+p2tr_vtriangle_free (P2trVTriangle *self)
+{
+  p2tr_point_unref (self->points[0]);
+  p2tr_point_unref (self->points[1]);
+  p2tr_point_unref (self->points[2]);
+  g_slice_free (P2trVTriangle, self);
+}
+
+P2trMesh*
+p2tr_vtriangle_get_mesh (P2trVTriangle *self)
+{
+  return p2tr_point_get_mesh (self->points[0]);
+}
+
+P2trTriangle*
+p2tr_vtriangle_is_real (P2trVTriangle *self)
+{
+  P2trEdge *e0, *e1, *e2;
+
+  /* The triangle exists if and only if all the edges
+   * still exist and they all are a part of the same
+   * triangle. */
+  if ((e0 = p2tr_point_has_edge_to (self->points[0], self->points[1])) &&
+      (e1 = p2tr_point_has_edge_to (self->points[1], self->points[2])) &&
+      (e2 = p2tr_point_has_edge_to (self->points[2], self->points[0])) &&
+      e0->tri == e1->tri && e1->tri == e2->tri)
+    return e0->tri;
+  else
+    return NULL;
+}
+
+void
+p2tr_vtriangle_create (P2trVTriangle *self)
+{
+  P2trMesh     *mesh;
+  P2trEdge     *e1, *e2, *e3;
+  P2trTriangle *tri;
+
+  g_assert (! p2tr_vtriangle_is_real (self));
+
+  mesh = p2tr_vtriangle_get_mesh (self);
+  e1 = p2tr_point_get_edge_to (self->points[0], self->points[1], FALSE);
+  e2 = p2tr_point_get_edge_to (self->points[1], self->points[2], FALSE);
+  e3 = p2tr_point_get_edge_to (self->points[2], self->points[0], FALSE);
+
+  if (mesh != NULL)
+    {
+      tri = p2tr_mesh_new_triangle (mesh, e1, e2, e3);
+      p2tr_mesh_unref (mesh);
+    }
+  else
+    tri = p2tr_triangle_new (e1, e2, e3);
+
+  p2tr_triangle_unref (tri);
+}
+
+void
+p2tr_vtriangle_remove (P2trVTriangle *self)
+{
+  P2trTriangle *tri = p2tr_vtriangle_is_real (self);
+
+  g_assert (tri != NULL);
+
+  p2tr_triangle_remove (tri);
+}
+
+P2trTriangle*
+p2tr_vtriangle_get (P2trVTriangle *self)
+{
+  P2trTriangle *real = p2tr_vtriangle_is_real (self);
+  g_assert (real != NULL);
+  return p2tr_triangle_ref (real);
+}
+
diff --git a/subprojects/poly2tri-c/poly2tri-c/refine/vtriangle.h 
b/subprojects/poly2tri-c/poly2tri-c/refine/vtriangle.h
new file mode 100644
index 000000000..5e63251fa
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/refine/vtriangle.h
@@ -0,0 +1,70 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_REFINE_VTRIANGLE_H__
+#define __P2TC_REFINE_VTRIANGLE_H__
+
+#include <glib.h>
+#include "rmath.h"
+#include "triangulation.h"
+
+/**
+ * @struct P2trVTriangle_
+ * A struct for representing a potential ("virtual") triangle
+ * in a triangular mesh
+ */
+struct P2trVTriangle_
+{
+  P2trPoint* points[3];
+  
+  guint refcount;
+};
+
+P2trVTriangle*   p2tr_vtriangle_new          (P2trTriangle  *tri);
+
+P2trVTriangle*   p2tr_vtriangle_ref          (P2trVTriangle *self);
+
+void             p2tr_vtriangle_unref        (P2trVTriangle *self);
+
+void             p2tr_vtriangle_free         (P2trVTriangle *self);
+
+P2trMesh*        p2tr_vtriangle_get_mesh     (P2trVTriangle *self);
+
+P2trTriangle*    p2tr_vtriangle_is_real      (P2trVTriangle *self);
+
+void             p2tr_vtriangle_create       (P2trVTriangle *self);
+
+void             p2tr_vtriangle_remove       (P2trVTriangle *self);
+
+P2trTriangle*    p2tr_vtriangle_get          (P2trVTriangle *self);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/render/mesh-render.c 
b/subprojects/poly2tri-c/poly2tri-c/render/mesh-render.c
new file mode 100644
index 000000000..f58bc3af7
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/render/mesh-render.c
@@ -0,0 +1,247 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <glib.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <poly2tri-c/refine/refine.h>
+#include "mesh-render.h"
+
+void
+p2tr_mesh_render_cache_uvt (P2trMesh        *T,
+                            P2trUVT         *dest,
+                            P2trImageConfig *config)
+{
+  p2tr_mesh_render_cache_uvt_exact (T, dest, config->x_samples * config->y_samples, config);
+}
+
+void
+p2tr_mesh_render_cache_uvt_exact (P2trMesh        *T,
+                                  P2trUVT         *dest,
+                                  gint             dest_len,
+                                  P2trImageConfig *config)
+{
+  gint x, y, n = dest_len;
+  P2trUVT *uvt = dest;
+  P2trTriangle *tr_prev = NULL;
+  P2trVector2 pt;
+  
+  pt.x = config->min_x;
+  pt.y = config->min_y;
+
+  uvt->tri = p2tr_mesh_find_point_local2 (T, &pt, NULL, &uvt->u, &uvt->v);
+  if (uvt->tri) p2tr_triangle_unref (uvt->tri);
+  tr_prev = uvt->tri;
+  
+  for (y = 0, pt.y = config->min_y; y < config->y_samples; ++y, pt.y += config->step_y)
+    for (x = 0, pt.x = config->min_x; x < config->x_samples; ++x, pt.x += config->step_x)
+      {
+        if (n-- == 0) return;
+        uvt->tri = p2tr_mesh_find_point_local2 (T, &pt, tr_prev, &uvt->u, &uvt->v);
+        if (uvt->tri) p2tr_triangle_unref (uvt->tri);
+        tr_prev = uvt->tri;
+        ++uvt;
+      }
+}
+
+#define P2TR_USE_BARYCENTRIC(u, v, A, B, C)                            \
+    ((A) + (v) * ((B) - (A)) + (u) * ((C) - (A)))
+
+/**
+ * This is a general macro for using a UVT cache in order to render a
+ * color interpolation triangular mesh. The reason this is a macro and
+ * not a function is to allow using different numeric types for
+ * representing colors
+ * @param uvt_cache The buffer containing the UVT cache of the area to
+ *        render. Should be of type  @ref P2trUVT*
+ * @param uvt_cache_w The width of the area for which the UVT cache was
+ *        created. Should be a positive integer.
+ * @param uvt_cache_h The height of the area for which the UVT cache was
+ *        created. Should be a positive integer.
+ * @param dest The buffer in which the rendering result should be saved.
+ *        Should be of type @ref cformat*
+ * @param n The amount of pixels to render into dest. Should be a
+ *        positive integer.
+ * @param cformat The type of the data inside @ref dest. This can be any
+ *        numeric type (double, float, int, ...)
+ * @param cpp The amount of color channels per pixel, not including the
+ *        alpha channel. Should be a positive integer.
+ * @param pt2col The function which maps mesh points into colors. This
+ *        function should be deterministic!
+ * @param pt2col_user_data An additional parameter to @ref pt2col
+ * @param alpha_last Specifies whether the alpha component should come
+ *        after or before the other color channels. Should be a boolean.
+ */
+#define P2TR_MESH_RENDER_FROM_CACHE(uvt_cache,                         \
+                                    uvt_cache_w,                       \
+                                    uvt_cache_h,                       \
+                                    dest,                              \
+                                    n,                                 \
+                                    cformat,                           \
+                                    cpp,                               \
+                                    pt2col,                            \
+                                    pt2col_user_data,                  \
+                                    alpha_last)                        \
+G_STMT_START                                                           \
+{                                                                      \
+  P2trUVT *uvt_p = (uvt_cache);                                        \
+  guint remain = n;                                                    \
+                                                                       \
+  P2trTriangle *tr_prev = NULL;                                        \
+  guint x, y, i;                                                       \
+  P2trPointToColorFuncC pt2col_c = (P2trPointToColorFuncC) (pt2col); \
+                                                                       \
+  cformat *colA = g_newa (cformat, (cpp));                             \
+  cformat *colB = g_newa (cformat, (cpp));                             \
+  cformat *colC = g_newa (cformat, (cpp));                             \
+                                                                       \
+  cformat *pixel = dest;                                               \
+                                                                       \
+  for (y = 0; y < (uvt_cache_w) && remain > 0; ++y)                    \
+    for (x = 0; x < (uvt_cache_h) && remain > 0; ++x, --remain, ++uvt_p) \
+      {                                                                \
+        P2trTriangle *tr_now = uvt_p->tri;                             \
+                                                                       \
+        /* If we are outside of the triangulation, set alpha to   */   \
+        /* zero and continue */                                        \
+        if (tr_now == NULL)                                            \
+          {                                                            \
+            /* Remember that cpp does not include the alpha! */        \
+            pixel[(alpha_last) ? (cpp) : 0] = 0;                       \
+            pixel += cpp + 1;                                          \
+          }                                                            \
+        else                                                           \
+          {                                                            \
+            gdouble u = uvt_p->u;                                      \
+            gdouble v = uvt_p->v;                                      \
+            /* If the triangle hasn't changed since the previous  */   \
+            /* pixel, then don't sample the color at the vertices */   \
+            /* again, since that is an expensive process!         */   \
+            if (tr_now != tr_prev)                                     \
+              {                                                        \
+                /* Get the points of the triangle in some fixed   */   \
+                /* order, just to make sure that the computation  */   \
+                /* goes the same everywhere                       */   \
+                P2trPoint *A = P2TR_TRIANGLE_GET_POINT (tr_now, 0);    \
+                P2trPoint *B = P2TR_TRIANGLE_GET_POINT (tr_now, 1);    \
+                P2trPoint *C = P2TR_TRIANGLE_GET_POINT (tr_now, 2);    \
+                /* At each point 'X' sample the color into 'colX' */   \
+                pt2col_c (A, (gpointer) colA, pt2col_user_data);       \
+                pt2col_c (B, (gpointer) colB, pt2col_user_data);       \
+                pt2col_c (C, (gpointer) colC, pt2col_user_data);       \
+                /* Set the current triangle */                         \
+                tr_now = tr_prev;                                      \
+              }                                                        \
+                                                                       \
+            /* We are inside the mesh, so set as opaque */             \
+            if (! alpha_last) *pixel++ = (cformat) 1;                  \
+            /* Interpolate the color using barycentric coodinates */   \
+            for (i = 0; i < cpp; ++i)                                  \
+              *pixel++ = (cformat) P2TR_USE_BARYCENTRIC (u, v,         \
+                  colA[i], colB[i], colC[i]);                          \
+            /* We are inside the mesh, so set as opaque */             \
+            if (alpha_last) *pixel++ = (cformat) 1;                    \
+          }                                                            \
+      }                                                                \
+}                                                                      \
+G_STMT_END
+
+#define P2TR_MESH_RENDER(mesh,                                         \
+                         dest,                                         \
+                         config,                                       \
+                         pt2col,                                       \
+                         pt2col_user_data,                             \
+                         cache_render_func)                            \
+G_STMT_START                                                           \
+{                                                                      \
+  gint n = (config)->x_samples * (config)->y_samples;                  \
+  P2trUVT *uvt_cache = g_new (P2trUVT, n);                             \
+                                                                       \
+  p2tr_mesh_render_cache_uvt_exact ((mesh), uvt_cache, n, (config));   \
+  cache_render_func (uvt_cache, (dest), n, (config), (pt2col),         \
+      (pt2col_user_data));                                             \
+                                                                       \
+  g_free (uvt_cache);                                                  \
+}                                                                      \
+G_STMT_END
+
+void
+p2tr_mesh_render_from_cache_f (P2trUVT               *uvt_cache,
+                               gfloat                *dest,
+                               gint                   n,
+                               P2trImageConfig       *config,
+                               P2trPointToColorFuncF  pt2col,
+                               gpointer               pt2col_user_data)
+{
+  P2TR_MESH_RENDER_FROM_CACHE (uvt_cache,
+      config->x_samples, config->y_samples,
+      dest, n, gfloat, config->cpp,
+      pt2col, pt2col_user_data,
+      config->alpha_last);
+}
+
+void
+p2tr_mesh_render_f (P2trMesh              *mesh,
+                    gfloat                *dest,
+                    P2trImageConfig       *config,
+                    P2trPointToColorFuncF  pt2col,
+                    gpointer               pt2col_user_data)
+{
+  P2TR_MESH_RENDER (mesh, dest, config, pt2col, pt2col_user_data,
+      p2tr_mesh_render_from_cache_f);
+}
+
+void
+p2tr_mesh_render_from_cache_b (P2trUVT               *uvt_cache,
+                               guint8                *dest,
+                               gint                   n,
+                               P2trImageConfig       *config,
+                               P2trPointToColorFuncB  pt2col,
+                               gpointer               pt2col_user_data)
+{
+  P2TR_MESH_RENDER_FROM_CACHE (uvt_cache,
+      config->x_samples, config->y_samples,
+      dest, n, guint8, config->cpp,
+      pt2col, pt2col_user_data,
+      config->alpha_last);
+}
+
+void
+p2tr_mesh_render_b (P2trMesh              *mesh,
+                    guint8                *dest,
+                    P2trImageConfig       *config,
+                    P2trPointToColorFuncB  pt2col,
+                    gpointer               pt2col_user_data)
+{
+  P2TR_MESH_RENDER (mesh, dest, config, pt2col, pt2col_user_data,
+      p2tr_mesh_render_from_cache_b);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/render/mesh-render.h 
b/subprojects/poly2tri-c/poly2tri-c/render/mesh-render.h
new file mode 100644
index 000000000..9a1d63c9b
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/render/mesh-render.h
@@ -0,0 +1,175 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TR_RENDER_MESH_RENDER_H__
+#define __P2TR_RENDER_MESH_RENDER_H__
+
+#include <glib.h>
+#include <poly2tri-c/refine/refine.h>
+
+/**
+ * A struct containing the necessary information to render a "color
+ * image" of a triangular mesh, by interpolating "colors" between its
+ * points.
+ * This can in fact be used to interpolate any N-Dimensional value set
+ * along the triangles of a mesh.
+ */
+typedef struct {
+  /** Minimal X and Y coordinates to start sampling at */
+  gdouble min_x, min_y;
+  /** Size of a step (distance between samples) in each axis */
+  gdouble step_x, step_y;
+  /** The amount of samples desired in each axis */
+  guint x_samples, y_samples;
+  /**
+   * The amount of channels per "pixel", both in the destination buffer
+   * and in the colors returned from the matching point-to-color
+   * function. Note that this does not include the alpha channel!
+   */
+  guint cpp;
+  /**
+   * Specifies whether the alpha channel (0 outside the mesh, 1 inside)
+   * should come after or before the the channel colors
+   */
+  gboolean alpha_last;
+} P2trImageConfig;
+
+/**
+ * A function that maps mesh points into colors
+ * @param point The mesh point
+ * @param dest The destination buffer for the color components, where
+ *        each component is one unsigned byte (guint8)
+ * @param user_data Custom data passed as a pointer to the function
+ */
+typedef void (*P2trPointToColorFuncB)   (P2trPoint            *point,
+                                         guint8               *dest,
+                                         gpointer              user_data);
+
+/**
+ * Similar to @ref P2trPointToColorFuncB, but with floating point data
+ * types for each color component
+ */
+typedef void (*P2trPointToColorFuncF)   (P2trPoint            *point,
+                                         gfloat               *dest,
+                                         gpointer              user_data);
+
+/**
+ * A generalization of all the point-to-color functions. This is used
+ * only for type casting inside the library and should not be used
+ * externally.
+ */
+typedef void (*P2trPointToColorFuncC)   (P2trPoint            *point,
+                                         void                 *dest,
+                                         gpointer              user_data);
+
+/**
+ * A struct for caching the barycentric coordinates of a point inside
+ * a triangle. A buffer of these structs is referred to as a UVT cache.
+ */
+typedef struct {
+  gdouble       u;
+  gdouble       v;
+  P2trTriangle *tri;
+} P2trUVT;
+
+/**
+ * Compute the UVT cache for the given mesh for the area and resolution
+ * specified by the image configuration struct. The cache for the point
+ * (min_x + i * step_x, min_y + j * step_y) would be at the index
+ * j * x_samples + i (assuming of course the point is inside the area
+ * described by image configuration struct).
+ */
+void   p2tr_mesh_render_cache_uvt       (P2trMesh             *mesh,
+                                         P2trUVT              *dest,
+                                         P2trImageConfig      *config);
+
+/**
+ * Similar to @ref p2tr_mesh_render_cache_uvt, but cache only the
+ * first @ref dest_len pixels.
+ */
+void   p2tr_mesh_render_cache_uvt_exact (P2trMesh             *mesh,
+                                         P2trUVT              *dest,
+                                         gint                  dest_len,
+                                         P2trImageConfig      *config);
+
+/**
+ * Render a mesh using a UVT cache that was computed for the given
+ * area, together with a point-to-color function.
+ * @param uvt_cache A cache for the given area, computed with
+ *        @ref p2tr_mesh_render_cache_uvt_exact
+ * @param dest The destination buffer for the image
+ * @param dest_len How many pixels to render from the area. The cache
+ *        should contain data for at least this many pixels!
+ * @param config The render configuration struct
+ * @param pt2col A function that receives points in the mesh and returns
+ *        colors. The returned colors should have config->cpp components
+ * @param pt2col_user_data Custom data to pass to @ref pt2col
+ */
+void   p2tr_mesh_render_from_cache_f    (P2trUVT               *uvt_cache,
+                                         gfloat                *dest,
+                                         gint                   dest_len,
+                                         P2trImageConfig       *config,
+                                         P2trPointToColorFuncF  pt2col,
+                                         gpointer               pt2col_user_data);
+
+/**
+ * Render a mesh with the given area and sampling configuration. Same
+ * as first caching @ref p2tr_mesh_render_cache_uvt and then calling
+ * @ref p2tr_mesh_render_cache_uvt_exact with the cache, and finally
+ * freeing the cache
+ */
+void   p2tr_mesh_render_f               (P2trMesh              *mesh,
+                                         gfloat                *dest,
+                                         P2trImageConfig       *config,
+                                         P2trPointToColorFuncF  pt2col,
+                                         gpointer               pt2col_user_data);
+
+/**
+ * See @ref p2tr_mesh_render_from_cache_f
+ */
+void   p2tr_mesh_render_from_cache_b    (P2trUVT               *uvt_cache,
+                                         guint8                *dest,
+                                         gint                   dest_len,
+                                         P2trImageConfig       *config,
+                                         P2trPointToColorFuncB  pt2col,
+                                         gpointer               pt2col_user_data);
+
+/**
+ * See @ref p2tr_mesh_render_f
+ */
+void   p2tr_mesh_render_b               (P2trMesh              *mesh,
+                                         guint8                *dest,
+                                         P2trImageConfig       *config,
+                                         P2trPointToColorFuncB  pt2col,
+                                         gpointer               pt2col_user_data);
+
+#endif
diff --git a/subprojects/poly2tri-c/poly2tri-c/render/meson.build 
b/subprojects/poly2tri-c/poly2tri-c/render/meson.build
new file mode 100644
index 000000000..ffe06a84c
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/render/meson.build
@@ -0,0 +1,6 @@
+
+libp2tc_render = static_library('p2tc_render',
+  [ 'mesh-render.c', 'svg-plot.c' ],
+  dependencies: [ glib, ],
+  include_directories: [ rootInclude, ],
+)
diff --git a/subprojects/poly2tri-c/poly2tri-c/render/svg-plot.c 
b/subprojects/poly2tri-c/poly2tri-c/render/svg-plot.c
new file mode 100644
index 000000000..66f636cc5
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/render/svg-plot.c
@@ -0,0 +1,204 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <stdlib.h>
+#include <stdio.h>
+#include <math.h>
+#include <glib.h>
+
+#include <poly2tri-c/refine/triangulation.h>
+
+#include "svg-plot.h"
+
+#define P2TR_SVG_NEWLINE "\n"
+
+void
+p2tr_render_svg_init (FILE              *out,
+                      const P2trVector2 *bottom_left,
+                      const P2trVector2 *top_right)
+{
+  gdouble real_width = top_right->x - bottom_left->x;
+  gdouble real_height = top_right->y - bottom_left->y;
+
+  /* Begin with the header of the document */
+  fprintf (out, "<?xml version=\"1.0\" standalone=\"no\"?>%s",
+      P2TR_SVG_NEWLINE);
+  fprintf (out, "<!DOCTYPE svg PUBLIC \"-//W3C//DTD SVG 1.1//EN\"%s",
+      P2TR_SVG_NEWLINE);
+  fprintf (out, "\"http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd\";>%s",
+      P2TR_SVG_NEWLINE);
+
+  fprintf (out, "<svg xmlns=\"http://www.w3.org/2000/svg\"";
+                "     version=\"1.1\"%s", P2TR_SVG_NEWLINE);
+
+  fprintf (out, "     viewBox=\"%f %f %f %f\"%s",
+      + bottom_left->x, - (bottom_left->y + real_height),
+      + real_width,     + real_height,
+      P2TR_SVG_NEWLINE);
+
+  fprintf (out, "     preserveAspectRatio=\"xMidYMid meet\"%s",
+      P2TR_SVG_NEWLINE);
+
+  /* Close the SVG tag */
+  fprintf (out, ">%s", P2TR_SVG_NEWLINE);
+
+  fprintf (out, "<g transform=\"scale(1,-1)\">%s",
+      P2TR_SVG_NEWLINE);
+}
+
+void
+p2tr_render_svg_finish (FILE *out)
+{
+  fprintf (out, "</g>%s", P2TR_SVG_NEWLINE);
+  fprintf (out, "</svg>%s", P2TR_SVG_NEWLINE);
+}
+
+static void
+p2tr_render_svg_style (FILE           *out,
+                       P2trSVGContext *context,
+                       gboolean        no_fill)
+{
+  fprintf (out, "style=\"");
+    {
+      if (context->stroke)
+        {
+          fprintf (out, "stroke: #%02x%02x%02x; stroke-opacity: %f; ",
+              context->stroke_color[0], context->stroke_color[1],
+              context->stroke_color[2], context->stroke_color[3] / 255.0);
+          fprintf (out, "stroke-:width: %f; stroke-linejoin: round; ",
+              context->stroke_width);
+        }
+
+      if (context->fill && ! no_fill)
+        fprintf (out, "fill: #%02x%02x%02x; fill-opacity: %f; ",
+            context->fill_color[0], context->fill_color[1],
+            context->fill_color[2], context->fill_color[3] / 255.0);
+
+      if (context->opacity != 1)
+        fprintf (out, "opacity: %f; ", context->opacity);
+    }
+  fprintf (out, "\"");
+}
+
+void
+p2tr_render_svg_draw_line (FILE              *out,
+                           P2trSVGContext    *context,
+                           const P2trVector2 *start,
+                           const P2trVector2 *end)
+{
+  fprintf (out, "<line x1=\"%f\" y1=\"%f\" x2=\"%f\" y2=\"%f\" ",
+      start->x, start->y, end->x, end->y);
+  p2tr_render_svg_style (out, context, TRUE);
+  fprintf (out, " />%s", P2TR_SVG_NEWLINE);
+}
+
+void
+p2tr_render_svg_draw_triangle (FILE              *out,
+                               P2trSVGContext    *context,
+                               const P2trVector2 *p1,
+                               const P2trVector2 *p2,
+                               const P2trVector2 *p3)
+{
+  fprintf (out, "<polygon points=\"%f,%f %f,%f %f,%f\" ",
+      p1->x, p1->y, p2->x, p2->y, p3->x, p3->y);
+  p2tr_render_svg_style (out, context, FALSE);
+  fprintf (out, " />%s", P2TR_SVG_NEWLINE);
+}
+
+void
+p2tr_render_svg_draw_circle (FILE              *out,
+                             P2trSVGContext    *context,
+                             const P2trVector2 *center,
+                             gdouble            radius)
+{
+  fprintf (out, "<circle cx=\"%f\" cy=\"%f\" r=\"%f\" ",
+      center->x, center->y, radius);
+  p2tr_render_svg_style (out, context, FALSE);
+  fprintf (out, " />%s", P2TR_SVG_NEWLINE);
+}
+
+void
+p2tr_render_svg (P2trMesh *mesh,
+                 FILE     *out)
+{
+  P2trHashSetIter  siter;
+  P2trTriangle    *tr;
+  P2trPoint       *pt;
+
+  /* Colors taken from the Tango Icon Theme color palette */
+  P2trSVGContext  TRI = {
+      TRUE,
+      1,
+      /* Sky Blue 3 */
+      { 32, 74, 135, 255 },
+      TRUE,
+      /* Sky Blue 1 */
+      { 114, 159, 207, 255 },
+      1
+  };
+
+  P2trSVGContext PT = {
+      FALSE,
+      0,
+      /* Orange 3 */
+      { 206, 92, 0, 1 },
+      TRUE,
+      /* Orange 1 */
+      { 245, 121, 0, 255 },
+      1
+  };
+
+  P2trVector2 bottom_left, top_right;
+
+  p2tr_mesh_get_bounds (mesh,
+      &bottom_left.x, &bottom_left.y,
+      &top_right.x,   &top_right.y);
+
+  bottom_left.x -= 10;
+  bottom_left.y -= 10;
+  top_right.x += 10;
+  top_right.y += 10;
+  p2tr_render_svg_init (out, &bottom_left, &top_right);
+
+  p2tr_hash_set_iter_init (&siter, mesh->triangles);
+  while (p2tr_hash_set_iter_next (&siter, (gpointer*)&tr))
+    p2tr_render_svg_draw_triangle (out, &TRI,
+        &P2TR_TRIANGLE_GET_POINT(tr, 0)->c,
+        &P2TR_TRIANGLE_GET_POINT(tr, 1)->c,
+        &P2TR_TRIANGLE_GET_POINT(tr, 2)->c);
+
+  p2tr_hash_set_iter_init (&siter, mesh->points);
+  while (p2tr_hash_set_iter_next (&siter, (gpointer*)&pt))
+    p2tr_render_svg_draw_circle (out, &PT, &pt->c, 1);
+
+  p2tr_render_svg_finish (out);
+}
diff --git a/subprojects/poly2tri-c/poly2tri-c/render/svg-plot.h 
b/subprojects/poly2tri-c/poly2tri-c/render/svg-plot.h
new file mode 100644
index 000000000..a94acd64b
--- /dev/null
+++ b/subprojects/poly2tri-c/poly2tri-c/render/svg-plot.h
@@ -0,0 +1,75 @@
+/*
+ * This file is a part of Poly2Tri-C
+ * (c) Barak Itkin <lightningismyname gmail com>
+ * http://code.google.com/p/poly2tri-c/
+ *
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without modification,
+ * are permitted provided that the following conditions are met:
+ *
+ * * Redistributions of source code must retain the above copyright notice,
+ *   this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above copyright notice,
+ *   this list of conditions and the following disclaimer in the documentation
+ *   and/or other materials provided with the distribution.
+ * * Neither the name of Poly2Tri nor the names of its contributors may be
+ *   used to endorse or promote products derived from this software without specific
+ *   prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
+ * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
+ * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
+ * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR
+ * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL,
+ * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
+ * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR
+ * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF
+ * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+ * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
+ * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef __P2TC_RENDER_SVG_PLOT_H__
+#define __P2TC_RENDER_SVG_PLOT_H__
+
+#include <poly2tri-c/refine/refine.h>
+
+typedef guint8 P2trSVGColor[4];
+
+typedef struct
+{
+  gboolean     stroke;
+  gdouble      stroke_width;
+  P2trSVGColor stroke_color;
+  gboolean     fill;
+  P2trSVGColor fill_color;
+  gdouble      opacity;
+} P2trSVGContext;
+
+void p2tr_render_svg_init          (FILE              *out,
+                                    const P2trVector2 *bottom_left,
+                                    const P2trVector2 *top_right);
+
+void p2tr_render_svg_draw_line     (FILE              *out,
+                                    P2trSVGContext    *context,
+                                    const P2trVector2 *start,
+                                    const P2trVector2 *end);
+
+void p2tr_render_svg_draw_triangle (FILE              *out,
+                                    P2trSVGContext    *context,
+                                    const P2trVector2 *p1,
+                                    const P2trVector2 *p2,
+                                    const P2trVector2 *p3);
+
+void p2tr_render_svg_draw_circle   (FILE              *out,
+                                    P2trSVGContext    *context,
+                                    const P2trVector2 *center,
+                                    gdouble            radius);
+
+void p2tr_render_svg_finish        (FILE              *out);
+
+void p2tr_render_svg               (P2trMesh          *mesh,
+                                    FILE              *out);
+
+#endif
diff --git a/tests/buffer/buffer-test-wrapper.c b/tests/buffer/buffer-test-wrapper.c
new file mode 100644
index 000000000..4a904a801
--- /dev/null
+++ b/tests/buffer/buffer-test-wrapper.c
@@ -0,0 +1,370 @@
+
+#include "config.h"
+#include <gegl.h>
+#include <gegl-buffer.h>
+#include <math.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <glib.h>
+#include <glib/gstdio.h>
+
+/* This file consists of a testing suite for the GeglBuffer API. For every
+ * function matching the regexp ^static.*(' in the file a test is performed and
+ * the output is stored in in a file with the name of the function.
+ *
+ * The makefile contains shell scripting that provides knowledge of how much
+ * passes the reference suite, testing should occur over a range of different
+ * tile sizes to make sure the behavior is consistent.
+ */
+
+#include "gegl/buffer/gegl-buffer-iterator.h"
+
+
+
+/* helper macros for the output, issue a test_start() after your defined
+ * variables and the first logic, use print as your printf and print_buffer to
+ * dump a GeglBuffer's contents to the log, issue test_end for the final
+ * rendering.
+ */
+
+#define test_start()              GString *gstring=g_string_new("");\
+                                  print (("Test: %s\n", TEST_NAME))
+#define print(args)              G_STMT_START {                \
+       gchar *_fmt = g_strdup_printf args;                     \
+       g_string_append (gstring, _fmt);                        \
+       g_free (_fmt);                                          \
+                                 } G_STMT_END
+#define print_buffer(buffer)      print_buffer_internal (gstring, buffer)
+#define print_linear_buffer_u8(w,h,b)  print_linear_buffer_internal_u8 (gstring,w,h,b)
+#define print_linear_buffer_float(w,h,b)  print_linear_buffer_internal_float (gstring,w,h,b)
+#define test_end()                return g_string_free (gstring, FALSE)
+
+
+static void print_buffer_internal (GString    *gstring,
+                                   GeglBuffer *buffer);
+static void
+print_linear_buffer_internal_float (GString    *gstring,
+                                    gint        width,
+                                    gint        height,
+                                    gfloat     *buf);
+static void
+print_linear_buffer_internal_u8    (GString    *gstring,
+                                    gint        width,
+                                    gint        height,
+                                    guchar     *buf);
+
+static void checkerboard          (GeglBuffer *buffer,
+                                   gint        cellsize,
+                                   gfloat      val1,
+                                   gfloat      val2);
+
+static void fill                  (GeglBuffer *buffer,
+                                   gfloat      value);
+
+static void vgrad                 (GeglBuffer *buffer);
+static void hgrad                 (GeglBuffer *buffer);
+
+static void rectangle             (GeglBuffer *buffer,
+                                   gint        x,
+                                   gint        y,
+                                   gint        width,
+                                   gint        height,
+                                   gfloat      value);
+
+static void fill_rect (GeglBuffer          *buffer,
+                       const GeglRectangle *roi,
+                       gfloat               value
+                       );
+
+
+
+/***********************************************************************/
+/**************************************************************************/
+
+static void
+print_linear_buffer_internal_float (GString    *gstring,
+                                    gint        width,
+                                    gint        height,
+                                    gfloat     *buf)
+{
+  gchar *scale[]={" ", "░", "▒", "▓", "█", "█"};
+  gint x,y;
+  print (("▛"));
+  for (x=0;x<width;x++)
+    print (("▀"));
+  print (("▜\n"));
+  for (y=0;y<height;y++)
+    {
+      print (("▌"));
+      for (x=0;x<width;x++)
+        {
+          gint val = floor ( buf[y*width+x] * 4 + 0.5);
+          if (val>4)
+            val=4;
+          if (val<0)
+            val=0;
+          print (("%s", scale[val]));
+        }
+      print (("▐\n"));
+    }
+  print (("▙"));
+  for (x=0;x<width;x++)
+    print (("▄"));
+  print (("▟\n"));
+}
+
+static void
+print_linear_buffer_internal_u8 (GString    *gstring,
+                                 gint        width,
+                                 gint        height,
+                                 guchar     *buf)
+{
+  gchar *scale[]={" ", "░", "▒", "▓", "█"};
+  gint x,y;
+  print (("▛"));
+  for (x=0;x<width;x++)
+    print (("▀"));
+  print (("▜\n"));
+  for (y=0;y<height;y++)
+    {
+      print (("▌"));
+      for (x=0;x<width;x++)
+        print (("%s", scale[ (gint)floor ( buf[y*width+x]/256.0 * 4 + 0.5)]));
+      print (("▐\n"));
+    }
+  print (("▙"));
+  for (x=0;x<width;x++)
+    print (("▄"));
+  print (("▟\n"));
+}
+
+
+static void
+print_buffer_internal (GString    *gstring,
+                       GeglBuffer *buffer)
+{
+  gfloat *buf;
+  gint width, height, x0, y0;
+  g_object_get (buffer, "x", &x0,
+                        "y", &y0,
+                        "width", &width,
+                        "height", &height,
+                        NULL);
+  buf = g_malloc (width*height*sizeof(gfloat));
+  gegl_buffer_get (buffer, NULL, 1.0, babl_format ("Y float"), buf, 0,
+                   GEGL_ABYSS_NONE);
+  print_linear_buffer_internal_float (gstring, width, height, buf);
+  g_free (buf);
+}
+
+static void
+fill (GeglBuffer *buffer,
+      gfloat      value)
+{
+  gfloat *buf;
+  gint x,y;
+  gint i;
+  gint width, height, x0, y0;
+  g_object_get (buffer, "x", &x0,
+                        "y", &y0,
+                        "width", &width,
+                        "height", &height,
+                        NULL);
+  buf = g_malloc (width*height*sizeof(gfloat));
+  gegl_buffer_get (buffer, NULL, 1.0, babl_format ("Y float"), buf, 0,
+                   GEGL_ABYSS_NONE);
+
+  i=0;
+  for (y=0;y<height;y++)
+    {
+      for (x=0;x<width;x++)
+        {
+          buf[i++]=value;
+        }
+    }
+  gegl_buffer_set (buffer, NULL, 0, babl_format ("Y float"), buf, GEGL_AUTO_ROWSTRIDE);
+  g_free (buf);
+}
+
+static void checkerboard          (GeglBuffer *buffer,
+                                   gint        cellsize,
+                                   gfloat      val1,
+                                   gfloat      val2)
+{
+  gfloat *buf;
+  gint x,y;
+  gint i;
+  gint width, height, x0, y0;
+  g_object_get (buffer, "x", &x0,
+                        "y", &y0,
+                        "width", &width,
+                        "height", &height,
+                        NULL);
+  buf = g_malloc (width*height*sizeof(gfloat));
+  gegl_buffer_get (buffer, NULL, 1.0, babl_format ("Y float"), buf, 0,
+                   GEGL_ABYSS_NONE);
+
+  i=0;
+  for (y=0;y<height;y++)
+    {
+      for (x=0;x<width;x++)
+        {
+          gfloat val=val1;
+          if ( (x/cellsize) % 2)
+            {
+              if ( (y/cellsize) % 2)
+                {
+                  val=val2;
+                }
+            }
+          else
+            {
+              if ( (y/cellsize) % 2 == 0)
+                {
+                  val=val2;
+                }
+            }
+          buf[i++]= val;
+        }
+    }
+
+  gegl_buffer_set (buffer, NULL, 0, babl_format ("Y float"), buf, GEGL_AUTO_ROWSTRIDE);
+
+  g_free (buf);
+}
+
+static void vgrad (GeglBuffer *buffer)
+{
+  gfloat *buf;
+  gint x,y;
+  gint i;
+  gint width, height, x0, y0;
+  g_object_get (buffer, "x", &x0,
+                        "y", &y0,
+                        "width", &width,
+                        "height", &height,
+                        NULL);
+  buf = g_malloc (width*height*sizeof(gfloat));
+  gegl_buffer_get (buffer, NULL, 1.0, babl_format ("Y float"), buf, 0,
+                   GEGL_ABYSS_NONE);
+
+  i=0;
+  for (y=0;y<height;y++)
+    {
+      for (x=0;x<width;x++)
+        {
+          buf[i++]= (1.0*y)/height;
+        }
+    }
+  gegl_buffer_set (buffer, NULL, 0, babl_format ("Y float"), buf, GEGL_AUTO_ROWSTRIDE);
+  g_free (buf);
+}
+
+static void hgrad (GeglBuffer *buffer)
+{
+  gfloat *buf;
+  gint x,y;
+  gint i;
+  gint width, height, x0, y0;
+  g_object_get (buffer, "x", &x0,
+                        "y", &y0,
+                        "width", &width,
+                        "height", &height,
+                        NULL);
+  buf = g_malloc (width*height*sizeof(gfloat));
+  gegl_buffer_get (buffer, NULL, 1.0, babl_format ("Y float"), buf, 0,
+                   GEGL_ABYSS_NONE);
+
+  i=0;
+  for (y=0;y<height;y++)
+    {
+      for (x=0;x<width;x++)
+        {
+          buf[i++]= (1.0*x)/width;
+        }
+    }
+  gegl_buffer_set (buffer, NULL, 0, babl_format ("Y float"), buf, GEGL_AUTO_ROWSTRIDE);
+  g_free (buf);
+}
+
+static void fill_rect (GeglBuffer          *buffer,
+                       const GeglRectangle *roi,
+                       gfloat               value
+                       )
+{
+  GeglBufferIterator *gi;
+  gi = gegl_buffer_iterator_new (buffer, roi, 0, NULL,
+                                 GEGL_ACCESS_WRITE, GEGL_ABYSS_NONE, 1);
+  while (gegl_buffer_iterator_next (gi))
+    {
+      gfloat *buf = gi->items[0].data;
+      gint    i;
+      for (i=0; i<gi->length; i++)
+        {
+          buf[i]=value;
+        }
+    }
+}
+
+void rectangle (GeglBuffer *buffer,
+                gint        x,
+                gint        y,
+                gint        width,
+                gint        height,
+                gfloat      value)
+{
+  GeglBuffer *sub_buf;
+  GeglRectangle rect={x,y,width,height};
+
+  sub_buf = gegl_buffer_create_sub_buffer (buffer, &rect);
+  fill (sub_buf, value);
+  g_object_unref (sub_buf);
+}
+
+
+
+gchar* test_name = TEST_NAME;
+
+#define TEST() gchar * test_function (void)
+TEST();
+#include TEST_INCLUDE
+
+gint main (gint argc, gchar **argv)
+{
+  gint i;
+  gegl_init (&argc, &argv);
+
+  for (i=0; i < 1; i++)
+    {
+      gchar *ret;
+
+      if (argc > 1)
+        {
+          /* handle any extra commandline options as a list of tests to
+           * run and output to standard output
+           */
+          gint j;
+          for (j=1;j<argc;j++)
+            {
+              if (g_str_equal (argv[j], test_name))
+                {
+                  ret=test_function();
+                  printf ("%s", ret);
+                  g_free (ret);
+                }
+            }
+        }
+      else
+        {
+          gchar output_file[1024];
+          printf ("%s ", test_name);
+          ret=test_function();
+          sprintf (output_file, "output/%s.buf", test_name);
+          g_file_set_contents (output_file, ret, -1, NULL);
+          g_free (ret);
+        }
+    }
+
+  gegl_exit ();
+  return 0;
+}
diff --git a/tests/buffer/buffer-tests-run.sh b/tests/buffer/buffer-tests-run.sh
new file mode 100644
index 000000000..e82fbd072
--- /dev/null
+++ b/tests/buffer/buffer-tests-run.sh
@@ -0,0 +1,11 @@
+#!/bin/bash -e
+
+mkdir -p output
+
+test_name=$1
+test_exe=$2
+${test_exe}
+
+LC_ALL=C diff \
+    "${REFERENCE_DIR}/${test_name}.buf" \
+              "output/${test_name}.buf"
diff --git a/tests/buffer/meson.build b/tests/buffer/meson.build
new file mode 100644
index 000000000..a803d379c
--- /dev/null
+++ b/tests/buffer/meson.build
@@ -0,0 +1,106 @@
+
+buffer_tests_list = [
+  'blank',
+  'buffer_copy',
+  'buffer_copy_abyss',
+  'buffer_copy_lower_left',
+  'buffer_copy_lower_right',
+  'buffer_copy_self',
+  'buffer_copy_upper_left',
+  'buffer_copy_upper_right',
+  'buffer_dup_copy1',
+  'buffer_dup_copy2',
+  'buffer_dup_delete1',
+  'buffer_dup_delete2',
+  'buffer_dup_write1',
+  'buffer_dup_write2',
+  'buffer_dup_write3',
+  'buffer_dup_write4',
+  'buffer_iterator1',
+  'buffer_iterator1sub',
+  'buffer_iterator2',
+  'buffer_iterator2sub',
+  'buffer_iterator3',
+  'buffer_iterator3sub',
+  'buffer_iterator4',
+  'buffer_iterator4sub',
+  'buffer_linear_copy',
+  'buffer_linear_iter',
+  'buffer_linear_iter2',
+  'buffer_multcopy',
+  'buffer_set_pattern',
+  'buffer_shift_diagonal',
+  'buffer_shift_horizontal',
+  'buffer_shift_vertical',
+  'checks',
+  'disabled_abyss',
+  'dup_linear_from_data',
+  'get_abyss_black',
+  'get_abyss_clamp',
+  'get_abyss_empty_buffer',
+  'get_abyss_loop',
+  'get_abyss_none',
+  'get_abyss_white',
+  'get_buffer_scaled',
+  'get_buffer_scaled2',
+  'get_shifted',
+  'gray',
+  'grow_extent',
+  'linear_from_data',
+  'linear_from_data_rows',
+  'linear_modify',
+  'linear_new',
+  'linear_proxy_modify',
+  'linear_shift',
+  'mipmap_iterator',
+  'mipmap_iterator2',
+  'mipmap_sampler',
+  'mipmap_set',
+  'mipmap_set2',
+  'rect',
+  'sample',
+  'save_small_roi',
+  'sub_rect_fills_and_gets',
+  'sub_sub_fill',
+  'sub_sub_fill2',
+  'vertical_gradient',
+]
+
+
+buffer_test_run_script = find_program('buffer-tests-run.sh')
+
+foreach test_name : buffer_tests_list
+
+  test_exe = executable(test_name,
+    'buffer-test-wrapper.c',
+    include_directories: [ rootInclude, geglInclude, ],
+    link_with: [
+      gegl_lib,
+    ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+      math,
+    ],
+    c_args: [
+      '-Wno-unused-function',
+      '-DTEST_NAME="@0@"'.format(test_name),
+      '-DTEST_INCLUDE="@0@"'.format('tests' / test_name + '.c'),
+    ],
+  )
+
+  test(test_name,
+  buffer_test_run_script,
+    args: [ test_name, test_exe.full_path(), ],
+    env: [
+      'GEGL_PATH=' + meson.build_root() / 'operations',
+      'GEGL_SWAP=RAM',
+      'REFERENCE_DIR=' + meson.source_root() / 'tests' / 'buffer' / 'reference',
+    ],
+    workdir: meson.current_build_dir(),
+    suite: 'buffers',
+    is_parallel: false,
+  )
+
+endforeach
diff --git a/tests/compositions/meson.build b/tests/compositions/meson.build
new file mode 100644
index 000000000..268eda4ec
--- /dev/null
+++ b/tests/compositions/meson.build
@@ -0,0 +1,100 @@
+compositions_tests = files(
+  'affine-with-negative.xml',
+  'alien-map.xml',
+  'apply-lens.xml',
+  'apply-lens3.xml',
+  'bump-map.xml',
+  'checkerboard.xml',
+  'clones.xml',
+  'color-enhance.xml',
+  'color-exchange.xml',
+  'color-to-alpha.xml',
+  'colors.xml',
+  'composite-transform.xml',
+  'contrast-curve.xml',
+  'convolve1.xml',
+  'convolve2.xml',
+  'edge.xml',
+  'fattal02.xml',
+  'gamma.xml',
+  'gegl.xml',
+  'grey-json.xml',
+  'hdr-color.xml',
+  'illusion.xml',
+  'image-compare.xml',
+  'lens-flare.xml',
+  'mantiuk06.xml',
+  'noise-cell.xml',
+  'noise-hurl.xml',
+  'noise-simplex.xml',
+  'noise-solid.xml',
+  'posterize.xml',
+  'rectangles.xml',
+  'red-eye-removal.xml',
+  'reflect.xml',
+  'reflect2.xml',
+  'reinhard05.xml',
+  'rgb-params.xml',
+  'rotate-on-center.xml',
+  'rotate.xml',
+  'scale-size-keepaspect.xml',
+  'simple-scale.xml',
+  'sinus.xml',
+  'stretch-contrast.xml',
+  'supernova.xml',
+  'transform.xml',
+  'weighted-blend.xml',
+)
+#run-matting-global.xml.sh
+#run-matting-global-big.xml.sh
+
+compositions_tests_without_opencl = files(
+  'color-reduction.xml',
+  'jpg-load-datauri.xml',
+  'pnm-ascii-load.xml',
+  'pnm-raw-load.xml',
+  'rgbe-load.xml',
+  'rgbe-save.xml',
+  'shift.xml',
+  'stretch-contrast-hsv.xml',
+  'tiff-load.xml',
+  'tile.xml',
+)
+
+
+if jasper.found()
+  compositions_tests_without_opencl += 'jp2-load.xml'
+endif
+if libumfpack.found()
+ # compositions_tests += 'matting-levin.xml'
+endif
+
+run_compositions = find_program('run-compositions.py')
+
+if false
+test('compositions_with_opencl',
+  run_compositions,
+  args: [
+    '--build-dir='+ meson.build_root(),
+    '--src-dir='  + meson.source_root(),
+    '--xml-dir='  + meson.current_source_dir(),
+    compositions_tests,
+  ],
+  is_parallel: false,
+  timeout: 120,
+  suite: 'compositions',
+)
+endif
+
+test('compositions_without_opencl',
+  run_compositions,
+  args: [
+    '--build-dir='+ meson.build_root(),
+    '--src-dir='  + meson.source_root(),
+    '--xml-dir='  + meson.current_source_dir(),
+    '--without-opencl',
+    compositions_tests_without_opencl,
+  ],
+  suite: 'compositions',
+  is_parallel: false,
+)
diff --git a/tests/ff-load-save/meson.build b/tests/ff-load-save/meson.build
new file mode 100644
index 000000000..66bc64ee9
--- /dev/null
+++ b/tests/ff-load-save/meson.build
@@ -0,0 +1,13 @@
+if gexiv2.found()
+
+  test('ff-load-save',
+    find_program('tests_ff_load_save.sh'),
+    env: [ 'MESON_BUILD_ROOT=' + meson.build_root() ],
+    workdir: meson.current_build_dir(),
+    suite: 'ff-load-save',
+    protocol: 'tap',
+    timeout: 120,
+    is_parallel: false,
+  )
+
+endif
diff --git a/tests/ff-load-save/tests_ff_load_save.sh b/tests/ff-load-save/tests_ff_load_save.sh
new file mode 100755
index 000000000..64efea912
--- /dev/null
+++ b/tests/ff-load-save/tests_ff_load_save.sh
@@ -0,0 +1,38 @@
+#!/usr/bin/env bash
+
+export GEGL_PATH="${MESON_BUILD_ROOT}/operations"
+
+FrameCounter="${MESON_BUILD_ROOT}/examples/frame-counter"
+GeglVideo="${MESON_BUILD_ROOT}/examples/gegl-video"
+
+testcount=0
+
+test() {
+  ((testcount++))
+  if "$@" 1> /dev/null; then
+    echo "ok ${testcount}"
+  else
+    echo "not ok ${testcount} - $@"
+  fi
+}
+endtests() {
+  echo "1..${testcount}"
+}
+
+# Videos
+
+test "${FrameCounter}" --video-codec mpeg4 --video-bit-rate 128            mpeg4-128kb.avi
+test "${FrameCounter}" --video-codec mpeg4 --video-bit-rate 512            mpeg4-512kb.avi
+test "${FrameCounter}" --video-codec mpeg4 --video-bit-rate 512            512kb.mp4
+test "${FrameCounter}" --video-codec mpeg4 --video-bit-rate 128            128kb.mp4
+test "${FrameCounter}" --video-codec mpeg4 --video-bit-rate 128 --fps 12   128kb-12fps.mp4
+test "${FrameCounter}" --video-codec mpeg4 --video-bit-rate 128 --fps 100  128kb-100fps.mp4
+test "${FrameCounter}"                     --video-bit-rate 512 --fps 28   512kb-28fps.ogv
+
+# Images
+
+for a in *.avi *.mp4 *.ogv ; do
+  test "${GeglVideo}" $a -s 74 -e 74 -of $a-
+done
+
+endtests
diff --git a/tests/meson.build b/tests/meson.build
new file mode 100644
index 000000000..22a2583b5
--- /dev/null
+++ b/tests/meson.build
@@ -0,0 +1,12 @@
+subdir('simple')
+subdir('mipmap')
+
+if false
+  subdir('opencl')
+endif
+
+subdir('python')
+subdir('xml')
+subdir('ff-load-save')
+subdir('compositions')
+subdir('buffer')
diff --git a/tests/mipmap/meson.build b/tests/mipmap/meson.build
new file mode 100644
index 000000000..1de47a1c7
--- /dev/null
+++ b/tests/mipmap/meson.build
@@ -0,0 +1,23 @@
+
+testnames = [
+  'invert-crop',
+  'invert',
+  'rotate-crop',
+  'rotate',
+  'unsharp-crop',
+  'unsharp',
+]
+
+foreach testname : testnames
+  test(testname.underscorify(),
+    find_program(testname + '.sh'),
+    env: [
+      'ABS_TOP_BUILDDIR=' + meson.build_root(),
+      'ABS_TOP_SRCDIR='   + meson.source_root(),
+      'GEGL_SWAP=RAM',
+      'GEGL_PATH=' + meson.build_root() / 'operations',
+    ],
+    suite: 'mipmap',
+    is_parallel: false,
+  )
+endforeach
diff --git a/tests/opencl/meson.build b/tests/opencl/meson.build
new file mode 100644
index 000000000..f73648561
--- /dev/null
+++ b/tests/opencl/meson.build
@@ -0,0 +1,44 @@
+
+test_list = [
+  'bilateral-filter',
+  'box-blur',
+  'brightness-contrast',
+  'color-temperature',
+  'edge-sobel',
+  'gaussian-blur',
+  'invert-linear',
+  'levels',
+  'mono-mixer',
+  'motion-blur',
+  'noise-reduction',
+  'oilify',
+  'opacity',
+  'pixelize',
+  'snn-mean',
+  'svg-src-over',
+  'threshold',
+  'value-invert',
+  'vignette',
+]
+
+opencl_test = find_program('opencl_test.sh')
+foreach test_name: test_list
+  xml_file = meson.current_source_dir() / test_name + '.xml'
+  out_dir  = meson.current_build_dir()
+  out_name = test_name + '.png'
+
+  test(test_name,
+    opencl_test,
+    env: [
+      'ABS_BUILDDIR='+ out_dir,
+      'GEGL_BIN='+ gegl_bin.full_path(),
+      'GEGL_IMGCMP_BIN='+ gegl_imgcmp.full_path(),
+      'XML_FILE='+ xml_file,
+      'OUT_FILE='+ out_name,
+      'GEGL_SWAP=RAM',
+      'GEGL_PATH='+ meson.build_root() / 'operations',
+    ],
+    suite: 'opencl',
+    is_parallel: false,
+  )
+endforeach
diff --git a/tests/opencl/opencl_test.sh b/tests/opencl/opencl_test.sh
new file mode 100644
index 000000000..724e58128
--- /dev/null
+++ b/tests/opencl/opencl_test.sh
@@ -0,0 +1,13 @@
+#!/bin/bash -e
+outdir_normal="${ABS_BUILDDIR}/output"
+outdir_opencl="${ABS_BUILDDIR}/output_cl"
+
+mkdir -p "${outdir_normal}"
+mkdir -p "${outdir_opencl}"
+
+GEGL_USE_OPENCL=no  "${GEGL_BIN}" "${XML_FILE}" -o "${outdir_normal}/${OUT_FILE}"
+GEGL_USE_OPENCL=yes "${GEGL_BIN}" "${XML_FILE}" -o "${outdir_opencl}/${OUT_FILE}"
+
+"${GEGL_IMGCMP_BIN}" \
+    "${outdir_normal}/${OUT_FILE}" \
+    "${outdir_opencl}/${OUT_FILE}"
diff --git a/tests/python/meson.build b/tests/python/meson.build
new file mode 100644
index 000000000..f0f9c5868
--- /dev/null
+++ b/tests/python/meson.build
@@ -0,0 +1,25 @@
+
+testnames = [
+  'gegl-buffer',
+  'gegl-color',
+  'gegl-format',
+  'gegl-node',
+  'gegl',
+]
+
+if python.found() and pygobject_found
+  foreach testname : testnames
+    testfile = 'test-' + testname + '.py'
+    test(testname,
+      find_program(testfile),
+      env: [
+        'LD_LIBRARY_PATH=' + meson.build_root() / 'gegl' + ':$LD_LIBRARY_PATH',
+        'GI_TYPELIB_PATH=' + meson.build_root() / 'gegl' + ':$GI_TYPELIB_PATH',
+        'GEGL_PATH='+ meson.build_root() / 'operations',
+        'GEGL_SWAP=RAM',
+      ],
+      suite: 'python',
+      is_parallel: false,
+    )
+  endforeach
+endif
diff --git a/tests/simple/meson.build b/tests/simple/meson.build
new file mode 100644
index 000000000..2c735d80a
--- /dev/null
+++ b/tests/simple/meson.build
@@ -0,0 +1,60 @@
+testnames = [
+  'backend-file',
+  'buffer-cast',
+  'buffer-changes',
+  'buffer-extract',
+  'buffer-hot-tile',
+  'buffer-sharing',
+  'buffer-tile-voiding',
+  'change-processor-rect',
+  'color-op',
+  'convert-format',
+  'empty-tile',
+  'format-sensing',
+  'gegl-color',
+  'gegl-rectangle',
+  'gegl-tile',
+  'image-compare',
+  'license-check',
+  'misc',
+  'node-connections',
+  'node-exponential',
+  'node-passthrough',
+  'node-properties',
+  'object-forked',
+  'opencl-colors',
+  'path',
+  'proxynop-processing',
+  'scaled-blit',
+  'serialize',
+  'svg-abyss',
+]
+
+foreach testname : testnames
+
+  test_exe = executable(testname,
+    'test-' + testname + '.c',
+    include_directories: [ rootInclude, geglInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+    ],
+    link_with: [
+      gegl_lib,
+    ],
+    install: false,
+  )
+  test(testname,
+    test_exe,
+    env: [
+      'ABS_TOP_BUILDDIR=' + meson.build_root(),
+      'ABS_TOP_SRCDIR='   + meson.source_root(),
+      'GEGL_SWAP=RAM',
+      'GEGL_PATH='+ meson.build_root() / 'operations',
+    ],
+    suite: 'simple',
+    is_parallel: false,
+  )
+
+endforeach
diff --git a/tests/xml/meson.build b/tests/xml/meson.build
new file mode 100644
index 000000000..38d39ba60
--- /dev/null
+++ b/tests/xml/meson.build
@@ -0,0 +1,34 @@
+
+xml_test_names = [
+  'test-load',
+  'test-roundtrip',
+  'test-save',
+]
+
+
+foreach xml_test_name : xml_test_names
+  xml_test = executable(xml_test_name,
+    xml_test_name + '.c',
+    include_directories: [ rootInclude, geglInclude, ],
+    dependencies: [
+      babl,
+      glib,
+      gobject,
+    ],
+    link_with: [
+      gegl_lib,
+    ]
+  )
+  test(xml_test_name.underscorify(),
+    xml_test,
+    env: [
+      'ABS_TOP_BUILDDIR=' + meson.build_root(),
+      'ABS_TOP_SRCDIR='   + meson.source_root(),
+      'GEGL_SWAP=RAM',
+      'GEGL_PATH=' + meson.build_root() / 'operations',
+    ],
+    suite: 'xml',
+    is_parallel: false,
+  )
+
+endforeach
diff --git a/tools/meson.build b/tools/meson.build
new file mode 100644
index 000000000..74fd37fcd
--- /dev/null
+++ b/tools/meson.build
@@ -0,0 +1,80 @@
+
+tools_deps = [
+  babl,
+  glib,
+  gobject,
+]
+
+tools_c_args = [
+  '-DTOP_SRCDIR="' + meson.source_root() + '"',
+]
+
+detect_opencl = executable(
+  'detect_opencl',
+  'detect_opencl.c',
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [ tools_deps, ],
+  link_with: [ gegl_lib, ],
+  c_args: [ tools_c_args ],
+  install: false,
+)
+gegl_imgcmp = executable(
+  'gegl-imgcmp',
+  'gegl-imgcmp.c',
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [ tools_deps, ],
+  link_with: [ gegl_lib, ],
+  c_args: [ tools_c_args ],
+  install: true,
+)
+gegl_tester = executable(
+  'gegl-tester',
+  'gegl-tester.c',
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [ tools_deps, ],
+  link_with: [ gegl_lib, ],
+  c_args: [ tools_c_args ],
+  install: false,
+)
+introspect = executable(
+  'introspect',
+  'introspect.c',
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [ tools_deps, ],
+  link_with: [ gegl_lib, ],
+  c_args: [ tools_c_args ],
+  install: false,
+)
+operation_reference = executable(
+  'operation_reference',
+  'operation_reference.c',
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [ tools_deps, ],
+  link_with: [ gegl_lib, ],
+  c_args: [ tools_c_args ],
+  install: false,
+)
+operations_html = executable(
+  'operations_html',
+  'operations_html.c',
+  include_directories: [ rootInclude, geglInclude, ],
+  dependencies: [ tools_deps, ],
+  link_with: [ gegl_lib, ],
+  c_args: [ tools_c_args ],
+  install: false,
+)
+
+if gexiv2.found()
+  exp_combine = executable(
+    'exp_combine',
+    'exp_combine.cpp',
+    include_directories: [ rootInclude, geglInclude, ],
+    dependencies: [ tools_deps, gexiv2, ],
+    link_with: [ gegl_lib, ],
+    c_args: [ tools_c_args ],
+    install: false,
+  )
+endif
+
+gen_loader = find_program('gen-loader.sh')
+gobj2dot   = find_program('gobj2dot.rb')
diff --git a/tools/operations_html.c b/tools/operations_html.c
index e5f2c37ea..84cfd919b 100644
--- a/tools/operations_html.c
+++ b/tools/operations_html.c
@@ -61,7 +61,7 @@ operation_to_image_path (const gchar *op_name)
 
   g_strdelimit (cleaned, ":", '-');
   filename = g_strconcat (cleaned, ".png", NULL);
-  output_path = g_build_path (G_DIR_SEPARATOR_S, "images", filename, NULL);
+  output_path = g_strdup (filename); //build_path (G_DIR_SEPARATOR_S, "images", filename, NULL);
 
   g_free (cleaned);
   g_free (filename);
@@ -389,7 +389,7 @@ const gchar *css = "@import url(../gegl.css); .categories{ clear:right;text-alig
 const gchar *html_pre = "<html><head><title>%s</title>\n"
                           "<style>%s%s</style></head><body><div id='content'>\n";
 const gchar *html_post =
-            "<div style='margin-top:3em;'><a href='../index.html'><img src='../images/GEGL.png' alt='GEGL' 
style='height: 4.0em;float:left; padding-right:0.5em;'/></a> This page is part of the online GEGL 
Documentation, GEGL is a data flow based image processing library/framework, made to fuel <a 
href='https://www.gimp.org/'>GIMPs</a> high-bit depth non-destructive editing future.</div></body></html>\n";
+            "<div style='margin-top:3em;'><a href='index.html'><img src='../images/GEGL.png' alt='GEGL' 
style='height: 4.0em;float:left; padding-right:0.5em;'/></a> This page is part of the online GEGL 
Documentation, GEGL is a data flow based image processing library/framework, made to fuel <a 
href='https://www.gimp.org/'>GIMPs</a> high-bit depth non-destructive editing future.</div></body></html>\n";
 
 gint
 main (gint argc, gchar **argv)
@@ -481,7 +481,7 @@ main (gint argc, gchar **argv)
       if (klass->compat_name)
         g_string_append_printf (s, "<b>compat-op:</b>&nbsp;%s<br/>\n", klass->compat_name);
 
-      if(1){ // XXX: re-enable before push.. it takes a lot of time
+      if(0){ // XXX: re-enable before push.. it takes a lot of time
         gchar *commandline = g_strdup_printf (
             "sh -c \"(cd " TOP_SRCDIR ";cd ..;grep -r '\\\"%s\\\"' operations) | grep operations | grep -v 
'~:' | grep '\\\"name\\\"' | cut -f 1 -d ':'\"",
              name);
@@ -610,11 +610,11 @@ all:
 
       {
         char *image = operation_to_image_path (name);
-        if (!g_file_test (image, G_FILE_TEST_EXISTS))
-        {
-           g_free (image);
-           image = g_strdup ("images/gegl-ditto.png");
-        }
+        //if (!g_file_test (image, G_FILE_TEST_EXISTS))
+        //{
+        //   g_free (image);
+        //   image = g_strdup ("gegl-ditto.png");
+       // }
         g_string_append_printf (s, "<a href='%s.html' title='", name_dup);
         if (description)
           xml_escape_string (s, description);
@@ -686,11 +686,11 @@ all:
 
       {
         char *image = operation_to_image_path (name);
-        if (!g_file_test (image, G_FILE_TEST_EXISTS))
-        {
-           g_free (image);
-           image = g_strdup ("images/gegl-ditto.png");
-        }
+        //if (!g_file_test (image, G_FILE_TEST_EXISTS))
+       // {
+       //    g_free (image);
+       //    image = g_strdup ("gegl-ditto.png");
+       // }
         g_string_append_printf (s, "<a href='%s.html' title='", name_dup);
         if (description)
           xml_escape_string (s, description);
@@ -705,7 +705,7 @@ all:
         g_string_append_printf (s, "</div></body></html>");
 
       {
-        gchar *html_name = "index.html";
+        gchar *html_name = "op-index.html";
         g_print ("%s\n", html_name);
         g_file_set_contents (html_name, s->str, -1, NULL);
       }



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]