diff --git a/src/jc_voronoi.h b/src/jc_voronoi.h index 3228257..a94c96d 100644 --- a/src/jc_voronoi.h +++ b/src/jc_voronoi.h @@ -53,6 +53,8 @@ The input bounding box is optional + The input domain is (-FLT_MAX, FLT_MAX] (for floats) + The api consists of these functions: void jcv_diagram_generate( int num_points, const jcv_point* points, const jcv_rect* rect, jcv_diagram* diagram ); @@ -72,6 +74,7 @@ //#define JCV_ATAN2 atan2 //#define JCV_CEIL ceil //#define JCV_FLOOR floor + //#define JCV_FLT_MAX 1.7976931348623157E+308 #include "jc_voronoi.h" void draw_edges(const jcv_diagram* diagram); @@ -186,6 +189,10 @@ extern "C" { #define JCV_PI 3.14159265358979323846264338327950288f #endif +#ifndef JCV_FLT_MAX + #define JCV_FLT_MAX 3.402823466e+38F +#endif + typedef JCV_REAL_TYPE jcv_real; #pragma pack(push, 1) @@ -394,7 +401,7 @@ typedef struct _jcv_context_internal static const int JCV_DIRECTION_LEFT = 0; static const int JCV_DIRECTION_RIGHT = 1; -static const jcv_real JCV_INVALID_VALUE = (jcv_real)-1; +static const jcv_real JCV_INVALID_VALUE = (jcv_real)-JCV_FLT_MAX; void jcv_diagram_free( jcv_diagram* d ) diff --git a/test/perftest.cpp b/test/perftest.cpp index 73bd636..d6d4f8c 100644 --- a/test/perftest.cpp +++ b/test/perftest.cpp @@ -1,5 +1,6 @@ #include #include +#include static size_t g_MallocCount = 0; static size_t g_MallocSize = 0; @@ -68,6 +69,8 @@ struct Context PointD* dsites; float* sitesx; float* sitesy; + PointF dgmin; + PointF dgmax; const char* testname; @@ -103,6 +106,9 @@ void setup_sites(int count, Context* context) context->boost_points.resize(count); #endif + context->dgmin = PointF(FLT_MAX, FLT_MAX); + context->dgmax = PointF(-FLT_MAX, -FLT_MAX); + int pointoffset = 10; // move the points inwards, for aestetic reasons srand(0); for( int i = 0; i < count; ++i ) @@ -118,6 +124,11 @@ void setup_sites(int count, Context* context) context->dpoints[i].x = x; context->dpoints[i].y = y; + context->dgmin.x = std::min(context->dgmin.x, x); + context->dgmin.y = std::min(context->dgmin.y, y); + context->dgmax.x = std::max(context->dgmax.x, x); + context->dgmax.y = std::max(context->dgmax.y, y); + #if defined(USE_VORONOIPP) context->vpp_sites[i] = new voronoi::VoronoiSite(x, y); #endif @@ -126,6 +137,11 @@ void setup_sites(int count, Context* context) #endif } + context->dgmin.x -= 1; + context->dgmin.y -= 1; + context->dgmax.x += 1; + context->dgmax.y += 1; + context->collectedges = false; } @@ -150,7 +166,7 @@ void null_setup(Context* context) int jc_voronoi(Context* context) { jcv_diagram diagram = { 0 }; - jcv_rect rect = { {0, 0}, {MAP_DIMENSION, MAP_DIMENSION} }; + jcv_rect rect = { {context->dgmin.x, context->dgmin.y}, {context->dgmax.x, context->dgmax.y} }; jcv_diagram_generate(context->count, (const jcv_point*)context->fsites, &rect, &diagram ); if( context->collectedges ) @@ -192,7 +208,7 @@ int jc_voronoi(Context* context) int shaneosullivan_voronoi(Context* context) { VoronoiDiagramGenerator generator; - generator.generateVoronoi(context->sitesx, context->sitesy, context->count, 0, MAP_DIMENSION, 0, MAP_DIMENSION); + generator.generateVoronoi(context->sitesx, context->sitesy, context->count, context->dgmin.x, context->dgmax.x, context->dgmin.y, context->dgmax.y); if( context->collectedges ) { @@ -212,7 +228,7 @@ int shaneosullivan_voronoi(Context* context) int fastjet_voronoi(Context* context) { fastjet::VoronoiDiagramGenerator generator; - generator.generateVoronoi((std::vector*)&context->dpoints, 0, MAP_DIMENSION, 0, MAP_DIMENSION); + generator.generateVoronoi((std::vector*)&context->dpoints, context->dgmin.x, context->dgmax.x, context->dgmin.y, context->dgmax.y); if( context->collectedges ) { @@ -237,7 +253,7 @@ int voronoiplusplus_voronoi(Context* context) if( context->collectedges ) { - const geometry::Rectangle rect(0, 0, MAP_DIMENSION, MAP_DIMENSION); + const geometry::Rectangle rect(context->dgmin.x, context->dgmin.y, context->dgmax.x, context->dgmax.y); geometry::ConvexPolygon boundingPolygon; boundingPolygon << rect.topLeft(); boundingPolygon << rect.topRight(); @@ -375,7 +391,7 @@ static void plot(int x, int y, unsigned char* image, int width, int height, int { if( x < 0 || y < 0 || x > (width-1) || y > (height-1) ) return; - int index = y * width * nchannels + x * nchannels; + int index = (height - y) * width * nchannels + x * nchannels; for( int i = 0; i < nchannels; ++i ) { image[index+i] = color[i]; @@ -414,6 +430,16 @@ static inline int max3(int a, int b, int c) return std::max(a, std::max(b, c)); } +// Remaps the point from the input space to image space +template +static inline PT remap(const PT& pt, const PT& min, const PT& max, const PT& scale) +{ + PT p; + p.x = (pt.x - min.x)/(max.x - min.x) * scale.x; + p.y = (pt.y - min.y)/(max.y - min.y) * scale.y; + return p; +} + template static void draw_triangle(const PT& v0, const PT& v1, const PT& v2, unsigned char* image, int width, int height, int nchannels, unsigned char* color) { @@ -463,6 +489,8 @@ static void output_image(const char* name, Context* context) srand(0); + PointF dimensions(MAP_DIMENSION, MAP_DIMENSION); + for( size_t i = 0; i < context->collectedcells.size(); ++i ) { const PointF& site = context->collectedcells[i].first; @@ -480,22 +508,34 @@ static void output_image(const char* name, Context* context) // Needed for voronoi++ float det = orient2d(site, edges[i].first, edges[i].second); - if( det > 0 ) - draw_triangle( site, edges[i].first, edges[i].second, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_tri); - else - draw_triangle( site, edges[i].second, edges[i].first, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_tri); + + const PointF& _p0 = site; + const PointF& _p1 = det > 0 ? edges[i].first : edges[i].second; + const PointF& _p2 = det > 0 ? edges[i].second : edges[i].first; + + PointF p0 = remap(_p0, context->dgmin, context->dgmax, dimensions); + PointF p1 = remap(_p1, context->dgmin, context->dgmax, dimensions); + PointF p2 = remap(_p2, context->dgmin, context->dgmax, dimensions); + draw_triangle( p0, p1, p2, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_tri); } } for( size_t i = 0; i < context->collectededges.size(); ++i ) { - plot_line( (int)context->collectededges[i].first.x, (int)context->collectededges[i].first.y, - (int)context->collectededges[i].second.x, (int)context->collectededges[i].second.y, data, MAP_DIMENSION, 3, color_blue); + PointF p0 = remap(context->collectededges[i].first, context->dgmin, context->dgmax, dimensions); + PointF p1 = remap(context->collectededges[i].second, context->dgmin, context->dgmax, dimensions); + plot_line( (int)p0.x, (int)p0.y, + (int)p1.x, (int)p1.y, data, MAP_DIMENSION, 3, color_white); } for( int i = 0; i < context->count; ++i ) { - plot( (int)context->fsites[i].x, (int)context->fsites[i].y, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_white); + PointF p = remap(context->fsites[i], context->dgmin, context->dgmax, dimensions); + plot( (int)p.x, (int)p.y, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_white); + plot( (int)p.x+1, (int)p.y, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_white); + plot( (int)p.x-1, (int)p.y, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_white); + plot( (int)p.x, 1+(int)p.y, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_white); + plot( (int)p.x, -1+(int)p.y, data, MAP_DIMENSION, MAP_DIMENSION, 3, color_white); } char path[512]; diff --git a/test/test.c b/test/test.c index 9a39b3d..00fd846 100644 --- a/test/test.c +++ b/test/test.c @@ -336,6 +336,35 @@ static void voronoi_test_issue10_zero_edge_length(Context* ctx) } } + +// Issue: https://github.com/JCash/voronoi/issues/22 +static void voronoi_test_issue22_wrong_edge_count(Context* ctx) +{ + jcv_point points[] = { + { 0, 0 }, + { 2, 0 }, + { -2, 0 }, + { 0, -2 }, + }; + int num_points = (int)(sizeof(points) / sizeof(jcv_point)); + + jcv_diagram_generate(num_points, points, 0, &ctx->diagram); + ASSERT_EQ( num_points, ctx->diagram.numsites ); + + const jcv_site *sites = jcv_diagram_get_sites(&ctx->diagram); + for( int i = 0; i < ctx->diagram.numsites; ++i ) + { + const jcv_site* site = &sites[i]; + const jcv_graphedge* e = site->edges; + int count = 0; + while (e) { + ++count; + e = e->next; + } + ASSERT_EQ( 4u, count ); + } +} + TEST_BEGIN(voronoi_test, voronoi_main_setup, voronoi_main_teardown, test_setup, test_teardown) TEST(voronoi_test_parallel_horiz_2) TEST(voronoi_test_parallel_vert_2) @@ -347,6 +376,7 @@ TEST_BEGIN(voronoi_test, voronoi_main_setup, voronoi_main_teardown, test_setup, TEST(voronoi_test_culling) TEST(voronoi_test_crash1) TEST(voronoi_test_issue10_zero_edge_length) + TEST(voronoi_test_issue22_wrong_edge_count) TEST_END(voronoi_test)