13
include/llmr/map/coverage.hpp
Normal file
13
include/llmr/map/coverage.hpp
Normal file
@@ -0,0 +1,13 @@
|
||||
#ifndef LLMR_MAP_COVERAGE
|
||||
#define LLMR_MAP_COVERAGE
|
||||
|
||||
#include <llmr/util/vec.hpp>
|
||||
#include <forward_list>
|
||||
|
||||
namespace llmr {
|
||||
|
||||
std::forward_list<vec3<int32_t>> covering_tiles(int32_t zoom, const box& points);
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -51,7 +51,7 @@ public:
|
||||
void stopScaling();
|
||||
|
||||
// Temporary
|
||||
void mapCornersToBox(uint32_t z, box& b) const;
|
||||
box mapCornersToBox(uint32_t z) const;
|
||||
|
||||
private:
|
||||
void setScaleXY(double new_scale, double xn, double yn, double duration = 0);
|
||||
|
||||
@@ -55,6 +55,7 @@ struct vec4 {
|
||||
|
||||
struct box {
|
||||
vec2<double> tl, tr, bl, br;
|
||||
vec2<double> center;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
98
src/map/coverage.cpp
Normal file
98
src/map/coverage.cpp
Normal file
@@ -0,0 +1,98 @@
|
||||
#include <llmr/map/coverage.hpp>
|
||||
#include <functional>
|
||||
#include <cmath>
|
||||
|
||||
// Taken from polymaps src/Layer.js
|
||||
// https://github.com/simplegeo/polymaps/blob/master/src/Layer.js#L333-L383
|
||||
|
||||
struct edge {
|
||||
double x0 = 0, y0 = 0;
|
||||
double x1 = 0, y1 = 0;
|
||||
double dx = 0, dy = 0;
|
||||
edge(double x0, double y0, double x1, double y1, double dx, double dy)
|
||||
: x0(x0), y0(y0), x1(x1), y1(y1), dx(dx), dy(dy) {}
|
||||
};
|
||||
|
||||
typedef const std::function<void(int32_t, int32_t, int32_t, int32_t)> ScanLine;
|
||||
|
||||
// scan-line conversion
|
||||
edge _edge(const llmr::vec2<double> a, const llmr::vec2<double> b) {
|
||||
if (a.y > b.y) {
|
||||
return { b.x, b.y, a.x, a.y, a.x - b.x, a.y - b.y };
|
||||
} else {
|
||||
return { a.x, a.y, b.x, b.y, b.x - a.x, b.y - a.y };
|
||||
}
|
||||
}
|
||||
|
||||
// scan-line conversion
|
||||
void _scanSpans(edge e0, edge e1, int32_t ymin, int32_t ymax, ScanLine scanLine) {
|
||||
double y0 = fmax(ymin, floor(e1.y0)),
|
||||
y1 = fmin(ymax, ceil(e1.y1));
|
||||
|
||||
// sort edges by x-coordinate
|
||||
if ((e0.x0 == e1.x0 && e0.y0 == e1.y0) ?
|
||||
(e0.x0 + e1.dy / e0.dy * e0.dx < e1.x1) :
|
||||
(e0.x1 - e1.dy / e0.dy * e0.dx < e1.x0)) {
|
||||
std::swap(e0, e1);
|
||||
}
|
||||
|
||||
// scan lines!
|
||||
double m0 = e0.dx / e0.dy,
|
||||
m1 = e1.dx / e1.dy,
|
||||
d0 = e0.dx > 0, // use y + 1 to compute x0
|
||||
d1 = e1.dx < 0; // use y + 1 to compute x1
|
||||
for (int32_t y = y0; y < y1; y++) {
|
||||
double x0 = m0 * fmax(0, fmin(e0.dy, y + d0 - e0.y0)) + e0.x0,
|
||||
x1 = m1 * fmax(0, fmin(e1.dy, y + d1 - e1.y0)) + e1.x0;
|
||||
scanLine(floor(x1), ceil(x0), y, ymax);
|
||||
}
|
||||
}
|
||||
|
||||
// scan-line conversion
|
||||
void _scanTriangle(const llmr::vec2<double> a, const llmr::vec2<double> b, const llmr::vec2<double> c, int32_t ymin, int32_t ymax, ScanLine& scanLine) {
|
||||
edge ab = _edge(a, b);
|
||||
edge bc = _edge(b, c);
|
||||
edge ca = _edge(c, a);
|
||||
|
||||
// sort edges by y-length
|
||||
if (ab.dy > bc.dy) { std::swap(ab, bc); }
|
||||
if (ab.dy > ca.dy) { std::swap(ab, ca); }
|
||||
if (bc.dy > ca.dy) { std::swap(bc, ca); }
|
||||
|
||||
// scan span! scan span!
|
||||
if (ab.dy) _scanSpans(ca, ab, ymin, ymax, scanLine);
|
||||
if (bc.dy) _scanSpans(ca, bc, ymin, ymax, scanLine);
|
||||
}
|
||||
|
||||
std::forward_list<llmr::vec3<int32_t>> llmr::covering_tiles(int32_t zoom, const box& points) {
|
||||
int32_t dim = pow(2, zoom);
|
||||
std::forward_list<vec3<int32_t>> tiles;
|
||||
|
||||
auto scanLine = [&tiles, zoom](int32_t x0, int32_t x1, int32_t y, int32_t ymax) {
|
||||
int32_t x, wx;
|
||||
if (y >= 0 && y <= ymax) {
|
||||
for (x = x0; x < x1; x++) {
|
||||
wx = (x + ymax) % ymax;
|
||||
tiles.emplace_front(wx, y, zoom);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
// Divide the screen up in two triangles and scan each of them:
|
||||
// \---+
|
||||
// | \ |
|
||||
// +---\.
|
||||
_scanTriangle(points.tl, points.tr, points.br, 0, dim, scanLine);
|
||||
_scanTriangle(points.br, points.bl, points.tl, 0, dim, scanLine);
|
||||
|
||||
const vec2<double>& center = points.center;
|
||||
tiles.sort([¢er](const vec3<int32_t>& a, const vec3<int32_t>& b) {
|
||||
// Sorts by distance from the box center
|
||||
return fabs(a.x - center.x) + fabs(a.y - center.y) <
|
||||
fabs(b.x - center.x) + fabs(b.y - center.y);
|
||||
});
|
||||
|
||||
tiles.unique();
|
||||
|
||||
return tiles;
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <llmr/platform/platform.hpp>
|
||||
#include <llmr/style/resources.hpp>
|
||||
#include <llmr/style/sprite.hpp>
|
||||
#include <llmr/map/coverage.hpp>
|
||||
|
||||
#include <algorithm>
|
||||
|
||||
@@ -329,27 +330,12 @@ bool Map::updateTiles() {
|
||||
int32_t min_covering_zoom = zoom - 10;
|
||||
if (min_covering_zoom < min_zoom) min_covering_zoom = min_zoom;
|
||||
|
||||
|
||||
int32_t max_dim = pow(2, zoom);
|
||||
|
||||
// Map four viewport corners to pixel coordinates
|
||||
box box;
|
||||
transform.mapCornersToBox(zoom, box);
|
||||
box box = transform.mapCornersToBox(zoom);
|
||||
|
||||
vec2<int32_t> tl, br;
|
||||
tl.x = fmax(0, floor(fmin(box.tl.x, box.bl.x)));
|
||||
tl.y = fmax(0, floor(fmin(box.tl.y, box.tr.y)));
|
||||
br.x = fmin(max_dim, ceil(fmax(box.tr.x, box.br.x)));
|
||||
br.y = fmin(max_dim, ceil(fmax(box.bl.y, box.br.y)));
|
||||
|
||||
|
||||
// TODO: Discard tiles that are outside the viewport
|
||||
std::forward_list<Tile::ID> required;
|
||||
for (int32_t y = tl.y; y < br.y; ++y) {
|
||||
for (int32_t x = tl.x; x < br.x; ++x) {
|
||||
required.emplace_front(x, y, zoom);
|
||||
}
|
||||
}
|
||||
// Performs a scanline algorithm search that covers the rectangle of the box
|
||||
// and sorts them by proximity to the center.
|
||||
std::forward_list<Tile::ID> required = llmr::covering_tiles(zoom, box);
|
||||
|
||||
// Retain is a list of tiles that we shouldn't delete, even if they are not
|
||||
// the most ideal tile for the current viewport. This may include tiles like
|
||||
|
||||
@@ -271,33 +271,37 @@ double Transform::getAngle() const {
|
||||
return angle;
|
||||
}
|
||||
|
||||
void Transform::mapCornersToBox(uint32_t z, box& b) const {
|
||||
box Transform::mapCornersToBox(uint32_t z) const {
|
||||
const double ref_scale = pow(2, z);
|
||||
|
||||
// Keep the map as upright as possible.
|
||||
double local_angle = angle;
|
||||
if (local_angle >= M_PI_2) local_angle -= M_PI;
|
||||
if (local_angle < -M_PI_2) local_angle += M_PI;
|
||||
|
||||
const double angle_sin = sin(-local_angle);
|
||||
const double angle_cos = cos(-local_angle);
|
||||
const double angle_sin = sin(-angle);
|
||||
const double angle_cos = cos(-angle);
|
||||
|
||||
const double w_2 = width / 2;
|
||||
const double h_2 = height / 2;
|
||||
const double ss_1 = ref_scale / (scale * util::tileSize);
|
||||
const double ss_2 = (scale * util::tileSize) / 2;
|
||||
const double ss_0 = scale * util::tileSize;
|
||||
const double ss_1 = ref_scale / ss_0;
|
||||
const double ss_2 = ss_0 / 2.0;
|
||||
|
||||
// Calculate the corners of the map view. The resulting coordinates will be
|
||||
// in fractional tile coordinates.
|
||||
box b;
|
||||
|
||||
b.tl.x = ((-w_2) * angle_cos - (-h_2) * angle_sin + ss_2 - x) * ss_1;
|
||||
b.tl.y = ((-w_2) * angle_sin + (-h_2) * angle_cos + ss_2 - y) * ss_1;
|
||||
|
||||
b.tr.x = ((+w_2) * angle_cos - (-h_2) * angle_sin + ss_2 - x) * ss_1;
|
||||
b.tr.y = ((+w_2) * angle_sin + (-h_2) * angle_cos + ss_2 - y) * ss_1;
|
||||
|
||||
b.bl.x = ((-w_2) * angle_cos - (+h_2) * angle_sin + ss_2 - x) * ss_1;
|
||||
b.bl.y = ((-w_2) * angle_sin + (+h_2) * angle_cos + ss_2 - y) * ss_1;
|
||||
|
||||
b.br.x = ((+w_2) * angle_cos - (+h_2) * angle_sin + ss_2 - x) * ss_1;
|
||||
b.br.y = ((+w_2) * angle_sin + (+h_2) * angle_cos + ss_2 - y) * ss_1;
|
||||
|
||||
// Quick hack: use the entire non-rotated bounding box.
|
||||
b.center.x = (ss_2 - x) * ss_1;
|
||||
b.center.y = (ss_2 - y) * ss_1;
|
||||
|
||||
return b;
|
||||
}
|
||||
|
||||
|
||||
@@ -119,7 +119,6 @@ void Painter::drawClippingMask(const mat4& matrix, uint8_t clip_id) {
|
||||
|
||||
void Painter::finishClippingMask() {
|
||||
glColorMask(true, true, true, true);
|
||||
glEnable(GL_STENCIL_TEST);
|
||||
glEnable(GL_DEPTH_TEST);
|
||||
glStencilMask(0x0);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user