Skip to content

Instantly share code, notes, and snippets.

@roxlu
Created February 13, 2014 13:50
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 1 You must be signed in to fork a gist
  • Save roxlu/5caa9c726187997bc15d to your computer and use it in GitHub Desktop.
Save roxlu/5caa9c726187997bc15d to your computer and use it in GitHub Desktop.
Triangle wrapper
cmake_minimum_required(VERSION 2.8)
include(${CMAKE_CURRENT_LIST_DIR}/Triplet.cmake)
set(app app)
set(sd ${CMAKE_CURRENT_LIST_DIR}/../src/)
if(CMAKE_BUILD_TYPE STREQUAL Debug)
set(app "${app}_debug")
endif()
include_directories(
${CMAKE_CURRENT_LIST_DIR}/../src
${CMAKE_CURRENT_LIST_DIR}/../include
${extern_include_dir}
${CMAKE_CURRENT_LIST_DIR}/../shared/tinylib/src
)
set(app_sources
${sd}/main.cpp
${sd}/Triangulate.cpp
${sd}/Drawer.cpp
${sd}/triangle.c
)
add_definitions(-DTRILIBRARY -DSINGLE)
if(UNIX AND NOT APPLE)
list(APPEND app_sources ${extern_source_dir}/GLXW/glxw.c)
message(STATUS "Adding glxw.c for GL-function loading.")
message(${app_sources})
endif()
add_executable(${app} ${app_sources})
if(APPLE)
find_library(fr_corefoundation CoreFoundation)
find_library(fr_cocoa Cocoa)
find_library(fr_opengl OpenGL)
find_library(fr_iokit IOKit)
find_library(fr_corevideo CoreVideo)
find_library(fr_opencl OpenCL)
set(app_libs
${extern_lib_dir}/libglfw3.a
${extern_lib_dir}/libuv.a
${extern_lib_dir}/libpng.a
${extern_lib_dir}/libremoxly.a
# For triangulation
# ${extern_lib_dir}/libCGAL.a
# ${extern_lib_dir}/libCGAL_Core.a
# ${extern_lib_dir}/libgmp.a
# ${extern_lib_dir}/libmpfr.a
# ${extern_lib_dir}/libboost_system.a
# ${extern_lib_dir}/libboost_thread.a
# ${extern_lib_dir}/libttl.a
${fr_corefoundation}
${fr_cocoa}
${fr_opengl}
${fr_iokit}
${fr_corevideo}
${fr_opencl}
-lz
)
endif()
target_link_libraries(${app} ${app_libs})
if(PACKAGE)
add_executable(${app}_pkg MACOSX_BUNDLE ${app_sources})
target_link_libraries(${app}_pkg ${app_libs})
install(TARGETS ${app}_pkg DESTINATION bin)
endif()
install(TARGETS ${app} DESTINATION bin)
#include "Drawer.h"
Drawer::Drawer()
:vao(0)
,vbo(0)
,prog(0)
,vert(0)
,frag(0)
,bytes_allocated(0)
,needs_update(false)
{
glGenVertexArrays(1, &vao);
glBindVertexArray(vao);
glGenBuffers(1, &vbo);
glBindBuffer(GL_ARRAY_BUFFER, vbo);
glEnableVertexAttribArray(0);
glVertexAttribPointer(0, 2, GL_FLOAT, GL_FALSE, sizeof(vec2), (GLvoid*)0);
const char* atts[] = { "a_pos" } ;
vert = rx_create_shader(GL_VERTEX_SHADER, DRAWER_VS);
frag = rx_create_shader(GL_FRAGMENT_SHADER, DRAWER_FS);
prog = rx_create_program_with_attribs(vert, frag, 1, atts);
mat4 pm;
pm.ortho(0, 1280, 720, 0, 0.0, 100.0f);
glUseProgram(prog);
glUniformMatrix4fv(glGetUniformLocation(prog, "u_pm"), 1, GL_FALSE, pm.ptr());
}
void Drawer::update() {
if(!points.size()) {
return;
}
if(!needs_update) {
return;
}
if(!points.size()) {
return;
}
glBindBuffer(GL_ARRAY_BUFFER, vbo);
size_t needed = points.size() * sizeof(vec2);
if(needed > bytes_allocated) {
glBufferData(GL_ARRAY_BUFFER, needed, points[0].ptr(), GL_STREAM_DRAW);
bytes_allocated = needed;
}
else {
glBufferSubData(GL_ARRAY_BUFFER, 0, needed, points[0].ptr());
}
needs_update = false;
}
void Drawer::draw() {
if(!points.size()) {
return;
}
update();
if(points.size()) {
glPointSize(0.5f);
glUseProgram(prog);
glBindVertexArray(vao);
glDrawArrays(GL_POINTS, 0, points.size());
glPolygonMode(GL_FRONT_AND_BACK, GL_LINE);
glDrawArrays(GL_TRIANGLES, 0, points.size());
glPolygonMode(GL_FRONT_AND_BACK, GL_FILL);
}
}
void Drawer::addPoint(float mx, float my) {
points.push_back(vec2(mx, my));
needs_update = true;
}
#ifndef DRAWER_H
#define DRAWER_H
#include <stdio.h>
#include <stdlib.h>
#define ROXLU_USE_OPENGL
#define ROXLU_USE_MATH
#include <tinylib.h>
static const char* DRAWER_VS = ""
"#version 150\n"
"uniform mat4 u_pm;"
"in vec4 a_pos;"
"void main() {"
" gl_Position = u_pm * a_pos; "
"}"
"";
static const char* DRAWER_FS = ""
"#version 150\n"
"out vec4 fragcolor;"
"void main() {"
" fragcolor = vec4(1.0, 0.0, 0.0, 1.0);"
"}"
"";
class Drawer {
public:
Drawer();
void update();
void draw();
void addPoint(float mx, float my);
void clear();
public:
/* drawing */
GLuint vao;
GLuint vbo;
GLuint prog;
GLuint vert;
GLuint frag;
std::vector<vec2> points;
size_t bytes_allocated;
bool needs_update;
};
inline void Drawer::clear() {
points.clear();
needs_update = true;
}
#endif
/*
BASIC GLFW + GLXW WINDOW AND OPENGL SETUP
------------------------------------------
See https://gist.github.com/roxlu/6698180 for the latest version of the example.
*/
#include <stdlib.h>
#include <stdio.h>
#if defined(__linux) || defined(_WIN32)
# include <GLXW/glxw.h>
#endif
#define GLFW_INCLUDE_GLCOREARB
#include <GLFW/glfw3.h>
#include "Triangulate.h"
#include "Drawer.h"
Drawer* drawer = NULL;
rx::Triangulate* tri = NULL;
bool needs_update = false;
std::vector<vec2> points;
std::vector<vec2> triangulated_points;
#define ROXLU_USE_MATH
#define ROXLU_USE_OPENGL
#define ROXLU_IMPLEMENTATION
#include <tinylib.h>
void button_callback(GLFWwindow* win, int bt, int action, int mods);
void cursor_callback(GLFWwindow* win, double x, double y);
void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods);
void char_callback(GLFWwindow* win, unsigned int key);
void error_callback(int err, const char* desc);
void resize_callback(GLFWwindow* window, int width, int height);
int main() {
glfwSetErrorCallback(error_callback);
if(!glfwInit()) {
printf("Error: cannot setup glfw.\n");
return false;
}
glfwWindowHint(GLFW_SAMPLES, 4);
glfwWindowHint(GLFW_CONTEXT_VERSION_MAJOR, 3);
glfwWindowHint(GLFW_CONTEXT_VERSION_MINOR, 2);
glfwWindowHint(GLFW_OPENGL_FORWARD_COMPAT, GL_TRUE);
glfwWindowHint(GLFW_OPENGL_PROFILE, GLFW_OPENGL_CORE_PROFILE);
GLFWwindow* win = NULL;
int w = 1280;
int h = 720;
win = glfwCreateWindow(w, h, "GLFW", NULL, NULL);
if(!win) {
glfwTerminate();
exit(EXIT_FAILURE);
}
glfwSetFramebufferSizeCallback(win, resize_callback);
glfwSetKeyCallback(win, key_callback);
glfwSetCharCallback(win, char_callback);
glfwSetCursorPosCallback(win, cursor_callback);
glfwSetMouseButtonCallback(win, button_callback);
glfwMakeContextCurrent(win);
glfwSwapInterval(1);
#if defined(__linux) || defined(_WIN32)
if(glxwInit() != 0) {
printf("Error: cannot initialize glxw.\n");
::exit(EXIT_FAILURE);
}
#endif
// ----------------------------------------------------------------
// THIS IS WHERE YOU START CALLING OPENGL FUNCTIONS, NOT EARLIER!!
// ----------------------------------------------------------------
drawer = new Drawer();
tri = new rx::Triangulate();
while(!glfwWindowShouldClose(win)) {
glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
if(needs_update && tri->size() >= 3) {
struct triangulateio out;
if(tri->triangulate("zpQ", out)) {
drawer->clear();
for(int i = 0; i < out.numberoftriangles; ++i) {
int a = out.trianglelist[i * 3 + 0];
int b = out.trianglelist[i * 3 + 1];
int c = out.trianglelist[i * 3 + 2];
vec2 pa(out.pointlist[(a * 2) + 0], out.pointlist[(a * 2) + 1]);
vec2 pb(out.pointlist[(b * 2) + 0], out.pointlist[(b * 2) + 1]);
vec2 pc(out.pointlist[(c * 2) + 0], out.pointlist[(c * 2) + 1]);
drawer->addPoint(pa.x, pa.y);
drawer->addPoint(pb.x, pb.y);
drawer->addPoint(pc.x, pc.y);
}
tri->free(out);
}
needs_update = false;
}
drawer->draw();
glfwSwapBuffers(win);
glfwPollEvents();
}
glfwTerminate();
return EXIT_SUCCESS;
}
void char_callback(GLFWwindow* win, unsigned int key) {
}
void key_callback(GLFWwindow* win, int key, int scancode, int action, int mods) {
if(action != GLFW_PRESS) {
return;
}
switch(key) {
case GLFW_KEY_ESCAPE: {
glfwSetWindowShouldClose(win, GL_TRUE);
break;
}
};
}
void resize_callback(GLFWwindow* window, int width, int height) {
}
void cursor_callback(GLFWwindow* win, double x, double y) {
}
void button_callback(GLFWwindow* win, int bt, int action, int mods) {
if(action == GLFW_PRESS || action == GLFW_REPEAT) {
double x,y;
glfwGetCursorPos(win, &x, &y);
if(tri) {
tri->add(x, y);
needs_update = true;
}
}
}
void error_callback(int err, const char* desc) {
printf("GLFW error: %s (%d)\n", desc, err);
}
#include <Triangulate.h>
#include <string>
#include <algorithm>
#include <stdlib.h>
#include <set>
namespace rx {
// ---------------------------------------------------------------
Triangulate::Triangulate() {
}
void Triangulate::add(float x, float y) {
segment_point p(x, y, in_points.size());
in_points.push_back(p);
}
bool Triangulate::triangulate(std::string opt, struct triangulateio& out) {
if(!in_points.size()) {
return false;
}
std::vector<segment_point> sorted_points;
std::vector<segment_point> clean_points;
// sort + unique will remove lots of points we don't want
std::sort(in_points.begin(), in_points.end(), lex_equal);
std::vector<segment_point>::iterator it_unique = std::unique(in_points.begin(), in_points.end(), is_equal);
std::copy(in_points.begin(), it_unique, std::back_inserter(sorted_points));
// now remove other points which are still to close
for(size_t i = 0; i < sorted_points.size(); ++i) {
bool can_add = true;
segment_point& a = sorted_points[i];
for(size_t j = 0; j < clean_points.size(); ++j) {
segment_point& b = clean_points[j];
float dx = a.x - b.x;
float dy = a.y - b.y;
float d = (dx * dx) + (dy * dy);
if(d < TRIANGULATE_MIN_DIST_SQ) {
can_add = false;
break;
}
}
if(can_add) {
clean_points.push_back(a);
}
}
// sort again, now on segment again.
std::sort(clean_points.begin(), clean_points.end(), segment_sort);
in_points.clear();
std::vector<tri_point> tri_points;
for(size_t i = 0; i < clean_points.size(); ++i) {
segment_point& p = clean_points[i];
tri_points.push_back(tri_point(p.x, p.y));
in_points.push_back(segment_point(p.x, p.y, in_points.size()));
}
if(tri_points.size() < 3) {
printf("Not enough points to triangulate.\n");
return false;
}
std::vector<int> edges;
for(size_t i = 0; i < tri_points.size() - 1; ++i) {
edges.push_back(i);
edges.push_back(i + 1);
}
edges.push_back(edges.back());
edges.push_back(0);
struct triangulateio in = { 0 } ;
// initialize a nice clean out list
out.pointlist = NULL;
out.pointattributelist = NULL;
out.pointmarkerlist = NULL;
out.numberofpoints = 0;
out.numberofpointattributes = 0;
out.trianglelist = NULL;
out.triangleattributelist = NULL;
out.trianglearealist = NULL;
out.neighborlist = NULL;
out.numberoftriangles = 0;
out.numberofcorners = 0;
out.numberoftriangleattributes = 0;
out.segmentlist = NULL;
out.segmentmarkerlist = NULL;
out.numberofsegments = 0;
out.holelist = NULL;
out.numberofholes = 0;
out.regionlist = NULL;
out.numberofregions = 0;
out.edgelist = NULL;
out.edgemarkerlist = NULL;
out.normlist = NULL;
out.numberofedges = 0;
out.pointlist = NULL;
out.pointmarkerlist = NULL;
in.pointlist = (float*)&tri_points[0].x;
in.numberofpoints = int(tri_points.size());
if(edges.size()) {
in.segmentlist = &edges.front();
in.numberofsegments = edges.size() / 2;
}
::triangulate((char*)opt.c_str(), &in, &out, NULL);
return true;
}
#define TRIANGULATE_FREE(e) { if (e) { ::free(e); e = NULL; } }
void Triangulate::free(struct triangulateio& out) {
TRIANGULATE_FREE(out.pointlist);
TRIANGULATE_FREE(out.pointmarkerlist);
TRIANGULATE_FREE(out.pointattributelist);
TRIANGULATE_FREE(out.trianglelist);
TRIANGULATE_FREE(out.triangleattributelist);
TRIANGULATE_FREE(out.trianglearealist);
TRIANGULATE_FREE(out.neighborlist);
TRIANGULATE_FREE(out.segmentlist);
TRIANGULATE_FREE(out.segmentmarkerlist);
TRIANGULATE_FREE(out.holelist);
TRIANGULATE_FREE(out.regionlist);
TRIANGULATE_FREE(out.edgelist);
TRIANGULATE_FREE(out.edgemarkerlist);
TRIANGULATE_FREE(out.normlist);
out.numberofpoints = 0;
out.numberofpointattributes = 0;
out.numberoftriangles = 0;
out.numberofcorners = 0;
out.numberoftriangleattributes = 0;
out.numberofsegments = 0;
out.numberofholes = 0;
out.numberofregions = 0;
out.numberofedges = 0;
}
} // namespace rx
/*
---------------------------------------------------------------------------------
oooo
`888
oooo d8b .ooooo. oooo ooo 888 oooo oooo
`888""8P d88' `88b `88b..8P' 888 `888 `888
888 888 888 Y888' 888 888 888
888 888 888 .o8"'88b 888 888 888
d888b `Y8bod8P' o88' 888o o888o `V88V"V8P'
www.roxlu.com
www.apollomedia.nl
www.twitter.com/roxlu
---------------------------------------------------------------------------------
*/
#ifndef ROXLU_TRIANGULATE_H
#define ROXLU_TRIANGULATE_H
/*
Triangulate
---------------------------------------
- ! ADD THESE TO YOUR PREPROCESSOR FLAGS:
-DTRILIBRARY
-DSINGLE
- ! AFTER CALLING `Triangulate::triangulate()` MAKE SURE TO CALL `Triangulate::free(out)` TO CLEAN MEMORY!
- ! TRIANGLE MAY CRASH, SEE THIS PAGE: http://www.cs.cmu.edu/~quake/triangle.trouble.html
WHEN YOU WANT TO TRIANGULATE A CONTOUR, A QUICK FIX WOULD BE TO NOT ADD EVERY
POINT, BUT E.G. EVERY 5TH ELEMENT.
<example>
Triangulate tri;
struct triangulateio out;
tri.triangulate("zpQ", out);
std::vector<vec2> vertices;
// Extract the triangles (you could use a GL element buffer
for(int i = 0; i < out.numberoftriangles; ++i) {
int a = out.trianglelist[i * 3 + 0];
int b = out.trianglelist[i * 3 + 1];
int c = out.trianglelist[i * 3 + 2];
vec2 pa(out.pointlist[(a * 2) + 0], out.pointlist[(a * 2) + 1]);
vec2 pb(out.pointlist[(b * 2) + 0], out.pointlist[(b * 2) + 1]);
vec2 pc(out.pointlist[(c * 2) + 0], out.pointlist[(c * 2) + 1]);
vertices.push_back(pa);
vertices.push_back(pb);
vertices.push_back(pc);
}
tri.free(out);
</example>
Very thin wrapper around the [Triangle library](http://www.cs.cmu.edu/~quake/triangle.html).
Important:
----------
After calling Triangualate::triangulate(out), make sure to call Triangulate::free(out)
to free up all allocated memory. Of course call Triangulate::free(out), after you've used
the data.
Usefull options
---------------
The triangulate() options expects a string with a couple of characters; where each character
defines how the triangulation is done. These are the ones I use most often:
- Q Suppress verbose output
- p Use this when you want to create "hand" like shapes instead; it will fit the contour basically
- a Imposes a maximum triangle area.
See: http://www.cs.cmu.edu/~quake/triangle.switch.html for more infor.
Examples:
---------
- tri.triangulate("zpQ", out); See: http://farm6.staticflickr.com/5504/11995424136_da15c9c991.jpg
- tri.triangulate("zpa100", out); See: http://farm6.staticflickr.com/5530/11994959684_43678280ea.jpg
- tri.triangulate("zpa10000", out); See: http://farm4.staticflickr.com/3814/11994889643_2d5275765e.jpg
- tri.triangulate("za100", out); See: http://farm4.staticflickr.com/3828/11994889833_05cf0984a6.jpg
Notes:
-----
You need to look into the source to figure out how to use the triangulation library in an application
as an library and not just the executable. Before you include the `triangle.h` file, you need to set some
#defines, so include like this:
````c++
extern "C" {
# define REAL float
# define ANSI_DECLARATORS
# define VOID void
# include "triangle.h"
}
````
Also add these to you compiler flags:
````
-DTRILIBRARY
-DSINGLE
````
References:
-----------
[0] http://www.cs.cmu.edu/~quake/triangle.html
[1] http://www.cs.cmu.edu/~quake/triangle.switch.html
[2] http://www.math.umbc.edu/~rouben/2011-01-math625/triangle-demo2.c
*/
extern "C" {
# define REAL float
# define ANSI_DECLARATORS
# define VOID void
# include "triangle.h"
}
#include <vector>
#include <string>
#ifndef TRIANGULATE_MIN_DIST
# define TRIANGULATE_MIN_DIST 10
#endif
#define TRIANGULATE_MIN_DIST_SQ (TRIANGULATE_MIN_DIST * TRIANGULATE_MIN_DIST)
namespace rx {
// ----------------------------------------------------
struct segment_point {
segment_point(float x, float y, size_t dx) : x(x), y(y), dx(dx) { }
float x;
float y;
size_t dx;
};
struct tri_point {
tri_point(float x, float y) : x(x), y(y) { }
float x;
float y;
};
bool is_equal(const segment_point& p1, const segment_point& p2);
bool lex_equal(const segment_point& p1, const segment_point& p2);
bool segment_sort(const segment_point& p1, const segment_point& p2);
// ----------------------------------------------------
class Triangulate {
public:
Triangulate();
void add(float x, float y); /* Add a point that you want to use to triangulate */
bool triangulate(std::string opt, struct triangulateio& out); /* Triangulate the points you added. We set the `struct triangulateio& out`. See Triangle documentation for the data which is set; or you see the example above */
void free(struct triangulateio& out); /* Free the `struct triangulateio` that you passed to `triangulate()`. Make sure to call this!! */
size_t size(); /* Return the number of points added */
void clear(); /* Clear the points you've added */
public:
std::vector<segment_point> in_points;
};
inline size_t Triangulate::size() {
return in_points.size();
}
inline void Triangulate::clear() {
in_points.clear();
}
inline bool is_equal(const segment_point& p1, const segment_point& p2) {
float dx = p1.x - p2.x;
float dy = p1.y - p2.y;
float d = (dx * dx) + (dy * dy);
if(d < TRIANGULATE_MIN_DIST_SQ) {
return true;
}
return false;
}
inline bool lex_equal(const segment_point& p1, const segment_point& p2) {
return (p1.x < p2.x) || ((p1.x == p2.x) && (p1.y < p2.y) );
}
inline bool segment_sort(const segment_point& p1, const segment_point& p2) {
return p1.dx < p2.dx;
}
} // namespace rx
#endif
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment