Merge branch 'master' into tessellation

Conflicts:
	include/llmr/renderer/fill_bucket.hpp
	src/renderer/fill_bucket.cpp
	src/renderer/painter.cpp
This commit is contained in:
Konstantin Käfer
2014-02-28 12:25:45 +01:00
55 changed files with 1313 additions and 370 deletions

View File

@@ -10,8 +10,8 @@ using namespace llmr;
void DebugFontBuffer::addText(const char *text, double left, double baseline, double scale) {
uint16_t *coords = nullptr;
int32_t length = strlen(text);
for (int32_t i = 0; i < length; ++i) {
size_t length = strlen(text);
for (size_t i = 0; i < length; ++i) {
if (text[i] < 32 || text[i] > 127) {
continue;
}

View 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;
}

View File

@@ -12,7 +12,7 @@ VertexBuffer::~VertexBuffer() {
}
}
uint32_t VertexBuffer::index() const {
size_t VertexBuffer::index() const {
// We store 2 coordinates per vertex + 1 linesofar + 1 extrude coord pair == 4 (== 8 bytes)
return array.size() / 2;
}

View File

@@ -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);
}

View File

@@ -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>
@@ -46,6 +48,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>()),
@@ -81,8 +84,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();
});
}
@@ -160,6 +162,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.
}
@@ -214,3 +218,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;
}

View File

@@ -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;

View File

@@ -52,13 +52,13 @@ FilteredVectorTileLayer::FilteredVectorTileLayer(const VectorTileLayer& layer, c
// Find out what key/value IDs we need.
auto key_it = std::find(layer.keys.begin(), layer.keys.end(), bucket_desc.source_field);
if (key_it != layer.keys.end()) {
key = key_it - layer.keys.begin();
key = (int32_t)(key_it - layer.keys.begin());
}
for (const Value& source_value : bucket_desc.source_value) {
auto value_it = std::find(layer.values.begin(), layer.values.end(), source_value);
if (value_it != layer.values.end()) {
values.insert(value_it - layer.values.begin());
values.insert((uint32_t)(value_it - layer.values.begin()));
}
}
}

View File

@@ -96,7 +96,6 @@ void FillBucket::tessellate() {
}
hasVertices = false;
std::vector<std::vector<ClipperLib::IntPoint>> polygons;
clipper.Execute(ClipperLib::ctUnion, polygons, ClipperLib::pftPositive);
clipper.Clear();

View File

@@ -103,7 +103,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
nextNormal = util::normal<double>(currentVertex, lastVertex);
}
size_t start_vertex = vertexBuffer->index();
int32_t start_vertex = (int32_t)vertexBuffer->index();
std::vector<TriangleElement> triangle_store;
std::vector<PointElement> point_store;
@@ -174,7 +174,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
// Add offset square begin cap.
if (!prevVertex && beginCap == CapType::Square) {
// Add first vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * (prevNormal.x + prevNormal.y), flip * (-prevNormal.x + prevNormal.y), // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -182,7 +182,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * (prevNormal.x - prevNormal.y), flip * (prevNormal.x + prevNormal.y), // extrude normal
0, 1, distance) - start_vertex; // texture normal
@@ -193,7 +193,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
// Add offset square end cap.
else if (!nextVertex && endCap == CapType::Square) {
// Add first vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
nextNormal.x - flip * nextNormal.y, flip * nextNormal.x + nextNormal.y, // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -201,7 +201,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
nextNormal.x + flip * nextNormal.y, -flip * nextNormal.x + nextNormal.y, // extrude normal
0, 1, distance) - start_vertex; // texture normal
@@ -227,7 +227,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
}
// Add first vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * joinNormal.x, flip * joinNormal.y, // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -235,7 +235,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
-flip * joinNormal.x, -flip * joinNormal.y, // extrude normal
0, 1, distance) - start_vertex; // texture normal
@@ -251,7 +251,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
else {
// Close up the previous line
// Add first vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * prevNormal.y, -flip * prevNormal.x, // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -259,7 +259,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex.
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
-flip * prevNormal.y, flip * prevNormal.x, // extrude normal
0, 1, distance) - start_vertex; // texture normal
@@ -289,7 +289,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
// Start the new quad.
// Add first vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
-flip * nextNormal.y, flip * nextNormal.x, // extrude normal
0, 0, distance) - start_vertex; // texture normal
@@ -297,7 +297,7 @@ void LineBucket::addGeometry(const std::vector<Coordinate>& vertices) {
e1 = e2; e2 = e3;
// Add second vertex
e3 = vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
e3 = (int32_t)vertexBuffer->add(currentVertex.x, currentVertex.y, // vertex pos
flip * nextNormal.y, -flip * nextNormal.x, // extrude normal
0, 1, distance) - start_vertex; // texture normal

View File

@@ -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) {
@@ -114,7 +117,7 @@ void Painter::drawClippingMask() {
// Draw the clipping mask
plainShader->setColor(1.0f, 0.0f, 1.0f, 1.0f);
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.index());
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
// glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_EQUAL, 0x80, 0x80);
@@ -360,6 +363,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 * transform.pixelRatio;
#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);
@@ -370,16 +411,16 @@ void Painter::renderDebug(const Tile::Ptr& tile) {
tileBorderArray.bind(*plainShader, tileBorderBuffer, BUFFER_OFFSET(0));
plainShader->setColor(1.0f, 0.0f, 0.0f, 1.0f);
lineWidth(4.0f * transform.pixelRatio);
glDrawArrays(GL_LINE_STRIP, 0, tileBorderBuffer.index());
glDrawArrays(GL_LINE_STRIP, 0, (GLsizei)tileBorderBuffer.index());
// draw debug info
tile->debugFontArray.bind(*plainShader, tile->debugFontBuffer, BUFFER_OFFSET(0));
plainShader->setColor(1.0f, 1.0f, 1.0f, 1.0f);
lineWidth(4.0f * transform.pixelRatio);
glDrawArrays(GL_LINES, 0, tile->debugFontBuffer.index());
glDrawArrays(GL_LINES, 0, (GLsizei)tile->debugFontBuffer.index());
plainShader->setColor(0.0f, 0.0f, 0.0f, 1.0f);
lineWidth(2.0f * transform.pixelRatio);
glDrawArrays(GL_LINES, 0, tile->debugFontBuffer.index());
glDrawArrays(GL_LINES, 0, (GLsizei)tile->debugFontBuffer.index());
// Revert blending mode to blend to the back.
glBlendFunc(GL_ONE_MINUS_DST_ALPHA, GL_ONE);
@@ -396,7 +437,7 @@ void Painter::renderMatte() {
// Draw the clipping mask
matteArray.bind(*plainShader, matteBuffer, BUFFER_OFFSET(0));
plainShader->setColor(white);
glDrawArrays(GL_TRIANGLES, 0, matteBuffer.index());
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)matteBuffer.index());
glEnable(GL_STENCIL_TEST);
}
@@ -410,5 +451,5 @@ void Painter::renderBackground() {
// Draw the clipping mask
coveringPlainArray.bind(*plainShader, tileStencilBuffer, BUFFER_OFFSET(0));
plainShader->setColor(white);
glDrawArrays(GL_TRIANGLES, 0, tileStencilBuffer.index());
glDrawArrays(GL_TRIANGLES, 0, (GLsizei)tileStencilBuffer.index());
}

View 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, (GLsizei)(vertex_end - vertex_start));
}

View 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;
}
}

View File

@@ -105,8 +105,8 @@ bool Shader::compileShader(GLuint *shader, GLenum type, const GLchar *source) {
*shader = glCreateShader(type);
#if defined(EMSCRIPTEN) || defined(GL_ES_VERSION_2_0)
// Add WebGL GLSL / OpenGL ES precision premable
#if defined(GL_ES_VERSION_2_0)
// Add OpenGL ES precision premable
const GLchar *preamble = "precision highp float;\n\n";
#else
// Desktop GLSL

View 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 * u_color;
}

View 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;
}

View File

@@ -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 * u_color;\n}\n",
}
};

View File

@@ -33,42 +33,83 @@ const unsigned char resources::style[] = {
108, 97, 110, 100, 117, 115, 101, 42, 5, 99, 108, 97, 115, 115, 50, 12,
10, 10, 105, 110, 100, 117, 115, 116, 114, 105, 97, 108, 10, 31, 10, 8,
98, 117, 105, 108, 100, 105, 110, 103, 16, 1, 26, 7, 115, 116, 114, 101,
101, 116, 115, 34, 8, 98, 117, 105, 108, 100, 105, 110, 103, 10, 48, 10,
7, 97, 108, 99, 111, 104, 111, 108, 16, 3, 26, 7, 115, 116, 114, 101,
101, 116, 115, 34, 9, 112, 111, 105, 95, 108, 97, 98, 101, 108, 42, 4,
116, 121, 112, 101, 50, 9, 10, 7, 65, 108, 99, 111, 104, 111, 108, 18,
12, 10, 4, 112, 97, 114, 107, 18, 4, 112, 97, 114, 107, 18, 12, 10,
4, 119, 111, 111, 100, 18, 4, 119, 111, 111, 100, 18, 14, 10, 5, 119,
97, 116, 101, 114, 18, 5, 119, 97, 116, 101, 114, 18, 20, 10, 8, 98,
117, 105, 108, 100, 105, 110, 103, 18, 8, 98, 117, 105, 108, 100, 105, 110,
103, 18, 28, 10, 12, 114, 111, 97, 100, 95, 108, 105, 109, 105, 116, 101,
100, 18, 12, 114, 111, 97, 100, 95, 108, 105, 109, 105, 116, 101, 100, 18,
28, 10, 12, 114, 111, 97, 100, 95, 114, 101, 103, 117, 108, 97, 114, 18,
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,
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,
18, 4, 0, 0, 128, 63, 45, 255, 102, 170, 51, 58, 8, 8, 2, 18,
4, 205, 204, 204, 61, 18, 39, 10, 5, 119, 97, 116, 101, 114, 34, 8,
8, 2, 18, 4, 0, 0, 128, 63, 45, 255, 233, 224, 186, 53, 255, 230,
182, 115, 66, 10, 119, 97, 116, 101, 114, 95, 101, 116, 115, 121, 18, 51,
10, 8, 98, 117, 105, 108, 100, 105, 110, 103, 34, 8, 8, 2, 18, 4,
0, 0, 128, 63, 45, 255, 0, 0, 0, 58, 24, 8, 4, 18, 20, 0,
0, 80, 65, 0, 0, 0, 0, 205, 204, 204, 61, 0, 0, 0, 0, 205,
204, 204, 61, 26, 41, 10, 12, 114, 111, 97, 100, 95, 108, 105, 109, 105,
116, 101, 100, 29, 255, 187, 187, 187, 34, 20, 8, 3, 18, 16, 0, 0,
0, 0, 0, 0, 128, 63, 0, 0, 240, 65, 0, 0, 128, 63, 26, 65,
10, 12, 114, 111, 97, 100, 95, 114, 101, 103, 117, 108, 97, 114, 29, 255,
153, 153, 153, 34, 44, 8, 3, 18, 40, 0, 0, 0, 0, 0, 0, 0,
63, 0, 0, 80, 65, 0, 0, 0, 63, 0, 0, 128, 65, 0, 0, 0,
64, 0, 0, 160, 65, 0, 0, 0, 66, 0, 0, 240, 65, 0, 0, 0,
66, 26, 71, 10, 10, 114, 111, 97, 100, 95, 108, 97, 114, 103, 101, 29,
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
101, 116, 115, 34, 8, 98, 117, 105, 108, 100, 105, 110, 103, 10, 52, 10,
11, 97, 108, 99, 111, 104, 111, 108, 95, 112, 111, 105, 16, 3, 26, 7,
115, 116, 114, 101, 101, 116, 115, 34, 9, 112, 111, 105, 95, 108, 97, 98,
101, 108, 42, 4, 116, 121, 112, 101, 50, 9, 10, 7, 65, 108, 99, 111,
104, 111, 108, 10, 46, 10, 8, 99, 97, 102, 101, 95, 112, 111, 105, 16,
3, 26, 7, 115, 116, 114, 101, 101, 116, 115, 34, 9, 112, 111, 105, 95,
108, 97, 98, 101, 108, 42, 4, 116, 121, 112, 101, 50, 6, 10, 4, 67,
97, 102, 101, 10, 52, 10, 11, 101, 109, 98, 97, 115, 115, 121, 95, 112,
111, 105, 16, 3, 26, 7, 115, 116, 114, 101, 101, 116, 115, 34, 9, 112,
111, 105, 95, 108, 97, 98, 101, 108, 42, 4, 116, 121, 112, 101, 50, 9,
10, 7, 69, 109, 98, 97, 115, 115, 121, 10, 46, 10, 8, 112, 97, 114,
107, 95, 112, 111, 105, 16, 3, 26, 7, 115, 116, 114, 101, 101, 116, 115,
34, 9, 112, 111, 105, 95, 108, 97, 98, 101, 108, 42, 4, 116, 121, 112,
101, 50, 6, 10, 4, 80, 97, 114, 107, 10, 58, 10, 14, 114, 101, 115,
116, 97, 117, 114, 97, 110, 116, 95, 112, 111, 105, 16, 3, 26, 7, 115,
116, 114, 101, 101, 116, 115, 34, 9, 112, 111, 105, 95, 108, 97, 98, 101,
108, 42, 4, 116, 121, 112, 101, 50, 12, 10, 10, 82, 101, 115, 116, 97,
117, 114, 97, 110, 116, 18, 12, 10, 4, 112, 97, 114, 107, 18, 4, 112,
97, 114, 107, 18, 12, 10, 4, 119, 111, 111, 100, 18, 4, 119, 111, 111,
100, 18, 14, 10, 5, 119, 97, 116, 101, 114, 18, 5, 119, 97, 116, 101,
114, 18, 20, 10, 8, 98, 117, 105, 108, 100, 105, 110, 103, 18, 8, 98,
117, 105, 108, 100, 105, 110, 103, 18, 28, 10, 12, 114, 111, 97, 100, 95,
108, 105, 109, 105, 116, 101, 100, 18, 12, 114, 111, 97, 100, 95, 108, 105,
109, 105, 116, 101, 100, 18, 28, 10, 12, 114, 111, 97, 100, 95, 114, 101,
103, 117, 108, 97, 114, 18, 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, 26, 10,
11, 97, 108, 99, 111, 104, 111, 108, 95, 112, 111, 105, 18, 11, 97, 108,
99, 111, 104, 111, 108, 95, 112, 111, 105, 18, 20, 10, 8, 99, 97, 102,
101, 95, 112, 111, 105, 18, 8, 99, 97, 102, 101, 95, 112, 111, 105, 18,
26, 10, 11, 101, 109, 98, 97, 115, 115, 121, 95, 112, 111, 105, 18, 11,
101, 109, 98, 97, 115, 115, 121, 95, 112, 111, 105, 18, 20, 10, 8, 112,
97, 114, 107, 95, 112, 111, 105, 18, 8, 112, 97, 114, 107, 95, 112, 111,
105, 18, 32, 10, 14, 114, 101, 115, 116, 97, 117, 114, 97, 110, 116, 95,
112, 111, 105, 18, 14, 114, 101, 115, 116, 97, 117, 114, 97, 110, 116, 95,
112, 111, 105, 26, 160, 5, 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, 18, 4, 0, 0, 128, 63, 45, 255,
102, 170, 51, 58, 8, 8, 2, 18, 4, 205, 204, 204, 61, 18, 39, 10,
5, 119, 97, 116, 101, 114, 34, 8, 8, 2, 18, 4, 0, 0, 128, 63,
45, 255, 233, 224, 186, 53, 255, 230, 182, 115, 66, 10, 119, 97, 116, 101,
114, 95, 101, 116, 115, 121, 18, 51, 10, 8, 98, 117, 105, 108, 100, 105,
110, 103, 34, 8, 8, 2, 18, 4, 0, 0, 128, 63, 45, 255, 0, 0,
0, 58, 24, 8, 4, 18, 20, 0, 0, 80, 65, 0, 0, 0, 0, 205,
204, 204, 61, 0, 0, 0, 0, 205, 204, 204, 61, 26, 41, 10, 12, 114,
111, 97, 100, 95, 108, 105, 109, 105, 116, 101, 100, 29, 255, 187, 187, 187,
34, 20, 8, 3, 18, 16, 0, 0, 0, 0, 0, 0, 128, 63, 0, 0,
240, 65, 0, 0, 128, 63, 26, 65, 10, 12, 114, 111, 97, 100, 95, 114,
101, 103, 117, 108, 97, 114, 29, 255, 153, 153, 153, 34, 44, 8, 3, 18,
40, 0, 0, 0, 0, 0, 0, 0, 63, 0, 0, 80, 65, 0, 0, 0,
63, 0, 0, 128, 65, 0, 0, 0, 64, 0, 0, 160, 65, 0, 0, 0,
66, 0, 0, 240, 65, 0, 0, 0, 66, 26, 71, 10, 10, 114, 111, 97,
100, 95, 108, 97, 114, 103, 101, 29, 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, 34, 68, 10, 11, 97, 108, 99, 111, 104, 111, 108, 95, 112, 111,
105, 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0, 0, 144, 65,
50, 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0, 0, 0, 0,
128, 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 12, 97, 108, 99, 111,
104, 111, 108, 45, 115, 104, 111, 112, 34, 57, 10, 8, 99, 97, 102, 101,
95, 112, 111, 105, 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0,
0, 144, 65, 50, 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0,
0, 0, 0, 128, 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 4, 99,
97, 102, 101, 34, 63, 10, 11, 101, 109, 98, 97, 115, 115, 121, 95, 112,
111, 105, 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0, 0, 144,
65, 50, 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0, 0, 0,
0, 128, 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 7, 101, 109, 98,
97, 115, 115, 121, 34, 57, 10, 8, 112, 97, 114, 107, 95, 112, 111, 105,
29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0, 0, 144, 65, 50,
24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0, 0, 0, 0, 128,
63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 4, 112, 97, 114, 107, 34,
69, 10, 14, 114, 101, 115, 116, 97, 117, 114, 97, 110, 116, 95, 112, 111,
105, 29, 255, 204, 204, 204, 34, 8, 8, 2, 18, 4, 0, 0, 144, 65,
50, 24, 8, 4, 18, 20, 0, 0, 112, 65, 0, 0, 0, 0, 0, 0,
128, 63, 0, 0, 0, 0, 0, 0, 64, 63, 66, 10, 114, 101, 115, 116,
97, 117, 114, 97, 110, 116
};
const unsigned long resources::style_size = sizeof(resources::style);

View File

@@ -23,25 +23,32 @@ ImagePosition::ImagePosition(const vec2<uint16_t>& size, vec2<float> tl, vec2<fl
br(br) {}
Sprite::~Sprite() {
if (img) {
free(img);
}
}
Sprite::operator bool() const {
std::lock_guard<std::mutex> lock(mtx);
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.size() && sprite->pos.size()) {
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 {
@@ -49,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 {
@@ -173,11 +180,11 @@ void Sprite::loadImage(const std::string& data) {
png_read_update_info(png, info);
unsigned int rowbytes = png_get_rowbytes(png, info);
png_size_t rowbytes = png_get_rowbytes(png, info);
assert(width * 4 == rowbytes);
img.resize(width * height * 4);
char *surface = const_cast<char *>(img.data());
img = (char *)malloc(width * height * 4);
char *surface = img;
assert(surface);
png_bytep row_pointers[height];
@@ -192,7 +199,10 @@ void Sprite::loadImage(const std::string& data) {
} catch (std::exception& e) {
fprintf(stderr, "loading PNG failed: %s\n", e.what());
png_destroy_read_struct(&png, &info, nullptr);
img.clear();
if (img) {
free(img);
img = nullptr;
}
width = 0;
height = 0;
}
@@ -234,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.data());
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);
}

View File

@@ -12,9 +12,10 @@ 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) {
void Style::load(const uint8_t *const data, size_t bytes) {
pbf style(data, bytes);
while (style.next()) {
@@ -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;
}
}
}

View File

@@ -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;