mirror of
https://github.com/felt/tippecanoe.git
synced 2026-10-02 08:25:40 +02:00
Searching for floating point conversion bugs
This commit is contained in:
@@ -10,7 +10,7 @@ CXX := $(CXX)
|
|||||||
CFLAGS := $(CFLAGS) -fPIE
|
CFLAGS := $(CFLAGS) -fPIE
|
||||||
CXXFLAGS := $(CXXFLAGS) -std=c++17 -fPIE
|
CXXFLAGS := $(CXXFLAGS) -std=c++17 -fPIE
|
||||||
LDFLAGS := $(LDFLAGS)
|
LDFLAGS := $(LDFLAGS)
|
||||||
WARNING_FLAGS := -Wall -Wshadow -Wsign-compare -Wextra -Wunreachable-code -Wuninitialized -Wshadow
|
WARNING_FLAGS := -Wall -Wshadow -Wsign-compare -Wextra -Wunreachable-code -Wuninitialized -Wshadow -Wfloat-conversion
|
||||||
RELEASE_FLAGS := -O3 -DNDEBUG
|
RELEASE_FLAGS := -O3 -DNDEBUG
|
||||||
DEBUG_FLAGS := -O0 -DDEBUG -fno-inline-functions -fno-omit-frame-pointer
|
DEBUG_FLAGS := -O0 -DDEBUG -fno-inline-functions -fno-omit-frame-pointer
|
||||||
|
|
||||||
|
|||||||
+40
-38
@@ -23,6 +23,8 @@
|
|||||||
#include "errors.hpp"
|
#include "errors.hpp"
|
||||||
#include "projection.hpp"
|
#include "projection.hpp"
|
||||||
|
|
||||||
|
#define ROUND(n) ((long long) std::round(n))
|
||||||
|
|
||||||
static int clip(double *x0, double *y0, double *x1, double *y1, double xmin, double ymin, double xmax, double ymax);
|
static int clip(double *x0, double *y0, double *x1, double *y1, double xmin, double ymin, double xmax, double ymax);
|
||||||
|
|
||||||
drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, long long *bbox, unsigned initial_x, unsigned initial_y) {
|
drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, long long *bbox, unsigned initial_x, unsigned initial_y) {
|
||||||
@@ -85,8 +87,8 @@ drawvec decode_geometry(char **meta, int z, unsigned tx, unsigned ty, long long
|
|||||||
|
|
||||||
void to_tile_scale(drawvec &geom, int z, int detail) {
|
void to_tile_scale(drawvec &geom, int z, int detail) {
|
||||||
for (size_t i = 0; i < geom.size(); i++) {
|
for (size_t i = 0; i < geom.size(); i++) {
|
||||||
geom[i].x = std::round((double) geom[i].x / (1LL << (32 - detail - z)));
|
geom[i].x = ROUND((double) geom[i].x / (1LL << (32 - detail - z)));
|
||||||
geom[i].y = std::round((double) geom[i].y / (1LL << (32 - detail - z)));
|
geom[i].y = ROUND((double) geom[i].y / (1LL << (32 - detail - z)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -108,7 +110,7 @@ drawvec remove_noop(drawvec geom, int type, int shift) {
|
|||||||
drawvec out;
|
drawvec out;
|
||||||
|
|
||||||
for (size_t i = 0; i < geom.size(); i++) {
|
for (size_t i = 0; i < geom.size(); i++) {
|
||||||
if (geom[i].op == VT_LINETO && std::round((double) geom[i].x / (1LL << shift)) == x && std::round((double) geom[i].y / (1LL << shift)) == y) {
|
if (geom[i].op == VT_LINETO && ROUND((double) geom[i].x / (1LL << shift)) == x && ROUND((double) geom[i].y / (1LL << shift)) == y) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -116,8 +118,8 @@ drawvec remove_noop(drawvec geom, int type, int shift) {
|
|||||||
out.push_back(geom[i]);
|
out.push_back(geom[i]);
|
||||||
} else { /* moveto or lineto */
|
} else { /* moveto or lineto */
|
||||||
out.push_back(geom[i]);
|
out.push_back(geom[i]);
|
||||||
x = std::round((double) geom[i].x / (1LL << shift));
|
x = ROUND((double) geom[i].x / (1LL << shift));
|
||||||
y = std::round((double) geom[i].y / (1LL << shift));
|
y = ROUND((double) geom[i].y / (1LL << shift));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -156,7 +158,7 @@ drawvec remove_noop(drawvec geom, int type, int shift) {
|
|||||||
|
|
||||||
for (size_t i = 0; i < geom.size(); i++) {
|
for (size_t i = 0; i < geom.size(); i++) {
|
||||||
if (geom[i].op == VT_MOVETO) {
|
if (geom[i].op == VT_MOVETO) {
|
||||||
if (i > 0 && geom[i - 1].op == VT_LINETO && std::round((double) geom[i - 1].x / (1LL << shift)) == std::round((double) geom[i].x / (1LL << shift)) && std::round((double) geom[i - 1].y / (1LL << shift)) == std::round((double) geom[i].y / (1LL << shift))) {
|
if (i > 0 && geom[i - 1].op == VT_LINETO && ROUND((double) geom[i - 1].x / (1LL << shift)) == ROUND((double) geom[i].x / (1LL << shift)) && ROUND((double) geom[i - 1].y / (1LL << shift)) == ROUND((double) geom[i].y / (1LL << shift))) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -282,7 +284,7 @@ static void decode_clipped(mapbox::geometry::multi_polygon<long long> &t, drawve
|
|||||||
drawvec ring;
|
drawvec ring;
|
||||||
|
|
||||||
for (size_t k = 0; k < t[i][j].size(); k++) {
|
for (size_t k = 0; k < t[i][j].size(); k++) {
|
||||||
ring.push_back(draw((k == 0) ? VT_MOVETO : VT_LINETO, std::round(t[i][j][k].x / scale), std::round(t[i][j][k].y / scale)));
|
ring.push_back(draw((k == 0) ? VT_MOVETO : VT_LINETO, ROUND(t[i][j][k].x / scale), ROUND(t[i][j][k].y / scale)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (ring.size() > 0 && ring[ring.size() - 1] != ring[0]) {
|
if (ring.size() > 0 && ring[ring.size() - 1] != ring[0]) {
|
||||||
@@ -328,7 +330,7 @@ drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip) {
|
|||||||
mapbox::geometry::linear_ring<long long> lr;
|
mapbox::geometry::linear_ring<long long> lr;
|
||||||
|
|
||||||
for (size_t k = i; k < j; k++) {
|
for (size_t k = i; k < j; k++) {
|
||||||
lr.push_back(mapbox::geometry::point<long long>(geom[k].x * scale, geom[k].y * scale));
|
lr.push_back(mapbox::geometry::point<long long>(ROUND(geom[k].x * scale), ROUND(geom[k].y * scale)));
|
||||||
}
|
}
|
||||||
|
|
||||||
if (lr.size() >= 3) {
|
if (lr.size() >= 3) {
|
||||||
@@ -349,11 +351,11 @@ drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip) {
|
|||||||
|
|
||||||
mapbox::geometry::linear_ring<long long> lr;
|
mapbox::geometry::linear_ring<long long> lr;
|
||||||
|
|
||||||
lr.push_back(mapbox::geometry::point<long long>(scale * -clip_buffer, scale * -clip_buffer));
|
lr.push_back(mapbox::geometry::point<long long>(ROUND(scale * -clip_buffer), ROUND(scale * -clip_buffer)));
|
||||||
lr.push_back(mapbox::geometry::point<long long>(scale * -clip_buffer, scale * (area + clip_buffer)));
|
lr.push_back(mapbox::geometry::point<long long>(ROUND(scale * -clip_buffer), ROUND(scale * (area + clip_buffer))));
|
||||||
lr.push_back(mapbox::geometry::point<long long>(scale * (area + clip_buffer), scale * (area + clip_buffer)));
|
lr.push_back(mapbox::geometry::point<long long>(ROUND(scale * (area + clip_buffer)), ROUND(scale * (area + clip_buffer))));
|
||||||
lr.push_back(mapbox::geometry::point<long long>(scale * (area + clip_buffer), scale * -clip_buffer));
|
lr.push_back(mapbox::geometry::point<long long>(ROUND(scale * (area + clip_buffer)), ROUND(scale * -clip_buffer)));
|
||||||
lr.push_back(mapbox::geometry::point<long long>(scale * -clip_buffer, scale * -clip_buffer));
|
lr.push_back(mapbox::geometry::point<long long>(ROUND(scale * -clip_buffer), ROUND(scale * -clip_buffer)));
|
||||||
|
|
||||||
wagyu.add_ring(lr, mapbox::geometry::wagyu::polygon_type_clip);
|
wagyu.add_ring(lr, mapbox::geometry::wagyu::polygon_type_clip);
|
||||||
}
|
}
|
||||||
@@ -414,8 +416,8 @@ drawvec clean_or_clip_poly(drawvec &geom, int z, int buffer, bool clip) {
|
|||||||
for (auto const &outer : result) {
|
for (auto const &outer : result) {
|
||||||
for (auto const &ring : outer) {
|
for (auto const &ring : outer) {
|
||||||
for (auto const &p : ring) {
|
for (auto const &p : ring) {
|
||||||
if (p.x / scale != std::round(p.x / scale) ||
|
if (p.x / scale != ROUND(p.x / scale) ||
|
||||||
p.y / scale != std::round(p.y / scale)) {
|
p.y / scale != ROUND(p.y / scale)) {
|
||||||
scale = 1;
|
scale = 1;
|
||||||
again = true;
|
again = true;
|
||||||
break;
|
break;
|
||||||
@@ -489,7 +491,7 @@ void check_polygon(drawvec &geom) {
|
|||||||
fprintf(stderr, "Internal error: self-intersecting polygon\n");
|
fprintf(stderr, "Internal error: self-intersecting polygon\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
size_t outer_start = -1;
|
ssize_t outer_start = -1;
|
||||||
size_t outer_len = 0;
|
size_t outer_len = 0;
|
||||||
|
|
||||||
for (size_t i = 0; i < geom.size(); i++) {
|
for (size_t i = 0; i < geom.size(); i++) {
|
||||||
@@ -515,7 +517,7 @@ void check_polygon(drawvec &geom) {
|
|||||||
if (!pnpoly(geom, outer_start, outer_len, geom[k].x, geom[k].y)) {
|
if (!pnpoly(geom, outer_start, outer_len, geom[k].x, geom[k].y)) {
|
||||||
bool on_edge = false;
|
bool on_edge = false;
|
||||||
|
|
||||||
for (size_t l = outer_start; l < outer_start + outer_len; l++) {
|
for (ssize_t l = outer_start; l < outer_start + (ssize_t) outer_len; l++) {
|
||||||
if (geom[k].x == geom[l].x || geom[k].y == geom[l].y) {
|
if (geom[k].x == geom[l].x || geom[k].y == geom[l].y) {
|
||||||
on_edge = true;
|
on_edge = true;
|
||||||
break;
|
break;
|
||||||
@@ -635,7 +637,7 @@ static std::vector<std::pair<double, double>> clip_poly1(std::vector<std::pair<d
|
|||||||
out.push_back(intersect(S, E, edge, minx, miny, maxx, maxy)); // on buffer edge
|
out.push_back(intersect(S, E, edge, minx, miny, maxx, maxy)); // on buffer edge
|
||||||
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
|
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
|
||||||
out.push_back(intersect(S, E, edge, ax, ay, bx, by)); // on tile boundary
|
out.push_back(intersect(S, E, edge, ax, ay, bx, by)); // on tile boundary
|
||||||
edge_nodes.push_back(draw(VT_MOVETO, std::round(out.back().first), std::round(out.back().second)));
|
edge_nodes.push_back(draw(VT_MOVETO, ROUND(out.back().first), ROUND(out.back().second)));
|
||||||
}
|
}
|
||||||
out.push_back(E);
|
out.push_back(E);
|
||||||
}
|
}
|
||||||
@@ -652,7 +654,7 @@ static std::vector<std::pair<double, double>> clip_poly1(std::vector<std::pair<d
|
|||||||
// now inside the tile
|
// now inside the tile
|
||||||
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
|
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
|
||||||
out.push_back(intersect(S, E, edge, ax, ay, bx, by)); // on tile boundary
|
out.push_back(intersect(S, E, edge, ax, ay, bx, by)); // on tile boundary
|
||||||
edge_nodes.push_back(draw(VT_MOVETO, std::round(out.back().first), std::round(out.back().second)));
|
edge_nodes.push_back(draw(VT_MOVETO, ROUND(out.back().first), ROUND(out.back().second)));
|
||||||
}
|
}
|
||||||
out.push_back(E);
|
out.push_back(E);
|
||||||
}
|
}
|
||||||
@@ -663,14 +665,14 @@ static std::vector<std::pair<double, double>> clip_poly1(std::vector<std::pair<d
|
|||||||
// now outside the buffer
|
// now outside the buffer
|
||||||
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
|
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
|
||||||
out.push_back(intersect(S, E, edge, ax, ay, bx, by)); // on tile boundary
|
out.push_back(intersect(S, E, edge, ax, ay, bx, by)); // on tile boundary
|
||||||
edge_nodes.push_back(draw(VT_MOVETO, std::round(out.back().first), std::round(out.back().second)));
|
edge_nodes.push_back(draw(VT_MOVETO, ROUND(out.back().first), ROUND(out.back().second)));
|
||||||
}
|
}
|
||||||
out.push_back(intersect(S, E, edge, minx, miny, maxx, maxy)); // on buffer edge
|
out.push_back(intersect(S, E, edge, minx, miny, maxx, maxy)); // on buffer edge
|
||||||
} else if (!inside(E, edge, ax, ay, bx, by)) {
|
} else if (!inside(E, edge, ax, ay, bx, by)) {
|
||||||
// now inside the buffer but outside the tile edge
|
// now inside the buffer but outside the tile edge
|
||||||
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
|
if (prevent[P_SIMPLIFY_SHARED_NODES]) {
|
||||||
out.push_back(intersect(S, E, edge, ax, ay, bx, by)); // on tile boundary
|
out.push_back(intersect(S, E, edge, ax, ay, bx, by)); // on tile boundary
|
||||||
edge_nodes.push_back(draw(VT_MOVETO, std::round(out.back().first), std::round(out.back().second)));
|
edge_nodes.push_back(draw(VT_MOVETO, ROUND(out.back().first), ROUND(out.back().second)));
|
||||||
}
|
}
|
||||||
out.push_back(E);
|
out.push_back(E);
|
||||||
} else {
|
} else {
|
||||||
@@ -735,9 +737,9 @@ drawvec simple_clip_poly(drawvec &geom, long long minx, long long miny, long lon
|
|||||||
}
|
}
|
||||||
for (size_t k = 0; k < tmp.size(); k++) {
|
for (size_t k = 0; k < tmp.size(); k++) {
|
||||||
if (k == 0) {
|
if (k == 0) {
|
||||||
out.push_back(draw(VT_MOVETO, std::round(tmp[k].first), std::round(tmp[k].second)));
|
out.push_back(draw(VT_MOVETO, ROUND(tmp[k].first), ROUND(tmp[k].second)));
|
||||||
} else {
|
} else {
|
||||||
out.push_back(draw(VT_LINETO, std::round(tmp[k].first), std::round(tmp[k].second)));
|
out.push_back(draw(VT_LINETO, ROUND(tmp[k].first), ROUND(tmp[k].second)));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -807,11 +809,11 @@ drawvec reduce_tiny_poly(drawvec &geom, int z, int detail, bool *reduced, double
|
|||||||
if (area > 0 && *accum_area > pixel * pixel) {
|
if (area > 0 && *accum_area > pixel * pixel) {
|
||||||
// XXX use centroid;
|
// XXX use centroid;
|
||||||
|
|
||||||
out.push_back(draw(VT_MOVETO, geom[i].x - pixel / 2, geom[i].y - pixel / 2));
|
out.push_back(draw(VT_MOVETO, ROUND(geom[i].x - pixel / 2), ROUND(geom[i].y - pixel / 2)));
|
||||||
out.push_back(draw(VT_LINETO, geom[i].x - pixel / 2 + pixel, geom[i].y - pixel / 2));
|
out.push_back(draw(VT_LINETO, ROUND(geom[i].x - pixel / 2 + pixel), ROUND(geom[i].y - pixel / 2)));
|
||||||
out.push_back(draw(VT_LINETO, geom[i].x - pixel / 2 + pixel, geom[i].y - pixel / 2 + pixel));
|
out.push_back(draw(VT_LINETO, ROUND(geom[i].x - pixel / 2 + pixel), ROUND(geom[i].y - pixel / 2 + pixel)));
|
||||||
out.push_back(draw(VT_LINETO, geom[i].x - pixel / 2, geom[i].y - pixel / 2 + pixel));
|
out.push_back(draw(VT_LINETO, ROUND(geom[i].x - pixel / 2), ROUND(geom[i].y - pixel / 2 + pixel)));
|
||||||
out.push_back(draw(VT_LINETO, geom[i].x - pixel / 2, geom[i].y - pixel / 2));
|
out.push_back(draw(VT_LINETO, ROUND(geom[i].x - pixel / 2), ROUND(geom[i].y - pixel / 2)));
|
||||||
includes_dust = true;
|
includes_dust = true;
|
||||||
|
|
||||||
*accum_area -= pixel * pixel;
|
*accum_area -= pixel * pixel;
|
||||||
@@ -964,8 +966,8 @@ drawvec clip_lines(drawvec &geom, long long minx, long long miny, long long maxx
|
|||||||
int c = clip(&x1, &y1, &x2, &y2, minx, miny, maxx, maxy);
|
int c = clip(&x1, &y1, &x2, &y2, minx, miny, maxx, maxy);
|
||||||
|
|
||||||
if (c > 1) { // clipped
|
if (c > 1) { // clipped
|
||||||
out.push_back(draw(VT_MOVETO, std::round(x1), std::round(y1)));
|
out.push_back(draw(VT_MOVETO, ROUND(x1), ROUND(y1)));
|
||||||
out.push_back(draw(VT_LINETO, std::round(x2), std::round(y2)));
|
out.push_back(draw(VT_LINETO, ROUND(x2), ROUND(y2)));
|
||||||
out.push_back(draw(VT_MOVETO, geom[i].x, geom[i].y));
|
out.push_back(draw(VT_MOVETO, geom[i].x, geom[i].y));
|
||||||
} else if (c == 1) { // unchanged
|
} else if (c == 1) { // unchanged
|
||||||
out.push_back(geom[i]);
|
out.push_back(geom[i]);
|
||||||
@@ -992,8 +994,8 @@ static long long square_distance_from_line(long long point_x, long long point_y,
|
|||||||
u = 0;
|
u = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
long long x = std::round(segA_x + u * p2x);
|
long long x = ROUND(segA_x + u * p2x);
|
||||||
long long y = std::round(segA_y + u * p2y);
|
long long y = ROUND(segA_y + u * p2y);
|
||||||
|
|
||||||
long long dx = x - point_x;
|
long long dx = x - point_x;
|
||||||
long long dy = y - point_y;
|
long long dy = y - point_y;
|
||||||
@@ -1095,12 +1097,12 @@ drawvec impose_tile_boundaries(drawvec &geom, long long extent) {
|
|||||||
int c = clip(&x1, &y1, &x2, &y2, 0, 0, extent, extent);
|
int c = clip(&x1, &y1, &x2, &y2, 0, 0, extent, extent);
|
||||||
|
|
||||||
if (c > 1) { // clipped
|
if (c > 1) { // clipped
|
||||||
if (std::round(x1) != geom[i - 1].x || std::round(y1) != geom[i - 1].y) {
|
if (ROUND(x1) != geom[i - 1].x || ROUND(y1) != geom[i - 1].y) {
|
||||||
out.push_back(draw(VT_LINETO, std::round(x1), std::round(y1)));
|
out.push_back(draw(VT_LINETO, ROUND(x1), ROUND(y1)));
|
||||||
out[out.size() - 1].necessary = 1;
|
out[out.size() - 1].necessary = 1;
|
||||||
}
|
}
|
||||||
if (std::round(x2) != geom[i - 0].x || std::round(y2) != geom[i - 0].y) {
|
if (ROUND(x2) != geom[i - 0].x || ROUND(y2) != geom[i - 0].y) {
|
||||||
out.push_back(draw(VT_LINETO, std::round(x2), std::round(y2)));
|
out.push_back(draw(VT_LINETO, ROUND(x2), ROUND(y2)));
|
||||||
out[out.size() - 1].necessary = 1;
|
out[out.size() - 1].necessary = 1;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1528,8 +1530,8 @@ drawvec stairstep(drawvec &geom, int z, int detail) {
|
|||||||
double scale = 1 << (32 - detail - z);
|
double scale = 1 << (32 - detail - z);
|
||||||
|
|
||||||
for (size_t i = 0; i < geom.size(); i++) {
|
for (size_t i = 0; i < geom.size(); i++) {
|
||||||
geom[i].x = std::round(geom[i].x / scale);
|
geom[i].x = ROUND(geom[i].x / scale);
|
||||||
geom[i].y = std::round(geom[i].y / scale);
|
geom[i].y = ROUND(geom[i].y / scale);
|
||||||
}
|
}
|
||||||
|
|
||||||
for (size_t i = 0; i < geom.size(); i++) {
|
for (size_t i = 0; i < geom.size(); i++) {
|
||||||
|
|||||||
+1
-1
@@ -26,7 +26,7 @@ struct draw {
|
|||||||
|
|
||||||
draw(int nop, long long nx, long long ny)
|
draw(int nop, long long nx, long long ny)
|
||||||
: x(nx),
|
: x(nx),
|
||||||
op(nop),
|
op((signed char) nop),
|
||||||
y(ny),
|
y(ny),
|
||||||
necessary(0) {
|
necessary(0) {
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user