-
-
Save roxlu/5caa9c726187997bc15d to your computer and use it in GitHub Desktop.
Triangle wrapper
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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) | |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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; | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
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); | |
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#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 |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
--------------------------------------------------------------------------------- | |
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