Merge branch 'master' into linux-wrapper
Conflicts: include/llmr/style/sprite.hpp
This commit is contained in:
@@ -6,8 +6,6 @@ var style = require('./style.js');
|
||||
var Protobuf = require('./protobuf.js');
|
||||
var fs = require('fs');
|
||||
|
||||
// var fs = require('fs');
|
||||
|
||||
var pbf = new Protobuf();
|
||||
|
||||
// enum
|
||||
@@ -181,23 +179,47 @@ function createLineClass(layer, name) {
|
||||
return pbf;
|
||||
}
|
||||
|
||||
function createMarkerClass(layer, name) {
|
||||
var pbf = new Protobuf();
|
||||
pbf.writeTaggedString(1 /* layer_name */, name);
|
||||
|
||||
if ('color' in layer) {
|
||||
var color = layer.color.match(/^#([0-9a-f]{6})$/i);
|
||||
if (!color) {
|
||||
console.warn('invalid color');
|
||||
} else {
|
||||
pbf.writeTaggedUInt32(3 /* color */, parseInt(color[1] + 'ff', 16));
|
||||
}
|
||||
}
|
||||
|
||||
if ('size' in layer) {
|
||||
pbf.writeMessage(4 /* size */, convertProperty(layer.size));
|
||||
}
|
||||
|
||||
if ('opacity' in layer) {
|
||||
pbf.writeMessage(6 /* opacity */, convertProperty(layer.opacity));
|
||||
}
|
||||
|
||||
if ('image' in layer) {
|
||||
pbf.writeTaggedString(8 /* image */, layer.image);
|
||||
}
|
||||
|
||||
return pbf;
|
||||
}
|
||||
|
||||
function createClass(klass) {
|
||||
var pbf = new Protobuf();
|
||||
pbf.writeTaggedString(1 /* name */, klass.name);
|
||||
for (var name in klass.layers) {
|
||||
switch (klass.layers[name].type) {
|
||||
case 'fill': pbf.writeMessage(2 /* fill */, createFillClass(klass.layers[name], name)); break;
|
||||
case 'line': pbf.writeMessage(3 /* line */, createLineClass(klass.layers[name], name)); break;
|
||||
case 'fill': pbf.writeMessage(2 /* fill */, createFillClass(klass.layers[name], name)); break;
|
||||
case 'line': pbf.writeMessage(3 /* line */, createLineClass(klass.layers[name], name)); break;
|
||||
case 'marker': pbf.writeMessage(4 /* marker */, createMarkerClass(klass.layers[name], name)); break;
|
||||
}
|
||||
}
|
||||
return pbf;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
for (var name in style.buckets) {
|
||||
var bucket = style.buckets[name];
|
||||
pbf.writeMessage(1 /* bucket */, createBucket(bucket, name));
|
||||
|
||||
@@ -140,6 +140,8 @@ module.exports = {
|
||||
},
|
||||
"alcohol": {
|
||||
"type": "marker",
|
||||
"color": "#999999",
|
||||
"size": 24,
|
||||
"image": "alcohol-shop"
|
||||
}
|
||||
}
|
||||
|
||||
19
include/llmr/geometry/point_buffer.hpp
Normal file
19
include/llmr/geometry/point_buffer.hpp
Normal file
@@ -0,0 +1,19 @@
|
||||
#ifndef LLMR_GEOMETRY_POINT_BUFFER
|
||||
#define LLMR_GEOMETRY_POINT_BUFFER
|
||||
|
||||
#include "buffer.hpp"
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class PointVertexBuffer : public Buffer<
|
||||
4 // 2 coordinates per vertex (== 4 bytes)
|
||||
> {
|
||||
public:
|
||||
typedef int16_t vertex_type;
|
||||
|
||||
void add(vertex_type x, vertex_type y);
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -20,7 +20,7 @@ public:
|
||||
~Map();
|
||||
|
||||
/* setup */
|
||||
void setup();
|
||||
void setup(float pixelRatio = 1);
|
||||
void loadStyle(const uint8_t *const data, uint32_t bytes);
|
||||
void loadSprite(const std::string& url);
|
||||
void loadSettings();
|
||||
@@ -29,13 +29,13 @@ public:
|
||||
/* callback */
|
||||
bool render();
|
||||
void cancelAnimations();
|
||||
void tileLoaded(std::shared_ptr<Tile> tile);
|
||||
void tileFailed(std::shared_ptr<Tile> tile);
|
||||
|
||||
/* position */
|
||||
void moveBy(double dx, double dy, double duration = 0);
|
||||
void setLonLat(double lon, double lat, double duration = 0);
|
||||
void getLonLat(double &lon, double &lat) const;
|
||||
void startPanning();
|
||||
void stopPanning();
|
||||
void resetPosition();
|
||||
|
||||
/* scale */
|
||||
@@ -47,12 +47,16 @@ public:
|
||||
void setLonLatZoom(double lon, double lat, double zoom, double duration = 0);
|
||||
void getLonLatZoom(double &lon, double &lat, double &zoom) const;
|
||||
void resetZoom();
|
||||
void startScaling();
|
||||
void stopScaling();
|
||||
|
||||
/* rotation */
|
||||
void rotateBy(double cx, double cy, double sx, double sy, double ex, double ey, double duration = 0);
|
||||
void setAngle(double angle, double cx = -1, double cy = -1, double duration = 0);
|
||||
double getAngle() const;
|
||||
void resetNorth();
|
||||
void startRotating();
|
||||
void stopRotating();
|
||||
|
||||
void toggleDebug();
|
||||
|
||||
@@ -75,6 +79,8 @@ private:
|
||||
int32_t min_zoom;
|
||||
int32_t max_zoom;
|
||||
|
||||
float pixel_ratio;
|
||||
|
||||
std::forward_list<std::shared_ptr<Tile>> tiles;
|
||||
std::forward_list<std::shared_ptr<Tile>> historic_tiles;
|
||||
};
|
||||
|
||||
@@ -23,6 +23,7 @@ class VectorTile;
|
||||
class VectorTileLayer;
|
||||
class FillVertexBuffer;
|
||||
class LineVertexBuffer;
|
||||
class PointVertexBuffer;
|
||||
class TriangleElementsBuffer;
|
||||
class LineElementsBuffer;
|
||||
class PointElementsBuffer;
|
||||
@@ -59,6 +60,7 @@ public:
|
||||
std::shared_ptr<Bucket> createBucket(const VectorTile& tile, const BucketDescription& bucket_desc);
|
||||
std::shared_ptr<Bucket> createFillBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
std::shared_ptr<Bucket> createLineBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
std::shared_ptr<Bucket> createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc);
|
||||
|
||||
void cancel();
|
||||
|
||||
@@ -77,6 +79,7 @@ public:
|
||||
|
||||
std::shared_ptr<FillVertexBuffer> fillVertexBuffer;
|
||||
std::shared_ptr<LineVertexBuffer> lineVertexBuffer;
|
||||
std::shared_ptr<PointVertexBuffer> pointVertexBuffer;
|
||||
|
||||
std::shared_ptr<TriangleElementsBuffer> triangleElementsBuffer;
|
||||
std::shared_ptr<LineElementsBuffer> lineElementsBuffer;
|
||||
|
||||
@@ -42,6 +42,14 @@ public:
|
||||
void getLonLat(double& lon, double& lat) const;
|
||||
void getLonLatZoom(double& lon, double& lat, double& zoom) const;
|
||||
|
||||
// Animations
|
||||
void startPanning();
|
||||
void stopPanning();
|
||||
void startRotating();
|
||||
void stopRotating();
|
||||
void startScaling();
|
||||
void stopScaling();
|
||||
|
||||
// Temporary
|
||||
void mapCornersToBox(uint32_t z, box& b) const;
|
||||
|
||||
@@ -61,6 +69,10 @@ public:
|
||||
|
||||
float pixelRatio = 1;
|
||||
|
||||
bool rotating = false;
|
||||
bool scaling = false;
|
||||
bool panning = false;
|
||||
|
||||
private:
|
||||
double x = 0, y = 0; // pixel values of the map center in the current scale
|
||||
double angle = 0;
|
||||
@@ -72,7 +84,10 @@ private:
|
||||
// cache values for spherical mercator math
|
||||
double zc, Bc, Cc;
|
||||
|
||||
std::forward_list<util::animation> animations;
|
||||
std::forward_list<std::shared_ptr<util::animation>> animations;
|
||||
std::shared_ptr<util::animation> scale_timeout;
|
||||
std::shared_ptr<util::animation> rotate_timeout;
|
||||
std::shared_ptr<util::animation> pan_timeout;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -6,7 +6,7 @@
|
||||
#include <string>
|
||||
|
||||
#define kTileURL "http://a.gl-api-us-east-1.tilestream.net/v3/mapbox.mapbox-streets-v4/%d/%d/%d.gl.pbf"
|
||||
#define kSpriteURL "http://mapbox-kkaefer.s3.amazonaws.com/static/sprite"
|
||||
#define kSpriteURL "https://dl.dropboxusercontent.com/u/575564/sprite"
|
||||
|
||||
namespace llmr {
|
||||
|
||||
@@ -14,16 +14,16 @@ class Tile;
|
||||
|
||||
namespace platform {
|
||||
|
||||
// Restarts painting. This could for example trigger the event loop of the
|
||||
// controlling application.
|
||||
void restart(void *obj);
|
||||
// Restarts painting. This could for example trigger the event loop of the controlling application.
|
||||
void restart();
|
||||
|
||||
struct Response {
|
||||
int16_t code = -1;
|
||||
std::string body;
|
||||
};
|
||||
|
||||
void request_http(std::string url, std::function<void(Response&)> func, std::function<void()> cb);
|
||||
// Makes an HTTP request of a URL on a background thread, calls a function with the results on the same thread, and finally calls a callback function on the main thread.
|
||||
void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback);
|
||||
|
||||
// Returns a relative timestamp in seconds. This value must be monotonic.
|
||||
double time();
|
||||
|
||||
@@ -12,6 +12,7 @@
|
||||
#include <llmr/renderer/shader-pattern.hpp>
|
||||
#include <llmr/renderer/shader-line.hpp>
|
||||
#include <llmr/renderer/shader-linejoin.hpp>
|
||||
#include <llmr/renderer/shader-point.hpp>
|
||||
|
||||
|
||||
namespace llmr {
|
||||
@@ -23,6 +24,7 @@ class Tile;
|
||||
|
||||
class FillBucket;
|
||||
class LineBucket;
|
||||
class PointBucket;
|
||||
|
||||
class Painter : private util::noncopyable {
|
||||
public:
|
||||
@@ -36,6 +38,7 @@ public:
|
||||
void renderBackground();
|
||||
void renderFill(FillBucket& bucket, const std::string& layer_name, const Tile::ID& id);
|
||||
void renderLine(LineBucket& bucket, const std::string& layer_name, const Tile::ID& id);
|
||||
void renderPoint(PointBucket& bucket, const std::string& layer_name, const Tile::ID& id);
|
||||
|
||||
private:
|
||||
void setupShaders();
|
||||
@@ -63,6 +66,7 @@ private:
|
||||
std::unique_ptr<LineShader> lineShader;
|
||||
std::unique_ptr<LinejoinShader> linejoinShader;
|
||||
std::unique_ptr<PatternShader> patternShader;
|
||||
std::unique_ptr<PointShader> pointShader;
|
||||
|
||||
// Set up the stencil quad we're using to generate the stencil mask.
|
||||
VertexBuffer tileStencilBuffer = {
|
||||
|
||||
51
include/llmr/renderer/point_bucket.hpp
Normal file
51
include/llmr/renderer/point_bucket.hpp
Normal file
@@ -0,0 +1,51 @@
|
||||
#ifndef LLMR_RENDERER_POINTBUCKET
|
||||
#define LLMR_RENDERER_POINTBUCKET
|
||||
|
||||
#include <llmr/renderer/bucket.hpp>
|
||||
#include <llmr/style/bucket_description.hpp>
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <llmr/geometry/point_buffer.hpp>
|
||||
|
||||
#include <vector>
|
||||
#include <memory>
|
||||
|
||||
#ifndef BUFFER_OFFSET
|
||||
#define BUFFER_OFFSET(i) ((char *)nullptr + (i))
|
||||
#endif
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class Style;
|
||||
class PointVertexBuffer;
|
||||
class BucketDescription;
|
||||
class PointShader;
|
||||
struct Coordinate;
|
||||
struct pbf;
|
||||
|
||||
class PointBucket : public Bucket {
|
||||
public:
|
||||
PointBucket(const std::shared_ptr<PointVertexBuffer>& vertexBuffer,
|
||||
const BucketDescription& bucket_desc);
|
||||
|
||||
virtual void render(Painter& painter, const std::string& layer_name, const Tile::ID& id);
|
||||
|
||||
void addGeometry(pbf& data);
|
||||
|
||||
bool hasPoints() const;
|
||||
|
||||
void drawPoints(PointShader& shader);
|
||||
|
||||
public:
|
||||
const BucketGeometryDescription geometry;
|
||||
|
||||
private:
|
||||
std::shared_ptr<PointVertexBuffer> vertexBuffer;
|
||||
VertexArrayObject<PointShader> array;
|
||||
|
||||
const uint32_t vertex_start;
|
||||
uint32_t vertex_end = 0;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
41
include/llmr/renderer/shader-point.hpp
Normal file
41
include/llmr/renderer/shader-point.hpp
Normal file
@@ -0,0 +1,41 @@
|
||||
#ifndef LLMR_RENDERER_SHADER_POINT
|
||||
#define LLMR_RENDERER_SHADER_POINT
|
||||
|
||||
#include "shader.hpp"
|
||||
|
||||
namespace llmr {
|
||||
|
||||
class PointShader : public Shader {
|
||||
public:
|
||||
PointShader();
|
||||
|
||||
void bind(char *offset);
|
||||
|
||||
void setImage(int32_t image);
|
||||
void setColor(const std::array<float, 4>& color);
|
||||
void setPointTopLeft(const std::array<float, 2>& point_tl);
|
||||
void setPointBottomRight(const std::array<float, 2>& point_br);
|
||||
void setSize(float size);
|
||||
|
||||
private:
|
||||
int32_t a_pos = -1;
|
||||
|
||||
int32_t image = -1;
|
||||
int32_t u_image = -1;
|
||||
|
||||
std::array<float, 4> color = {};
|
||||
int32_t u_color = -1;
|
||||
|
||||
std::array<float, 2> point_tl = {};
|
||||
int32_t u_point_tl = -1;
|
||||
|
||||
std::array<float, 2> point_br = {};
|
||||
int32_t u_point_br = -1;
|
||||
|
||||
float size = 0;
|
||||
int32_t u_size = -1;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -16,6 +16,7 @@ enum {
|
||||
OUTLINE_SHADER,
|
||||
PATTERN_SHADER,
|
||||
PLAIN_SHADER,
|
||||
POINT_SHADER,
|
||||
SHADER_COUNT
|
||||
};
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ class ClassDescription {
|
||||
public:
|
||||
std::map<std::string, FillClass> fill;
|
||||
std::map<std::string, LineClass> line;
|
||||
std::map<std::string, PointClass> point;
|
||||
};
|
||||
|
||||
|
||||
|
||||
@@ -60,8 +60,22 @@ struct FunctionProperty {
|
||||
inline T operator()(float z) const { return function(z, values); }
|
||||
};
|
||||
|
||||
struct PointClass {
|
||||
FunctionProperty<bool> hidden;
|
||||
FunctionProperty<float> size;
|
||||
Color color = {{ 0, 0, 0, 1 }};
|
||||
FunctionProperty<float> opacity = 1;
|
||||
std::string image;
|
||||
};
|
||||
|
||||
struct PointProperties {
|
||||
bool hidden = false;
|
||||
float size = 0;
|
||||
Color color = {{ 0, 0, 0, 1 }};
|
||||
float opacity = 1.0;
|
||||
std::string image;
|
||||
};
|
||||
|
||||
// LineClass is the information we parse from the stylesheet
|
||||
struct LineClass {
|
||||
FunctionProperty<bool> hidden;
|
||||
FunctionProperty<float> width;
|
||||
@@ -70,7 +84,6 @@ struct LineClass {
|
||||
FunctionProperty<float> opacity = 1;
|
||||
};
|
||||
|
||||
// LineProperties is the one we resolve this to.
|
||||
struct LineProperties {
|
||||
bool hidden = false;
|
||||
float width = 0;
|
||||
@@ -79,9 +92,6 @@ struct LineProperties {
|
||||
float opacity = 1.0;
|
||||
};
|
||||
|
||||
|
||||
|
||||
|
||||
struct FillClass {
|
||||
FunctionProperty<bool> hidden;
|
||||
Winding winding = Winding::NonZero;
|
||||
|
||||
@@ -36,7 +36,7 @@ class Sprite : public std::enable_shared_from_this<Sprite> {
|
||||
public:
|
||||
~Sprite();
|
||||
|
||||
void load(const std::string& base_url);
|
||||
void load(const std::string& base_url, float pixelRatio = 1);
|
||||
void bind(bool linear = false);
|
||||
|
||||
ImagePosition getPosition(const std::string& name, bool repeating = false);
|
||||
|
||||
@@ -36,6 +36,7 @@ private:
|
||||
static std::pair<std::string, ClassDescription> parseClass(pbf data);
|
||||
static std::pair<std::string, FillClass> parseFillClass(pbf data);
|
||||
static std::pair<std::string, LineClass> parseLineClass(pbf data);
|
||||
static std::pair<std::string, PointClass> parsePointClass(pbf data);
|
||||
template <typename T> static FunctionProperty<T> parseProperty(pbf data);
|
||||
static Color parseColor(pbf& data);
|
||||
|
||||
@@ -53,6 +54,7 @@ public:
|
||||
struct {
|
||||
std::map<std::string, FillProperties> fills;
|
||||
std::map<std::string, LineProperties> lines;
|
||||
std::map<std::string, PointProperties> points;
|
||||
} computed;
|
||||
};
|
||||
|
||||
|
||||
@@ -2,6 +2,7 @@
|
||||
#define LLMR_UTIL_ANIMATION
|
||||
|
||||
#include <llmr/util/noncopyable.hpp>
|
||||
#include <llmr/platform/platform.hpp>
|
||||
|
||||
namespace llmr {
|
||||
namespace util {
|
||||
@@ -12,14 +13,51 @@ public:
|
||||
running,
|
||||
complete
|
||||
};
|
||||
animation(double duration)
|
||||
: start(platform::time()),
|
||||
duration(duration) {}
|
||||
|
||||
animation(double from, double to, double &value, double duration);
|
||||
double progress() const {
|
||||
return (platform::time() - start) / duration;
|
||||
}
|
||||
|
||||
virtual state update() const = 0;
|
||||
virtual ~animation();
|
||||
|
||||
protected:
|
||||
const double start, duration;
|
||||
};
|
||||
|
||||
class ease_animation : public animation {
|
||||
public:
|
||||
ease_animation(double from, double to, double& value, double duration);
|
||||
state update() const;
|
||||
|
||||
private:
|
||||
const double start, duration;
|
||||
const double from, to;
|
||||
double &value;
|
||||
double& value;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
class timeout : public animation {
|
||||
public:
|
||||
timeout(T final_value, T& value, double duration)
|
||||
: animation(duration),
|
||||
final_value(final_value),
|
||||
value(value) {}
|
||||
|
||||
state update() const {
|
||||
if (progress() >= 1) {
|
||||
value = final_value;
|
||||
return complete;
|
||||
} else {
|
||||
return running;
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
const T final_value;
|
||||
T& value;
|
||||
};
|
||||
|
||||
}
|
||||
|
||||
@@ -49,7 +49,7 @@ class MBXMapView
|
||||
{
|
||||
settings.load();
|
||||
|
||||
map.setup();
|
||||
map.setup([[UIScreen mainScreen] scale]);
|
||||
|
||||
CGRect frame = [[UIScreen mainScreen] bounds];
|
||||
map.resize(frame.size.width, frame.size.height, frame.size.width, frame.size.height);
|
||||
@@ -141,6 +141,8 @@ class MBXMapView
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
mapView->settings.sync();
|
||||
}
|
||||
|
||||
@@ -236,6 +238,8 @@ class MBXMapView
|
||||
|
||||
if (pinch.state == UIGestureRecognizerStateBegan)
|
||||
{
|
||||
mapView->map.startScaling();
|
||||
|
||||
self.scale = mapView->map.getScale();
|
||||
}
|
||||
else if (pinch.state == UIGestureRecognizerStateChanged)
|
||||
@@ -258,6 +262,8 @@ class MBXMapView
|
||||
}
|
||||
else if (pinch.state == UIGestureRecognizerStateEnded)
|
||||
{
|
||||
mapView->map.stopScaling();
|
||||
|
||||
if (fabsf(pinch.velocity) < 20)
|
||||
return;
|
||||
|
||||
@@ -270,6 +276,10 @@ class MBXMapView
|
||||
|
||||
mapView->map.scaleBy(new_scale / scale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y, duration);
|
||||
}
|
||||
else if (pinch.state == UIGestureRecognizerStateCancelled)
|
||||
{
|
||||
mapView->map.stopScaling();
|
||||
}
|
||||
|
||||
[self updateRender];
|
||||
}
|
||||
@@ -280,12 +290,18 @@ class MBXMapView
|
||||
|
||||
if (rotate.state == UIGestureRecognizerStateBegan)
|
||||
{
|
||||
mapView->map.startRotating();
|
||||
|
||||
self.angle = mapView->map.getAngle();
|
||||
}
|
||||
else if (rotate.state == UIGestureRecognizerStateChanged)
|
||||
{
|
||||
mapView->map.setAngle(self.angle + rotate.rotation, [rotate locationInView:rotate.view].x, [rotate locationInView:rotate.view].y);
|
||||
}
|
||||
else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
|
||||
{
|
||||
mapView->map.stopRotating();
|
||||
}
|
||||
|
||||
[self updateRender];
|
||||
}
|
||||
@@ -373,27 +389,27 @@ class MBXMapView
|
||||
return ([validSimultaneousGestures containsObject:[gestureRecognizer class]] && [validSimultaneousGestures containsObject:[otherGestureRecognizer class]]);
|
||||
}
|
||||
|
||||
CADisplayLink *displayLink;
|
||||
MBXMapView *mapView;
|
||||
NSOperationQueue *queue = NULL;
|
||||
CADisplayLink *displayLink;
|
||||
NSOperationQueue *queue;
|
||||
|
||||
namespace llmr
|
||||
{
|
||||
namespace platform
|
||||
{
|
||||
void restart(void *)
|
||||
void restart()
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MBXNeedsRenderNotification object:nil];
|
||||
}
|
||||
|
||||
void request_http(std::string url, std::function<void(Response&)> func, std::function<void()> cb)
|
||||
void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback)
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil userInfo:[NSDictionary dictionaryWithObject:@1 forKey:@"count"]];
|
||||
|
||||
if (!queue) {
|
||||
queue = [[NSOperationQueue alloc] init];
|
||||
}
|
||||
if (!queue)
|
||||
queue = [NSOperationQueue new];
|
||||
|
||||
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:[NSString stringWithUTF8String:url.c_str()]]];
|
||||
NSMutableURLRequest *urlRequest = [NSMutableURLRequest requestWithURL:[NSURL URLWithString:@(url.c_str())]];
|
||||
|
||||
[NSURLConnection sendAsynchronousRequest:urlRequest
|
||||
queue:queue
|
||||
@@ -407,10 +423,11 @@ namespace llmr
|
||||
res.body = { (const char *)[data bytes], [data length] };
|
||||
}
|
||||
|
||||
func(res);
|
||||
background_function(res);
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
cb();
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void)
|
||||
{
|
||||
foreground_callback();
|
||||
});
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil userInfo:[NSDictionary dictionaryWithObject:@(-1) forKey:@"count"]];
|
||||
|
||||
@@ -5,9 +5,9 @@
|
||||
#include <llmr/platform/platform.hpp>
|
||||
#include "settings.hpp"
|
||||
|
||||
#include <thread>
|
||||
#include <cstdio>
|
||||
|
||||
NSString *const MBXNeedsRenderNotification = @"MBXNeedsRenderNotification";
|
||||
#include <thread>
|
||||
|
||||
class MapView {
|
||||
public:
|
||||
@@ -39,8 +39,14 @@ public:
|
||||
glfwSetWindowUserPointer(window, this);
|
||||
glfwMakeContextCurrent(window);
|
||||
|
||||
|
||||
int width, height;
|
||||
glfwGetWindowSize(window, &width, &height);
|
||||
int fb_width, fb_height;
|
||||
glfwGetFramebufferSize(window, &fb_width, &fb_height);
|
||||
|
||||
settings.load();
|
||||
map.setup();
|
||||
map.setup((double)fb_width / width);
|
||||
|
||||
resize(window, 0, 0);
|
||||
|
||||
@@ -55,15 +61,9 @@ public:
|
||||
glfwSetScrollCallback(window, scroll);
|
||||
glfwSetCharCallback(window, character);
|
||||
glfwSetKeyCallback(window, key);
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserverForName:MBXNeedsRenderNotification
|
||||
object:nil
|
||||
queue:[NSOperationQueue mainQueue]
|
||||
usingBlock: ^ (NSNotification * notification) {
|
||||
dirty = true;
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
static void character(GLFWwindow *window, unsigned int codepoint) {
|
||||
|
||||
}
|
||||
@@ -110,6 +110,7 @@ public:
|
||||
scale = 1.0 / scale;
|
||||
}
|
||||
|
||||
mapView->map.startScaling();
|
||||
mapView->map.scaleBy(scale, mapView->last_x, mapView->last_y);
|
||||
}
|
||||
|
||||
@@ -132,11 +133,14 @@ public:
|
||||
if (mapView->rotating) {
|
||||
mapView->start_x = mapView->last_x;
|
||||
mapView->start_y = mapView->last_y;
|
||||
} else {
|
||||
mapView->map.stopRotating();
|
||||
}
|
||||
} else if (button == GLFW_MOUSE_BUTTON_LEFT) {
|
||||
mapView->tracking = action == GLFW_PRESS;
|
||||
|
||||
if (action == GLFW_RELEASE) {
|
||||
mapView->map.stopPanning();
|
||||
double now = glfwGetTime();
|
||||
if (now - mapView->last_click < 0.4) {
|
||||
mapView->map.scaleBy(2.0, mapView->last_x, mapView->last_y);
|
||||
@@ -149,8 +153,14 @@ public:
|
||||
static void mousemove(GLFWwindow *window, double x, double y) {
|
||||
MapView *mapView = (MapView *)glfwGetWindowUserPointer(window);
|
||||
if (mapView->tracking) {
|
||||
mapView->map.moveBy(x - mapView->last_x, y - mapView->last_y);
|
||||
double dx = x - mapView->last_x;
|
||||
double dy = y - mapView->last_y;
|
||||
if (dx || dy) {
|
||||
mapView->map.startPanning();
|
||||
mapView->map.moveBy(dx, dy);
|
||||
}
|
||||
} else if (mapView->rotating) {
|
||||
mapView->map.startRotating();
|
||||
mapView->map.rotateBy(mapView->start_x, mapView->start_y, mapView->last_x, mapView->last_y, x, y);
|
||||
}
|
||||
mapView->last_x = x;
|
||||
@@ -217,24 +227,26 @@ public:
|
||||
};
|
||||
|
||||
MapView *mapView;
|
||||
NSOperationQueue *queue = NULL;
|
||||
NSOperationQueue *queue;
|
||||
|
||||
namespace llmr {
|
||||
namespace platform {
|
||||
|
||||
void restart(void *) {
|
||||
void restart() {
|
||||
mapView->dirty = true;
|
||||
CGEventRef event = CGEventCreate(NULL);
|
||||
CGEventSetType(event, kCGEventMouseMoved);
|
||||
[[NSApplication sharedApplication] postEvent: [NSEvent eventWithCGEvent:event] atStart:NO];
|
||||
}
|
||||
|
||||
void request_http(std::string url, std::function<void(Response&)> func, std::function<void()> cb) {
|
||||
void request_http(std::string url, std::function<void(Response&)> background_function, std::function<void()> foreground_callback) {
|
||||
if (!queue) {
|
||||
queue = [[NSOperationQueue alloc] init];
|
||||
queue = [NSOperationQueue new];
|
||||
}
|
||||
|
||||
NSMutableURLRequest *urlRequest = [NSMutableURLRequest
|
||||
requestWithURL:[NSURL
|
||||
URLWithString:[NSString
|
||||
stringWithUTF8String:url.c_str()]]];
|
||||
URLWithString:@(url.c_str())]];
|
||||
|
||||
[NSURLConnection
|
||||
sendAsynchronousRequest:urlRequest
|
||||
@@ -247,11 +259,10 @@ void request_http(std::string url, std::function<void(Response&)> func, std::fun
|
||||
res.body = { (const char *)[data bytes], [data length] };
|
||||
}
|
||||
|
||||
func(res);
|
||||
background_function(res);
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
cb();
|
||||
foreground_callback();
|
||||
});
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MBXNeedsRenderNotification object:nil];
|
||||
}];
|
||||
}
|
||||
|
||||
|
||||
Binary file not shown.
12
src/geometry/point_buffer.cpp
Normal file
12
src/geometry/point_buffer.cpp
Normal file
@@ -0,0 +1,12 @@
|
||||
#include <llmr/geometry/point_buffer.hpp>
|
||||
#include <llmr/platform/gl.hpp>
|
||||
|
||||
#include <cmath>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
void PointVertexBuffer::add(vertex_type x, vertex_type y) {
|
||||
vertex_type *vertices = static_cast<vertex_type *>(addElement());
|
||||
vertices[0] = x;
|
||||
vertices[1] = y;
|
||||
}
|
||||
@@ -15,19 +15,20 @@ Map::Map(Settings& settings)
|
||||
painter(transform, settings, style),
|
||||
min_zoom(0),
|
||||
max_zoom(14) {
|
||||
|
||||
// TODO: Extract that information from the stylesheet instead of hard coding
|
||||
style.sprite = std::make_shared<Sprite>();
|
||||
style.sprite->load(kSpriteURL);
|
||||
}
|
||||
|
||||
Map::~Map() {
|
||||
settings.sync();
|
||||
}
|
||||
|
||||
void Map::setup() {
|
||||
void Map::setup(float pixelRatio) {
|
||||
painter.setup();
|
||||
|
||||
pixel_ratio = pixelRatio;
|
||||
|
||||
style.sprite = std::make_shared<Sprite>();
|
||||
style.sprite->load(kSpriteURL, pixel_ratio);
|
||||
|
||||
style.load(resources::style, resources::style_size);
|
||||
// style.loadJSON((const char *)resources::style, resources::style_size);
|
||||
}
|
||||
@@ -54,7 +55,7 @@ void Map::resize(uint32_t width, uint32_t height, uint32_t fb_width, uint32_t fb
|
||||
transform.height = height;
|
||||
transform.fb_width = fb_width;
|
||||
transform.fb_height = fb_height;
|
||||
transform.pixelRatio = (double)fb_width / (double)width;
|
||||
transform.pixelRatio = pixel_ratio;
|
||||
update();
|
||||
}
|
||||
|
||||
@@ -66,6 +67,16 @@ void Map::moveBy(double dx, double dy, double duration) {
|
||||
settings.persist();
|
||||
}
|
||||
|
||||
void Map::startPanning() {
|
||||
transform.startPanning();
|
||||
platform::restart();
|
||||
}
|
||||
|
||||
void Map::stopPanning() {
|
||||
transform.stopPanning();
|
||||
platform::restart();
|
||||
}
|
||||
|
||||
void Map::scaleBy(double ds, double cx, double cy, double duration) {
|
||||
transform.scaleBy(ds, cx, cy, duration);
|
||||
style.cascade(transform.getZoom());
|
||||
@@ -76,6 +87,16 @@ void Map::scaleBy(double ds, double cx, double cy, double duration) {
|
||||
settings.persist();
|
||||
}
|
||||
|
||||
void Map::startScaling() {
|
||||
transform.startScaling();
|
||||
platform::restart();
|
||||
}
|
||||
|
||||
void Map::stopScaling() {
|
||||
transform.stopScaling();
|
||||
platform::restart();
|
||||
}
|
||||
|
||||
void Map::rotateBy(double cx, double cy, double sx, double sy, double ex, double ey, double duration) {
|
||||
transform.rotateBy(cx, cy, sx, sy, ex, ey, duration);
|
||||
update();
|
||||
@@ -84,6 +105,16 @@ void Map::rotateBy(double cx, double cy, double sx, double sy, double ex, double
|
||||
settings.persist();
|
||||
}
|
||||
|
||||
void Map::startRotating() {
|
||||
transform.startRotating();
|
||||
platform::restart();
|
||||
}
|
||||
|
||||
void Map::stopRotating() {
|
||||
transform.stopRotating();
|
||||
platform::restart();
|
||||
}
|
||||
|
||||
void Map::setLonLat(double lon, double lat, double duration) {
|
||||
transform.setLonLat(lon, lat, duration);
|
||||
update();
|
||||
@@ -92,7 +123,7 @@ void Map::setLonLat(double lon, double lat, double duration) {
|
||||
settings.persist();
|
||||
}
|
||||
|
||||
void Map::getLonLat(double &lon, double &lat) const {
|
||||
void Map::getLonLat(double& lon, double& lat) const {
|
||||
transform.getLonLat(lon, lat);
|
||||
}
|
||||
|
||||
@@ -106,7 +137,7 @@ void Map::setLonLatZoom(double lon, double lat, double zoom, double duration) {
|
||||
settings.persist();
|
||||
}
|
||||
|
||||
void Map::getLonLatZoom(double &lon, double &lat, double &zoom) const {
|
||||
void Map::getLonLatZoom(double& lon, double& lat, double& zoom) const {
|
||||
transform.getLonLatZoom(lon, lat, zoom);
|
||||
}
|
||||
|
||||
@@ -202,7 +233,7 @@ void Map::cancelAnimations() {
|
||||
|
||||
void Map::update() {
|
||||
updateTiles();
|
||||
platform::restart(this);
|
||||
platform::restart();
|
||||
}
|
||||
|
||||
|
||||
@@ -375,12 +406,12 @@ bool Map::updateTiles() {
|
||||
}
|
||||
|
||||
bool Map::render() {
|
||||
bool changed = false;
|
||||
if (transform.needsAnimation()) {
|
||||
transform.updateAnimations();
|
||||
changed = updateTiles();
|
||||
}
|
||||
|
||||
bool changed = updateTiles();
|
||||
|
||||
painter.clear();
|
||||
|
||||
for (Tile::Ptr& tile : tiles) {
|
||||
@@ -395,14 +426,3 @@ bool Map::render() {
|
||||
|
||||
return changed || transform.needsAnimation();
|
||||
}
|
||||
|
||||
void Map::tileLoaded(Tile::Ptr tile) {
|
||||
// std::cerr << "loaded " << tile->toString() << std::endl;
|
||||
update();
|
||||
}
|
||||
|
||||
void Map::tileFailed(Tile::Ptr tile) {
|
||||
// fprintf(stderr, "[%8zx] tile failed to load %d/%d/%d\n",
|
||||
// std::hash<std::thread::id>()(std::this_thread::get_id()),
|
||||
// tile->z, tile->x, tile->y);
|
||||
}
|
||||
|
||||
@@ -4,9 +4,11 @@
|
||||
#include <llmr/map/vector_tile.hpp>
|
||||
#include <llmr/geometry/fill_buffer.hpp>
|
||||
#include <llmr/geometry/line_buffer.hpp>
|
||||
#include <llmr/geometry/point_buffer.hpp>
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/renderer/line_bucket.hpp>
|
||||
#include <llmr/renderer/point_bucket.hpp>
|
||||
#include <llmr/platform/platform.hpp>
|
||||
#include <llmr/util/pbf.hpp>
|
||||
#include <llmr/util/string.hpp>
|
||||
@@ -45,6 +47,7 @@ Tile::Tile(ID id, const Style& style)
|
||||
state(initial),
|
||||
fillVertexBuffer(std::make_shared<FillVertexBuffer>()),
|
||||
lineVertexBuffer(std::make_shared<LineVertexBuffer>()),
|
||||
pointVertexBuffer(std::make_shared<PointVertexBuffer>()),
|
||||
triangleElementsBuffer(std::make_shared<TriangleElementsBuffer>()),
|
||||
lineElementsBuffer(std::make_shared<LineElementsBuffer>()),
|
||||
pointElementsBuffer(std::make_shared<PointElementsBuffer>()),
|
||||
@@ -80,8 +83,7 @@ void Tile::request() {
|
||||
fprintf(stderr, "tile loading failed\n");
|
||||
}
|
||||
}, []() {
|
||||
// TODO: Make sure this gets passed the correct map ID/pointer.
|
||||
platform::restart(nullptr);
|
||||
platform::restart();
|
||||
});
|
||||
}
|
||||
|
||||
@@ -159,6 +161,8 @@ std::shared_ptr<Bucket> Tile::createBucket(const VectorTile& tile, const BucketD
|
||||
return createFillBucket(layer, bucket_desc);
|
||||
} else if (bucket_desc.type == BucketType::Line) {
|
||||
return createLineBucket(layer, bucket_desc);
|
||||
} else if (bucket_desc.type == BucketType::Point) {
|
||||
return createPointBucket(layer, bucket_desc);
|
||||
} else {
|
||||
// TODO: create other bucket types.
|
||||
}
|
||||
@@ -200,3 +204,19 @@ std::shared_ptr<Bucket> Tile::createLineBucket(const VectorTileLayer& layer, con
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
std::shared_ptr<Bucket> Tile::createPointBucket(const VectorTileLayer& layer, const BucketDescription& bucket_desc) {
|
||||
std::shared_ptr<PointBucket> bucket = std::make_shared<PointBucket>(pointVertexBuffer, bucket_desc);
|
||||
|
||||
FilteredVectorTileLayer filtered_layer(layer, bucket_desc);
|
||||
for (pbf feature : filtered_layer) {
|
||||
while (feature.next(4)) { // geometry
|
||||
pbf geometry_pbf = feature.message();
|
||||
if (geometry_pbf) {
|
||||
bucket->addGeometry(geometry_pbf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return bucket;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
#include <llmr/map/transform.hpp>
|
||||
#include <llmr/util/constants.hpp>
|
||||
#include <llmr/util/mat4.hpp>
|
||||
#include <llmr/util/std.hpp>
|
||||
#include <llmr/util/math.hpp>
|
||||
#include <cstdio>
|
||||
|
||||
@@ -24,8 +25,8 @@ bool Transform::needsAnimation() const {
|
||||
}
|
||||
|
||||
void Transform::updateAnimations() {
|
||||
animations.remove_if([](const util::animation& animation) {
|
||||
return animation.update() == util::animation::complete;
|
||||
animations.remove_if([](const std::shared_ptr<util::animation>& animation) {
|
||||
return animation->update() == util::animation::complete;
|
||||
});
|
||||
}
|
||||
|
||||
@@ -40,8 +41,25 @@ void Transform::moveBy(double dx, double dy, double duration) {
|
||||
x = xn;
|
||||
y = yn;
|
||||
} else {
|
||||
animations.emplace_front(x, xn, x, duration);
|
||||
animations.emplace_front(y, yn, y, duration);
|
||||
animations.emplace_front(std::make_shared<util::ease_animation>(x, xn, x, duration));
|
||||
animations.emplace_front(std::make_shared<util::ease_animation>(y, yn, y, duration));
|
||||
}
|
||||
}
|
||||
|
||||
void Transform::startPanning() {
|
||||
stopPanning();
|
||||
|
||||
// Add a 200ms timeout for resetting this to false
|
||||
panning = true;
|
||||
pan_timeout = std::make_shared<util::timeout<bool>>(false, panning, 0.2);
|
||||
animations.emplace_front(pan_timeout);
|
||||
}
|
||||
|
||||
void Transform::stopPanning() {
|
||||
panning = false;
|
||||
if (pan_timeout) {
|
||||
animations.remove(pan_timeout);
|
||||
pan_timeout.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,6 +77,22 @@ void Transform::scaleBy(double ds, double cx, double cy, double duration) {
|
||||
setScale(new_scale, cx, cy, duration);
|
||||
}
|
||||
|
||||
void Transform::startScaling() {
|
||||
stopScaling();
|
||||
|
||||
// Add a 200ms timeout for resetting this to false
|
||||
scaling = true;
|
||||
scale_timeout = std::make_shared<util::timeout<bool>>(false, scaling, 0.2);
|
||||
animations.emplace_front(scale_timeout);
|
||||
}
|
||||
|
||||
void Transform::stopScaling() {
|
||||
scaling = false;
|
||||
if (scale_timeout) {
|
||||
animations.remove(scale_timeout);
|
||||
scale_timeout.reset();
|
||||
}
|
||||
}
|
||||
|
||||
void Transform::rotateBy(double anchor_x, double anchor_y, double start_x, double start_y, double end_x, double end_y, double duration) {
|
||||
double center_x = width / 2, center_y = height / 2;
|
||||
@@ -94,7 +128,24 @@ void Transform::setAngle(double new_angle, double duration) {
|
||||
if (duration == 0) {
|
||||
angle = new_angle;
|
||||
} else {
|
||||
animations.emplace_front(angle, new_angle, angle, duration);
|
||||
animations.emplace_front(std::make_shared<util::ease_animation>(angle, new_angle, angle, duration));
|
||||
}
|
||||
}
|
||||
|
||||
void Transform::startRotating() {
|
||||
stopRotating();
|
||||
|
||||
// Add a 200ms timeout for resetting this to false
|
||||
rotating = true;
|
||||
rotate_timeout = std::make_shared<util::timeout<bool>>(false, rotating, 0.2);
|
||||
animations.emplace_front(rotate_timeout);
|
||||
}
|
||||
|
||||
void Transform::stopRotating() {
|
||||
rotating = false;
|
||||
if (rotate_timeout) {
|
||||
animations.remove(rotate_timeout);
|
||||
rotate_timeout.reset();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -104,9 +155,9 @@ void Transform::setScaleXY(double new_scale, double xn, double yn, double durati
|
||||
x = xn;
|
||||
y = yn;
|
||||
} else {
|
||||
animations.emplace_front(scale, new_scale, scale, duration);
|
||||
animations.emplace_front(x, xn, x, duration);
|
||||
animations.emplace_front(y, yn, y, duration);
|
||||
animations.emplace_front(std::make_shared<util::ease_animation>(scale, new_scale, scale, duration));
|
||||
animations.emplace_front(std::make_shared<util::ease_animation>(x, xn, x, duration));
|
||||
animations.emplace_front(std::make_shared<util::ease_animation>(y, yn, y, duration));
|
||||
}
|
||||
|
||||
const double s = scale * util::tileSize;
|
||||
|
||||
@@ -7,6 +7,7 @@
|
||||
|
||||
#include <llmr/renderer/fill_bucket.hpp>
|
||||
#include <llmr/renderer/line_bucket.hpp>
|
||||
#include <llmr/renderer/point_bucket.hpp>
|
||||
|
||||
#include <llmr/map/transform.hpp>
|
||||
#include <llmr/map/settings.hpp>
|
||||
@@ -29,6 +30,7 @@ Painter::Painter(Transform& transform, Settings& settings, Style& style)
|
||||
void Painter::setup() {
|
||||
setupShaders();
|
||||
|
||||
assert(pointShader);
|
||||
assert(plainShader);
|
||||
assert(outlineShader);
|
||||
assert(lineShader);
|
||||
@@ -52,6 +54,7 @@ void Painter::setupShaders() {
|
||||
lineShader = std::make_unique<LineShader>();
|
||||
linejoinShader = std::make_unique<LinejoinShader>();
|
||||
patternShader = std::make_unique<PatternShader>();
|
||||
pointShader = std::make_unique<PointShader>();
|
||||
}
|
||||
|
||||
void Painter::useProgram(uint32_t program) {
|
||||
@@ -377,6 +380,44 @@ void Painter::renderLine(LineBucket& bucket, const std::string& layer_name, cons
|
||||
}
|
||||
}
|
||||
|
||||
void Painter::renderPoint(PointBucket& bucket, const std::string& layer_name, const Tile::ID& id) {
|
||||
const PointProperties& properties = style.computed.points[layer_name];
|
||||
|
||||
// Abort early.
|
||||
if (!bucket.hasPoints()) return;
|
||||
if (properties.hidden) return;
|
||||
|
||||
Color color = properties.color;
|
||||
color[0] *= properties.opacity;
|
||||
color[1] *= properties.opacity;
|
||||
color[2] *= properties.opacity;
|
||||
color[3] *= properties.opacity;
|
||||
|
||||
std::string sized_image = properties.image;
|
||||
sized_image.append("-");
|
||||
sized_image.append(std::to_string(static_cast<int>(std::round(properties.size))));
|
||||
|
||||
ImagePosition imagePos = style.sprite->getPosition(sized_image, false);
|
||||
|
||||
// fprintf(stderr, "%f/%f => %f/%f\n", imagePos.tl.x, imagePos.tl.y, imagePos.br.x, imagePos.br.y);
|
||||
|
||||
useProgram(pointShader->program);
|
||||
pointShader->setMatrix(matrix);
|
||||
pointShader->setImage(0);
|
||||
pointShader->setColor(color);
|
||||
const float pointSize = properties.size * 1.4142135623730951;
|
||||
#if defined(GL_ES_VERSION_2_0)
|
||||
pointShader->setSize(pointSize);
|
||||
#else
|
||||
glPointSize(pointSize);
|
||||
glEnable(GL_POINT_SPRITE);
|
||||
#endif
|
||||
pointShader->setPointTopLeft({{ imagePos.tl.x, imagePos.tl.y }});
|
||||
pointShader->setPointBottomRight({{ imagePos.br.x, imagePos.br.y }});
|
||||
style.sprite->bind(transform.rotating || transform.scaling || transform.panning);
|
||||
bucket.drawPoints(*pointShader);
|
||||
}
|
||||
|
||||
void Painter::renderDebug(const Tile::Ptr& tile) {
|
||||
// Blend to the front, not the back.
|
||||
glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
|
||||
|
||||
52
src/renderer/point_bucket.cpp
Normal file
52
src/renderer/point_bucket.cpp
Normal file
@@ -0,0 +1,52 @@
|
||||
#include <llmr/renderer/point_bucket.hpp>
|
||||
#include <llmr/geometry/point_buffer.hpp>
|
||||
#include <llmr/geometry/elements_buffer.hpp>
|
||||
#include <llmr/geometry/geometry.hpp>
|
||||
|
||||
#include <llmr/renderer/painter.hpp>
|
||||
#include <llmr/style/style.hpp>
|
||||
#include <llmr/map/vector_tile.hpp>
|
||||
|
||||
#include <llmr/platform/gl.hpp>
|
||||
|
||||
#include <cassert>
|
||||
|
||||
struct geometry_too_long_exception : std::exception {};
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
PointBucket::PointBucket(const std::shared_ptr<PointVertexBuffer>& vertexBuffer,
|
||||
const BucketDescription& bucket_desc)
|
||||
: geometry(bucket_desc.geometry),
|
||||
vertexBuffer(vertexBuffer),
|
||||
vertex_start(vertexBuffer->index()) {
|
||||
}
|
||||
|
||||
void PointBucket::addGeometry(pbf& geom) {
|
||||
Geometry::command cmd;
|
||||
Geometry geometry(geom);
|
||||
int32_t x, y;
|
||||
while ((cmd = geometry.next(x, y)) != Geometry::end) {
|
||||
if (cmd == Geometry::move_to) {
|
||||
vertexBuffer->add(x, y);
|
||||
} else {
|
||||
fprintf(stderr, "other command than move_to in point geometry\n");
|
||||
}
|
||||
}
|
||||
|
||||
vertex_end = vertexBuffer->index();
|
||||
}
|
||||
|
||||
void PointBucket::render(Painter& painter, const std::string& layer_name, const Tile::ID& id) {
|
||||
painter.renderPoint(*this, layer_name, id);
|
||||
}
|
||||
|
||||
bool PointBucket::hasPoints() const {
|
||||
return vertex_end > 0;
|
||||
}
|
||||
|
||||
void PointBucket::drawPoints(PointShader& shader) {
|
||||
char *vertex_index = BUFFER_OFFSET(vertex_start * vertexBuffer->itemSize);
|
||||
array.bind(shader, *vertexBuffer, vertex_index);
|
||||
glDrawArrays(GL_POINTS, 0, vertex_end - vertex_start);
|
||||
}
|
||||
74
src/renderer/shader-point.cpp
Normal file
74
src/renderer/shader-point.cpp
Normal file
@@ -0,0 +1,74 @@
|
||||
#include <llmr/renderer/shader-point.hpp>
|
||||
#include <llmr/shader/shaders.hpp>
|
||||
#include <llmr/platform/gl.hpp>
|
||||
|
||||
#include <cstdio>
|
||||
|
||||
using namespace llmr;
|
||||
|
||||
PointShader::PointShader()
|
||||
: Shader(
|
||||
shaders[POINT_SHADER].vertex,
|
||||
shaders[POINT_SHADER].fragment
|
||||
) {
|
||||
if (!valid) {
|
||||
fprintf(stderr, "invalid point shader\n");
|
||||
return;
|
||||
}
|
||||
|
||||
a_pos = glGetAttribLocation(program, "a_pos");
|
||||
|
||||
u_matrix = glGetUniformLocation(program, "u_matrix");
|
||||
u_color = glGetUniformLocation(program, "u_color");
|
||||
u_size = glGetUniformLocation(program, "u_size");
|
||||
u_point_tl = glGetUniformLocation(program, "u_tl");
|
||||
u_point_br = glGetUniformLocation(program, "u_br");
|
||||
|
||||
// fprintf(stderr, "PointShader:\n");
|
||||
// fprintf(stderr, " - u_matrix: %d\n", u_matrix);
|
||||
// fprintf(stderr, " - u_color: %d\n", u_color);
|
||||
// fprintf(stderr, " - u_size: %d\n", u_size);
|
||||
// fprintf(stderr, " - u_point_tl: %d\n", u_point_tl);
|
||||
// fprintf(stderr, " - u_point_br: %d\n", u_point_br);
|
||||
// fprintf(stderr, " - u_image: %d\n", u_image);
|
||||
}
|
||||
|
||||
void PointShader::bind(char *offset) {
|
||||
glEnableVertexAttribArray(a_pos);
|
||||
glVertexAttribPointer(a_pos, 2, GL_SHORT, false, 0, offset);
|
||||
}
|
||||
|
||||
void PointShader::setImage(int32_t new_image) {
|
||||
if (image != new_image) {
|
||||
glUniform1i(u_image, new_image);
|
||||
image = new_image;
|
||||
}
|
||||
}
|
||||
|
||||
void PointShader::setColor(const std::array<float, 4>& new_color) {
|
||||
if (color != new_color) {
|
||||
glUniform4fv(u_color, 1, new_color.data());
|
||||
color = new_color;
|
||||
}
|
||||
}
|
||||
|
||||
void PointShader::setSize(float new_size) {
|
||||
if (size != new_size) {
|
||||
glUniform1f(u_size, new_size);
|
||||
size = new_size;
|
||||
}
|
||||
}
|
||||
|
||||
void PointShader::setPointTopLeft(const std::array<float, 2>& new_point_tl) {
|
||||
if (point_tl != new_point_tl) {
|
||||
glUniform2fv(u_point_tl, 1, new_point_tl.data());
|
||||
point_tl = new_point_tl;
|
||||
}
|
||||
}
|
||||
|
||||
void PointShader::setPointBottomRight(const std::array<float, 2>& new_point_br) {
|
||||
if (point_br != new_point_br) {
|
||||
glUniform2fv(u_point_br, 1, new_point_br.data());
|
||||
point_br = new_point_br;
|
||||
}
|
||||
}
|
||||
21
src/shader/point.fragment.glsl
Normal file
21
src/shader/point.fragment.glsl
Normal file
@@ -0,0 +1,21 @@
|
||||
#define root2 1.4142135623730951
|
||||
|
||||
uniform sampler2D u_image;
|
||||
uniform vec2 u_tl;
|
||||
uniform vec2 u_br;
|
||||
uniform vec4 u_color;
|
||||
|
||||
uniform vec2 pos;
|
||||
uniform float inbounds;
|
||||
uniform vec4 color;
|
||||
|
||||
void main() {
|
||||
vec2 pos = (gl_PointCoord * 2.0 - 1.0) * root2 / 2.0 + 0.5;
|
||||
|
||||
float inbounds = step(0.0, pos.x) * step(0.0, pos.y) *
|
||||
(1.0 - step(1.0, pos.x)) * (1.0 - step(1.0, pos.y));
|
||||
|
||||
vec4 color = texture2D(u_image, mix(u_tl, u_br, pos)) * inbounds;
|
||||
|
||||
gl_FragColor = color;
|
||||
}
|
||||
9
src/shader/point.vertex.glsl
Normal file
9
src/shader/point.vertex.glsl
Normal file
@@ -0,0 +1,9 @@
|
||||
attribute vec2 a_pos;
|
||||
|
||||
uniform mat4 u_matrix;
|
||||
uniform float u_size;
|
||||
|
||||
void main() {
|
||||
gl_Position = u_matrix * vec4(a_pos, 0, 1);
|
||||
gl_PointSize = u_size;
|
||||
}
|
||||
@@ -23,5 +23,9 @@ const shader_source llmr::shaders[SHADER_COUNT] = {
|
||||
{
|
||||
"attribute vec2 a_pos;\n\nuniform mat4 u_matrix;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n}\n",
|
||||
"uniform vec4 u_color;\n\nvoid main() {\n gl_FragColor = u_color;\n}\n",
|
||||
},
|
||||
{
|
||||
"attribute vec2 a_pos;\n\nuniform mat4 u_matrix;\nuniform float u_size;\n\nvoid main() {\n gl_Position = u_matrix * vec4(a_pos, 0, 1);\n gl_PointSize = u_size;\n}\n",
|
||||
"#define root2 1.4142135623730951\n\nuniform sampler2D u_image;\nuniform vec2 u_tl;\nuniform vec2 u_br;\nuniform vec4 u_color;\n\nuniform vec2 pos;\nuniform float inbounds;\nuniform vec4 color;\n\nvoid main() {\n vec2 pos = (gl_PointCoord * 2.0 - 1.0) * root2 / 2.0 + 0.5;\n\n float inbounds = step(0.0, pos.x) * step(0.0, pos.y) *\n (1.0 - step(1.0, pos.x)) * (1.0 - step(1.0, pos.y));\n\n vec4 color = texture2D(u_image, mix(u_tl, u_br, pos)) * inbounds;\n\n gl_FragColor = color;\n}\n",
|
||||
}
|
||||
};
|
||||
|
||||
@@ -47,7 +47,7 @@ const unsigned char resources::style[] = {
|
||||
12, 114, 111, 97, 100, 95, 114, 101, 103, 117, 108, 97, 114, 18, 24, 10,
|
||||
10, 114, 111, 97, 100, 95, 108, 97, 114, 103, 101, 18, 10, 114, 111, 97,
|
||||
100, 95, 108, 97, 114, 103, 101, 18, 18, 10, 7, 97, 108, 99, 111, 104,
|
||||
111, 108, 18, 7, 97, 108, 99, 111, 104, 111, 108, 26, 220, 2, 10, 7,
|
||||
111, 108, 18, 7, 97, 108, 99, 111, 104, 111, 108, 26, 132, 3, 10, 7,
|
||||
100, 101, 102, 97, 117, 108, 116, 18, 27, 10, 4, 112, 97, 114, 107, 34,
|
||||
8, 8, 2, 18, 4, 0, 0, 128, 63, 45, 255, 159, 223, 200, 66, 4,
|
||||
112, 97, 114, 107, 18, 31, 10, 4, 119, 111, 111, 100, 34, 8, 8, 2,
|
||||
@@ -69,6 +69,9 @@ const unsigned char resources::style[] = {
|
||||
255, 102, 102, 102, 34, 52, 8, 3, 18, 48, 0, 0, 0, 0, 0, 0,
|
||||
0, 63, 0, 0, 48, 65, 0, 0, 0, 63, 0, 0, 80, 65, 0, 0,
|
||||
128, 63, 0, 0, 128, 65, 0, 0, 128, 64, 0, 0, 160, 65, 0, 0,
|
||||
128, 66, 0, 0, 240, 65, 0, 0, 128, 66
|
||||
128, 66, 0, 0, 240, 65, 0, 0, 128, 66, 34, 38, 10, 7, 97, 108,
|
||||
99, 111, 104, 111, 108, 29, 255, 153, 153, 153, 34, 8, 8, 2, 18, 4,
|
||||
0, 0, 192, 65, 66, 12, 97, 108, 99, 111, 104, 111, 108, 45, 115, 104,
|
||||
111, 112
|
||||
};
|
||||
const unsigned long resources::style_size = sizeof(resources::style);
|
||||
|
||||
@@ -34,19 +34,21 @@ Sprite::operator bool() const {
|
||||
return loaded;
|
||||
}
|
||||
|
||||
void Sprite::load(const std::string& base_url) {
|
||||
void Sprite::load(const std::string& base_url, float pixelRatio) {
|
||||
std::shared_ptr<Sprite> sprite = shared_from_this();
|
||||
|
||||
auto complete = [sprite]() {
|
||||
std::lock_guard<std::mutex> lock(sprite->mtx);
|
||||
if (sprite->img && sprite->pos.size()) {
|
||||
sprite->loaded = true;
|
||||
platform::restart(NULL);
|
||||
platform::restart();
|
||||
fprintf(stderr, "sprite loaded\n");
|
||||
}
|
||||
};
|
||||
|
||||
platform::request_http(base_url + ".json", [sprite](const platform::Response & res) {
|
||||
std::string suffix = (pixelRatio > 1 ? "@2x" : "");
|
||||
|
||||
platform::request_http(base_url + suffix + ".json", [sprite](const platform::Response & res) {
|
||||
if (res.code == 200) {
|
||||
sprite->parseJSON(res.body);
|
||||
} else {
|
||||
@@ -54,7 +56,7 @@ void Sprite::load(const std::string& base_url) {
|
||||
}
|
||||
}, complete);
|
||||
|
||||
platform::request_http(base_url + ".png", [sprite](const platform::Response & res) {
|
||||
platform::request_http(base_url + suffix + ".png", [sprite](const platform::Response & res) {
|
||||
if (res.code == 200) {
|
||||
sprite->loadImage(res.body);
|
||||
} else {
|
||||
@@ -242,11 +244,13 @@ void Sprite::bind(bool linear) {
|
||||
|
||||
if (!texture) {
|
||||
glGenTextures(1, &texture);
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
|
||||
glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
|
||||
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, img);
|
||||
} else {
|
||||
glActiveTexture(GL_TEXTURE0);
|
||||
glBindTexture(GL_TEXTURE_2D, texture);
|
||||
}
|
||||
|
||||
|
||||
@@ -12,6 +12,7 @@ Style::Style() {
|
||||
void Style::reset() {
|
||||
computed.fills.clear();
|
||||
computed.lines.clear();
|
||||
computed.points.clear();
|
||||
}
|
||||
|
||||
void Style::load(const uint8_t *const data, uint32_t bytes) {
|
||||
@@ -89,10 +90,12 @@ std::pair<std::string, ClassDescription> Style::parseClass(pbf data) {
|
||||
while (data.next()) {
|
||||
if (data.tag == 1) { // name
|
||||
name = data.string();
|
||||
} else if (data.tag == 2) { // fill_style
|
||||
} else if (data.tag == 2) { // fill style
|
||||
klass.fill.insert(parseFillClass(data.message()));
|
||||
} else if (data.tag == 3) { // stroke_style
|
||||
} else if (data.tag == 3) { // line style
|
||||
klass.line.insert(parseLineClass(data.message()));
|
||||
} else if (data.tag == 4) { // point style
|
||||
klass.point.insert(parsePointClass(data.message()));
|
||||
} else {
|
||||
data.skip();
|
||||
}
|
||||
@@ -159,6 +162,30 @@ std::pair<std::string, LineClass> Style::parseLineClass(pbf data) {
|
||||
return { name, stroke };
|
||||
}
|
||||
|
||||
std::pair<std::string, PointClass> Style::parsePointClass(pbf data) {
|
||||
PointClass point;
|
||||
std::string name;
|
||||
|
||||
while (data.next()) {
|
||||
if (data.tag == 1) { // name
|
||||
name = data.string();
|
||||
} else if (data.tag == 2) { // hidden
|
||||
point.hidden = parseProperty<bool>(data.message());
|
||||
} else if (data.tag == 3) { // color
|
||||
point.color = parseColor(data);
|
||||
} else if (data.tag == 4) { // size
|
||||
point.size = parseProperty<float>(data.message());
|
||||
} else if (data.tag == 6) { // opacity
|
||||
point.opacity = parseProperty<float>(data.message());
|
||||
} else if (data.tag == 8) { // image
|
||||
point.image = data.string();
|
||||
} else {
|
||||
data.skip();
|
||||
}
|
||||
}
|
||||
|
||||
return { name, point };
|
||||
}
|
||||
|
||||
Color Style::parseColor(pbf& data) {
|
||||
uint32_t rgba = data.fixed<uint32_t, 4>();
|
||||
@@ -240,6 +267,21 @@ void Style::cascade(float z) {
|
||||
stroke.color = layer.color;
|
||||
stroke.opacity = layer.opacity(z);
|
||||
}
|
||||
|
||||
// Cascade point classes
|
||||
for (const auto& point_pair : sheetClass.point) {
|
||||
const std::string& layer_name = point_pair.first;
|
||||
const llmr::PointClass& layer = point_pair.second;
|
||||
|
||||
// TODO: This should be restricted to point styles that have actual
|
||||
// values so as to not override with default values.
|
||||
llmr::PointProperties& point = computed.points[layer_name];
|
||||
point.hidden = layer.hidden(z);
|
||||
point.color = layer.color;
|
||||
point.size = layer.size(z);
|
||||
point.opacity = layer.opacity(z);
|
||||
point.image = layer.image;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -6,16 +6,17 @@ using namespace llmr::util;
|
||||
|
||||
UnitBezier ease(0.25, 0.1, 0.25, 1);
|
||||
|
||||
animation::animation(double from, double to, double &value, double duration)
|
||||
: start(platform::time()),
|
||||
duration(duration),
|
||||
animation::~animation() {}
|
||||
|
||||
ease_animation::ease_animation(double from, double to, double &value, double duration)
|
||||
: animation(duration),
|
||||
from(from),
|
||||
to(to),
|
||||
value(value) {
|
||||
}
|
||||
|
||||
animation::state animation::update() const {
|
||||
double t = (platform::time() - start) / duration;
|
||||
animation::state ease_animation::update() const {
|
||||
double t = progress();
|
||||
if (t >= 1) {
|
||||
value = to;
|
||||
return complete;
|
||||
|
||||
Reference in New Issue
Block a user