derive from GLKViewController
this handles resizing the view/framebuffer much better fixes #130, #131, #133, #118
This commit is contained in:
4
common/foundation_request.h
Normal file
4
common/foundation_request.h
Normal file
@@ -0,0 +1,4 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
|
||||
NSString *const MBXNeedsRenderNotification = @"MBXNeedsRenderNotification";
|
||||
NSString *const MBXUpdateActivityNotification = @"MBXUpdateActivityNotification";
|
||||
@@ -1,4 +1,5 @@
|
||||
#import <Foundation/Foundation.h>
|
||||
#import "foundation_request.h"
|
||||
|
||||
#include <memory>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
@@ -29,6 +30,7 @@ platform::request_http(const std::string &url, std::function<void(Response *)> b
|
||||
|
||||
if ([error code] == NSURLErrorCancelled) {
|
||||
// We intentionally cancelled this request. Do nothing.
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil];
|
||||
return;
|
||||
}
|
||||
|
||||
@@ -46,11 +48,14 @@ platform::request_http(const std::string &url, std::function<void(Response *)> b
|
||||
|
||||
dispatch_async(dispatch_get_main_queue(), ^(void) {
|
||||
foreground_callback();
|
||||
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil];
|
||||
});
|
||||
}];
|
||||
|
||||
req = std::make_shared<Request>(task, url);
|
||||
[task resume];
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MBXUpdateActivityNotification object:nil];
|
||||
return req;
|
||||
}
|
||||
|
||||
|
||||
@@ -234,7 +234,7 @@ public:
|
||||
namespace llmr {
|
||||
namespace platform {
|
||||
|
||||
double time() {
|
||||
double elapsed() {
|
||||
return glfwGetTime();
|
||||
}
|
||||
|
||||
|
||||
@@ -27,7 +27,7 @@ public:
|
||||
void setup();
|
||||
void loadStyle(const uint8_t *const data, uint32_t bytes);
|
||||
void loadSettings();
|
||||
void resize(uint16_t width, uint16_t height, uint16_t fb_width, uint16_t fb_height);
|
||||
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height);
|
||||
void toggleRaster();
|
||||
|
||||
/* callback */
|
||||
|
||||
@@ -22,7 +22,9 @@ public:
|
||||
void updateAnimations();
|
||||
void cancelAnimations();
|
||||
|
||||
void resize(uint16_t width, uint16_t height, uint16_t fb_width, uint16_t fb_height);
|
||||
// Map view changes.
|
||||
// Note: width * ratio does not necessarily equal fb_width
|
||||
void resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height);
|
||||
|
||||
// Relative changes
|
||||
void moveBy(double dx, double dy, double duration = 0);
|
||||
|
||||
@@ -40,7 +40,7 @@ std::shared_ptr<Request> request_http(const std::string &url,
|
||||
void cancel_request_http(const std::shared_ptr<Request> &req);
|
||||
|
||||
// Returns a relative timestamp in seconds. This value must be monotonic.
|
||||
double time();
|
||||
double elapsed();
|
||||
|
||||
// Performs routine cleanup operation and is called on every loop iteration.
|
||||
void cleanup();
|
||||
|
||||
@@ -14,11 +14,11 @@ public:
|
||||
complete
|
||||
};
|
||||
animation(double duration)
|
||||
: start(platform::time()),
|
||||
: start(platform::elapsed()),
|
||||
duration(duration) {}
|
||||
|
||||
double progress() const {
|
||||
return (platform::time() - start) / duration;
|
||||
return (platform::elapsed() - start) / duration;
|
||||
}
|
||||
|
||||
virtual state update() const = 0;
|
||||
|
||||
@@ -1,13 +1,6 @@
|
||||
//
|
||||
// MBXViewController.h
|
||||
// ios
|
||||
//
|
||||
// Created by Justin R. Miller on 1/27/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import <UIKit/UIKit.h>
|
||||
#import <GLKit/GLKit.h>
|
||||
|
||||
@interface MBXViewController : UIViewController
|
||||
@interface MBXViewController : GLKViewController
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
@@ -1,15 +1,9 @@
|
||||
//
|
||||
// MBXViewController.m
|
||||
// ios
|
||||
//
|
||||
// Created by Justin R. Miller on 1/27/14.
|
||||
//
|
||||
//
|
||||
|
||||
#import "MBXViewController.h"
|
||||
|
||||
#import "MBXSettings.h"
|
||||
|
||||
#import "../common/foundation_request.h"
|
||||
|
||||
#import <GLKit/GLKit.h>
|
||||
#import <OpenGLES/EAGL.h>
|
||||
#import <QuartzCore/QuartzCore.h>
|
||||
@@ -18,9 +12,6 @@
|
||||
#include <llmr/platform/platform.hpp>
|
||||
#include <llmr/map/tile.hpp>
|
||||
|
||||
NSString *const MBXNeedsRenderNotification = @"MBXNeedsRenderNotification";
|
||||
NSString *const MBXUpdateActivityNotification = @"MBXUpdateActivityNotification";
|
||||
|
||||
@interface MBXViewController () <UIGestureRecognizerDelegate>
|
||||
|
||||
@property (nonatomic) EAGLContext *context;
|
||||
@@ -30,73 +21,64 @@ NSString *const MBXUpdateActivityNotification = @"MBXUpdateActivityNotification"
|
||||
@property (nonatomic) CGFloat quickZoomStart;
|
||||
@property (nonatomic) BOOL debug;
|
||||
@property (nonatomic) UIView *palette;
|
||||
@property (nonatomic) NSTimeInterval elapsed;
|
||||
|
||||
@end
|
||||
|
||||
@implementation MBXViewController
|
||||
|
||||
class MBXMapView
|
||||
{
|
||||
public:
|
||||
MBXMapView() : settings(), map(settings)
|
||||
{
|
||||
}
|
||||
llmr::Map *map = nullptr;
|
||||
llmr::Settings_iOS *settings = nullptr;
|
||||
MBXViewController *view = nullptr;
|
||||
|
||||
~MBXMapView()
|
||||
{
|
||||
}
|
||||
|
||||
void init()
|
||||
{
|
||||
settings.load();
|
||||
|
||||
map.setup();
|
||||
|
||||
map.loadSettings();
|
||||
}
|
||||
|
||||
public:
|
||||
llmr::Settings_iOS settings;
|
||||
llmr::Map map;
|
||||
};
|
||||
|
||||
- (void)loadView
|
||||
{
|
||||
[super loadView];
|
||||
|
||||
queue = dispatch_queue_create(NULL, DISPATCH_QUEUE_CONCURRENT);
|
||||
|
||||
NSURLSessionConfiguration *sessionConfig = [NSURLSessionConfiguration defaultSessionConfiguration];
|
||||
sessionConfig.timeoutIntervalForResource = 6;
|
||||
sessionConfig.HTTPMaximumConnectionsPerHost = 8;
|
||||
sessionConfig.requestCachePolicy = NSURLRequestUseProtocolCachePolicy;
|
||||
|
||||
session = [NSURLSession sessionWithConfiguration:sessionConfig];
|
||||
|
||||
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||
|
||||
if ( ! self.context)
|
||||
{
|
||||
NSLog(@"Failed to initialize OpenGL ES context");
|
||||
return;
|
||||
}
|
||||
|
||||
GLKView *view = [[GLKView alloc] initWithFrame:[[UIScreen mainScreen] bounds] context:self.context];
|
||||
|
||||
view.enableSetNeedsDisplay = NO;
|
||||
view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
|
||||
view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
|
||||
[view bindDrawable];
|
||||
|
||||
[EAGLContext setCurrentContext:self.context];
|
||||
|
||||
self.view = view;
|
||||
}
|
||||
#pragma mark - Setup
|
||||
|
||||
- (void)viewDidLoad
|
||||
{
|
||||
[super viewDidLoad];
|
||||
|
||||
view = self;
|
||||
|
||||
self.preferredFramesPerSecond = 60;
|
||||
|
||||
self.context = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
|
||||
|
||||
if (!self.context) {
|
||||
NSLog(@"Failed to create OpenGL ES context");
|
||||
}
|
||||
|
||||
GLKView *view = (GLKView *)self.view;
|
||||
|
||||
view.context = self.context;
|
||||
view.drawableStencilFormat = GLKViewDrawableStencilFormat8;
|
||||
view.drawableDepthFormat = GLKViewDrawableDepthFormat16;
|
||||
|
||||
[view bindDrawable];
|
||||
|
||||
[self setupMap];
|
||||
[self setupInteraction];
|
||||
[self setupDebugUI];
|
||||
}
|
||||
|
||||
- (void)setupMap
|
||||
{
|
||||
[EAGLContext setCurrentContext:self.context];
|
||||
fprintf(stderr, "setupmap\n");
|
||||
|
||||
settings = new llmr::Settings_iOS();
|
||||
settings->load();
|
||||
|
||||
map = new llmr::Map(*settings);
|
||||
map->setup();
|
||||
map->loadSettings();
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateRender:) name:MBXNeedsRenderNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateNetworkActivity:) name:MBXUpdateActivityNotification object:nil];
|
||||
}
|
||||
|
||||
|
||||
- (void)setupInteraction
|
||||
{
|
||||
UIPanGestureRecognizer *pan = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handlePanGesture:)];
|
||||
pan.delegate = self;
|
||||
[self.view addGestureRecognizer:pan];
|
||||
@@ -130,20 +112,10 @@ class MBXMapView
|
||||
quickZoom.minimumPressDuration = 0.25;
|
||||
[self.view addGestureRecognizer:quickZoom];
|
||||
}
|
||||
}
|
||||
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateRender:) name:MBXNeedsRenderNotification object:nil];
|
||||
[[NSNotificationCenter defaultCenter] addObserver:self selector:@selector(updateNetworkActivity:) name:MBXUpdateActivityNotification object:nil];
|
||||
|
||||
GLKView *view = (GLKView *)self.view;
|
||||
CGRect rect = [view frame];
|
||||
|
||||
mapView = new MBXMapView();
|
||||
mapView->init();
|
||||
mapView->map.resize(rect.size.width, rect.size.height, [view drawableWidth], [view drawableHeight]);
|
||||
|
||||
displayLink = [CADisplayLink displayLinkWithTarget:self selector:@selector(render:)];
|
||||
[displayLink addToRunLoop:[NSRunLoop mainRunLoop] forMode:NSDefaultRunLoopMode];
|
||||
|
||||
- (void)setupDebugUI
|
||||
{
|
||||
NSArray *selectorNames = @[ @"unrotate", @"resetPosition", @"toggleDebug", @"toggleRaster" ];
|
||||
CGFloat buttonSize = 40;
|
||||
CGFloat bufferSize = 20;
|
||||
@@ -173,17 +145,58 @@ class MBXMapView
|
||||
|
||||
- (NSUInteger)supportedInterfaceOrientations
|
||||
{
|
||||
return ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad ? UIInterfaceOrientationMaskAll : UIInterfaceOrientationMaskAllButUpsideDown);
|
||||
// We support everything; it's up to the embedding application to limit the orientations.
|
||||
return UIInterfaceOrientationMaskAll;
|
||||
}
|
||||
|
||||
-(void)viewWillLayoutSubviews {
|
||||
#pragma mark - NotificationCenter callbacks
|
||||
|
||||
- (void)updateRender:(NSNotification *)notification
|
||||
{
|
||||
[self updateRender];
|
||||
}
|
||||
|
||||
- (void)updateRender
|
||||
{
|
||||
self.paused = NO;
|
||||
}
|
||||
|
||||
- (void)updateNetworkActivity:(NSNotification *)notification
|
||||
{
|
||||
[[NSURLSession sharedSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks)
|
||||
{
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:([downloadTasks count] > 0)];
|
||||
}];
|
||||
}
|
||||
|
||||
#pragma mark - Rendering delegates
|
||||
|
||||
- (void)viewWillLayoutSubviews
|
||||
{
|
||||
// This callback is called whenever the view dimensions change. We trigger an update to have a fresh render to display
|
||||
// while the view change is transitioned.
|
||||
[super viewWillLayoutSubviews];
|
||||
|
||||
GLKView *view = (GLKView *)self.view;
|
||||
CGRect rect = [view bounds];
|
||||
mapView->map.resize(rect.size.width, rect.size.height, [view drawableWidth], [view drawableHeight]);
|
||||
// TODO: Pause all current animations during animation. We don't want other events to fire while the view is rotated.
|
||||
|
||||
[self updateRender];
|
||||
}
|
||||
|
||||
- (void)glkView:(GLKView *)view drawInRect:(CGRect)rect
|
||||
{
|
||||
self.elapsed = CACurrentMediaTime();
|
||||
|
||||
// Render the map
|
||||
map->resize(rect.size.width, rect.size.height, view.contentScaleFactor, view.drawableWidth, view.drawableHeight);
|
||||
|
||||
if (map->render() == false) {
|
||||
self.paused = YES;
|
||||
settings->sync();
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - Debugging UI
|
||||
|
||||
- (void)togglePalette
|
||||
{
|
||||
if (self.palette.alpha < 1)
|
||||
@@ -208,88 +221,52 @@ class MBXMapView
|
||||
|
||||
- (void)unrotate
|
||||
{
|
||||
mapView->map.resetNorth();
|
||||
map->resetNorth();
|
||||
}
|
||||
|
||||
- (void)resetPosition
|
||||
{
|
||||
mapView->map.resetPosition();
|
||||
map->resetPosition();
|
||||
}
|
||||
|
||||
- (void)toggleDebug
|
||||
{
|
||||
mapView->map.toggleDebug();
|
||||
map->toggleDebug();
|
||||
|
||||
self.debug = ! self.debug;
|
||||
}
|
||||
|
||||
- (void)toggleRaster
|
||||
{
|
||||
mapView->map.toggleRaster();
|
||||
map->toggleRaster();
|
||||
}
|
||||
|
||||
#pragma mark - Destruction
|
||||
|
||||
- (void)dealloc
|
||||
{
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
mapView->settings.sync();
|
||||
}
|
||||
|
||||
- (void)updateRender:(NSNotification *)notification
|
||||
{
|
||||
[self updateRender];
|
||||
}
|
||||
|
||||
- (void)updateNetworkActivity:(NSNotification *)notification
|
||||
{
|
||||
[[NSURLSession sharedSession] getTasksWithCompletionHandler:^(NSArray *dataTasks, NSArray *uploadTasks, NSArray *downloadTasks)
|
||||
{
|
||||
[[UIApplication sharedApplication] setNetworkActivityIndicatorVisible:([downloadTasks count] > 0)];
|
||||
}];
|
||||
}
|
||||
|
||||
- (void)updateRender
|
||||
{
|
||||
[NSObject cancelPreviousPerformRequestsWithTarget:self];
|
||||
|
||||
displayLink.paused = NO;
|
||||
|
||||
[self performSelector:@selector(pauseRender) withObject:nil afterDelay:3.0];
|
||||
}
|
||||
|
||||
- (void)pauseRender
|
||||
{
|
||||
displayLink.paused = YES;
|
||||
|
||||
mapView->settings.sync();
|
||||
}
|
||||
|
||||
- (void)render:(id)sender
|
||||
{
|
||||
mapView->map.render();
|
||||
|
||||
static double frames = 0;
|
||||
static double elapsed = 0;
|
||||
|
||||
frames++;
|
||||
|
||||
double current = [[NSDate date] timeIntervalSince1970];
|
||||
|
||||
if (current - elapsed >= 1)
|
||||
{
|
||||
if (self.debug)
|
||||
NSLog(@"FPS: %f", frames / (current - elapsed));
|
||||
|
||||
elapsed = current;
|
||||
frames = 0;
|
||||
if (map) {
|
||||
delete map;
|
||||
map = nullptr;
|
||||
}
|
||||
if (settings) {
|
||||
settings->sync();
|
||||
delete settings;
|
||||
settings = nullptr;
|
||||
}
|
||||
|
||||
[(GLKView *)self.view display];
|
||||
[[NSNotificationCenter defaultCenter] removeObserver:self];
|
||||
|
||||
if ([EAGLContext currentContext] == self.context) {
|
||||
[EAGLContext setCurrentContext:nil];
|
||||
}
|
||||
}
|
||||
|
||||
#pragma mark - UI gestures
|
||||
|
||||
- (void)handlePanGesture:(UIPanGestureRecognizer *)pan
|
||||
{
|
||||
mapView->map.cancelAnimations();
|
||||
map->cancelAnimations();
|
||||
|
||||
if (pan.state == UIGestureRecognizerStateBegan)
|
||||
{
|
||||
@@ -300,21 +277,22 @@ class MBXMapView
|
||||
CGPoint delta = CGPointMake([pan translationInView:pan.view].x - self.center.x,
|
||||
[pan translationInView:pan.view].y - self.center.y);
|
||||
|
||||
mapView->map.moveBy(delta.x, delta.y);
|
||||
map->moveBy(delta.x, delta.y);
|
||||
|
||||
self.center = CGPointMake(self.center.x + delta.x, self.center.y + delta.y);
|
||||
}
|
||||
else if (pan.state == UIGestureRecognizerStateEnded)
|
||||
{
|
||||
if ([pan velocityInView:pan.view].x < 50 && [pan velocityInView:pan.view].y < 50)
|
||||
if ([pan velocityInView:pan.view].x < 50 && [pan velocityInView:pan.view].y < 50) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGPoint finalCenter = CGPointMake(self.center.x + (0.1 * [pan velocityInView:pan.view].x),
|
||||
self.center.y + (0.1 * [pan velocityInView:pan.view].y));
|
||||
|
||||
CGFloat duration = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad ? 0.3 : 0.5);
|
||||
|
||||
mapView->map.moveBy(finalCenter.x - self.center.x, finalCenter.y - self.center.y, duration);
|
||||
map->moveBy(finalCenter.x - self.center.x, finalCenter.y - self.center.y, duration);
|
||||
}
|
||||
|
||||
[self updateRender];
|
||||
@@ -322,13 +300,15 @@ class MBXMapView
|
||||
|
||||
- (void)handlePinchGesture:(UIPinchGestureRecognizer *)pinch
|
||||
{
|
||||
mapView->map.cancelAnimations();
|
||||
self.elapsed = CACurrentMediaTime();
|
||||
|
||||
map->cancelAnimations();
|
||||
|
||||
if (pinch.state == UIGestureRecognizerStateBegan)
|
||||
{
|
||||
mapView->map.startScaling();
|
||||
map->startScaling();
|
||||
|
||||
self.scale = mapView->map.getScale();
|
||||
self.scale = map->getScale();
|
||||
}
|
||||
else if (pinch.state == UIGestureRecognizerStateChanged)
|
||||
{
|
||||
@@ -346,27 +326,30 @@ class MBXMapView
|
||||
|
||||
CGFloat newZoom = log2f(self.scale) + adjustment;
|
||||
|
||||
mapView->map.scaleBy(powf(2, newZoom) / mapView->map.getScale(), [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y);
|
||||
CGPoint pt = [pinch locationInView:pinch.view];
|
||||
map->scaleBy(powf(2, newZoom) / map->getScale(), pt.x, pt.y);
|
||||
}
|
||||
else if (pinch.state == UIGestureRecognizerStateEnded)
|
||||
{
|
||||
mapView->map.stopScaling();
|
||||
map->stopScaling();
|
||||
|
||||
if (fabsf(pinch.velocity) < 20)
|
||||
if (fabsf(pinch.velocity) < 20) {
|
||||
return;
|
||||
}
|
||||
|
||||
CGFloat finalZoom = log2f(mapView->map.getScale()) + (0.01 * pinch.velocity);
|
||||
CGFloat finalZoom = log2f(map->getScale()) + (0.01 * pinch.velocity);
|
||||
|
||||
double scale = mapView->map.getScale();
|
||||
double scale = map->getScale();
|
||||
double new_scale = powf(2, finalZoom);
|
||||
|
||||
CGFloat duration = ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPad ? 0.3 : 0.5);
|
||||
|
||||
mapView->map.scaleBy(new_scale / scale, [pinch locationInView:pinch.view].x, [pinch locationInView:pinch.view].y, duration);
|
||||
CGPoint pt = [pinch locationInView:pinch.view];
|
||||
map->scaleBy(new_scale / scale, pt.x, pt.y, duration);
|
||||
}
|
||||
else if (pinch.state == UIGestureRecognizerStateCancelled)
|
||||
{
|
||||
mapView->map.stopScaling();
|
||||
map->stopScaling();
|
||||
}
|
||||
|
||||
[self updateRender];
|
||||
@@ -374,21 +357,24 @@ class MBXMapView
|
||||
|
||||
- (void)handleRotateGesture:(UIRotationGestureRecognizer *)rotate
|
||||
{
|
||||
mapView->map.cancelAnimations();
|
||||
self.elapsed = CACurrentMediaTime();
|
||||
|
||||
map->cancelAnimations();
|
||||
|
||||
if (rotate.state == UIGestureRecognizerStateBegan)
|
||||
{
|
||||
mapView->map.startRotating();
|
||||
map->startRotating();
|
||||
|
||||
self.angle = mapView->map.getAngle();
|
||||
self.angle = map->getAngle();
|
||||
}
|
||||
else if (rotate.state == UIGestureRecognizerStateChanged)
|
||||
{
|
||||
mapView->map.setAngle(self.angle + rotate.rotation, [rotate locationInView:rotate.view].x, [rotate locationInView:rotate.view].y);
|
||||
CGPoint pt = [rotate locationInView:rotate.view];
|
||||
map->setAngle(self.angle + rotate.rotation, pt.x, pt.y);
|
||||
}
|
||||
else if (rotate.state == UIGestureRecognizerStateEnded || rotate.state == UIGestureRecognizerStateCancelled)
|
||||
{
|
||||
mapView->map.stopRotating();
|
||||
map->stopRotating();
|
||||
}
|
||||
|
||||
[self updateRender];
|
||||
@@ -396,36 +382,48 @@ class MBXMapView
|
||||
|
||||
- (void)handleSingleTapGesture:(UITapGestureRecognizer *)singleTap
|
||||
{
|
||||
self.elapsed = CACurrentMediaTime();
|
||||
|
||||
[self togglePalette];
|
||||
}
|
||||
|
||||
- (void)handleDoubleTapGesture:(UITapGestureRecognizer *)doubleTap
|
||||
{
|
||||
mapView->map.cancelAnimations();
|
||||
self.elapsed = CACurrentMediaTime();
|
||||
|
||||
if (doubleTap.state == UIGestureRecognizerStateEnded)
|
||||
mapView->map.scaleBy(2, [doubleTap locationInView:doubleTap.view].x, [doubleTap locationInView:doubleTap.view].y, 0.5);
|
||||
map->cancelAnimations();
|
||||
|
||||
if (doubleTap.state == UIGestureRecognizerStateEnded) {
|
||||
CGPoint pt = [doubleTap locationInView:doubleTap.view];
|
||||
map->scaleBy(2, pt.x, pt.y, 0.5);
|
||||
}
|
||||
|
||||
[self updateRender];
|
||||
}
|
||||
|
||||
- (void)handleTwoFingerTapGesture:(UITapGestureRecognizer *)twoFingerTap
|
||||
{
|
||||
mapView->map.cancelAnimations();
|
||||
self.elapsed = CACurrentMediaTime();
|
||||
|
||||
if (twoFingerTap.state == UIGestureRecognizerStateEnded)
|
||||
mapView->map.scaleBy(0.5, [twoFingerTap locationInView:twoFingerTap.view].x, [twoFingerTap locationInView:twoFingerTap.view].y, 0.5);
|
||||
map->cancelAnimations();
|
||||
|
||||
if (twoFingerTap.state == UIGestureRecognizerStateEnded) {
|
||||
CGPoint pt = [twoFingerTap locationInView:twoFingerTap.view];
|
||||
map->scaleBy(0.5, pt.x, pt.y, 0.5);
|
||||
}
|
||||
|
||||
[self updateRender];
|
||||
}
|
||||
|
||||
- (void)handleQuickZoomGesture:(UILongPressGestureRecognizer *)quickZoom
|
||||
{
|
||||
mapView->map.cancelAnimations();
|
||||
self.elapsed = CACurrentMediaTime();
|
||||
|
||||
map->cancelAnimations();
|
||||
|
||||
if (quickZoom.state == UIGestureRecognizerStateBegan)
|
||||
{
|
||||
self.scale = mapView->map.getScale();
|
||||
self.scale = map->getScale();
|
||||
|
||||
self.quickZoomStart = [quickZoom locationInView:quickZoom.view].y;
|
||||
}
|
||||
@@ -435,7 +433,7 @@ class MBXMapView
|
||||
|
||||
CGFloat newZoom = log2f(self.scale) + (distance / 100);
|
||||
|
||||
mapView->map.scaleBy(powf(2, newZoom) / mapView->map.getScale(), self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
|
||||
map->scaleBy(powf(2, newZoom) / map->getScale(), self.view.bounds.size.width / 2, self.view.bounds.size.height / 2);
|
||||
}
|
||||
|
||||
[self updateRender];
|
||||
@@ -448,33 +446,29 @@ class MBXMapView
|
||||
return ([validSimultaneousGestures containsObject:[gestureRecognizer class]] && [validSimultaneousGestures containsObject:[otherGestureRecognizer class]]);
|
||||
}
|
||||
|
||||
MBXMapView *mapView = nullptr;
|
||||
CADisplayLink *displayLink = nullptr;
|
||||
dispatch_queue_t queue = nullptr;
|
||||
NSURLSession *session = nullptr;
|
||||
|
||||
namespace llmr {
|
||||
namespace platform {
|
||||
|
||||
namespace llmr
|
||||
{
|
||||
namespace platform
|
||||
{
|
||||
class Request
|
||||
{
|
||||
public:
|
||||
int16_t identifier;
|
||||
std::string original_url;
|
||||
class Request {
|
||||
public:
|
||||
int16_t identifier;
|
||||
std::string original_url;
|
||||
};
|
||||
|
||||
void restart()
|
||||
{
|
||||
void restart() {
|
||||
[[NSNotificationCenter defaultCenter] postNotificationName:MBXNeedsRenderNotification object:nil];
|
||||
}
|
||||
|
||||
double time()
|
||||
{
|
||||
return [displayLink timestamp];
|
||||
double time() {
|
||||
return [view timeSinceFirstResume];
|
||||
}
|
||||
|
||||
double elapsed() {
|
||||
return view.elapsed;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@end
|
||||
@end
|
||||
|
||||
|
||||
@@ -44,16 +44,14 @@ void Map::loadSettings() {
|
||||
update();
|
||||
}
|
||||
|
||||
void Map::resize(uint16_t width, uint16_t height, uint16_t fb_width, uint16_t fb_height) {
|
||||
transform.resize(width, height, fb_width, fb_height);
|
||||
void Map::resize(uint16_t width, uint16_t height, float ratio, uint16_t fb_width, uint16_t fb_height) {
|
||||
transform.resize(width, height, ratio, fb_width, fb_height);
|
||||
painter.resize();
|
||||
|
||||
if (!style.sprite || style.sprite->pixelRatio != transform.getPixelRatio()) {
|
||||
style.sprite = std::make_shared<Sprite>(transform.getPixelRatio());
|
||||
style.sprite->load(kSpriteURL);
|
||||
}
|
||||
|
||||
update();
|
||||
}
|
||||
|
||||
void Map::toggleRaster() {
|
||||
@@ -479,5 +477,9 @@ bool Map::render() {
|
||||
|
||||
painter.renderMatte();
|
||||
|
||||
return changed || transform.needsAnimation() || painter.needsAnimation();
|
||||
|
||||
bool transition = transform.needsAnimation();
|
||||
bool animation = painter.needsAnimation();
|
||||
|
||||
return changed || transition || animation;
|
||||
}
|
||||
|
||||
@@ -34,12 +34,12 @@ void Transform::cancelAnimations() {
|
||||
animations.clear();
|
||||
}
|
||||
|
||||
void Transform::resize(uint16_t w, uint16_t h, uint16_t fb_w, uint16_t fb_h) {
|
||||
void Transform::resize(uint16_t w, uint16_t h, float ratio, uint16_t fb_w, uint16_t fb_h) {
|
||||
width = w;
|
||||
height = h;
|
||||
pixelRatio = ratio;
|
||||
fb_width = fb_w;
|
||||
fb_height = fb_h;
|
||||
pixelRatio = (float)fb_w / (float)w;
|
||||
}
|
||||
|
||||
void Transform::moveBy(double dx, double dy, double duration) {
|
||||
|
||||
@@ -11,7 +11,7 @@ void FrameHistory::record(float zoom) {
|
||||
}
|
||||
|
||||
if (history.size() > 0 || history.back().z != zoom) {
|
||||
history.emplace_back(FrameSnapshot{static_cast<float>(platform::time() * 1000), zoom});
|
||||
history.emplace_back(FrameSnapshot{static_cast<float>(platform::elapsed() * 1000), zoom});
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -529,7 +529,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
|
||||
|
||||
// Label fading
|
||||
const float duration = 300.0f;
|
||||
const float currentTime = platform::time() * 1000;
|
||||
const float currentTime = platform::elapsed() * 1000;
|
||||
|
||||
std::deque<FrameSnapshot> &history = frameHistory.history;
|
||||
|
||||
@@ -558,7 +558,7 @@ void Painter::renderText(TextBucket& bucket, const std::string& layer_name, cons
|
||||
if (timeDiff > duration) timeDiff = 1;
|
||||
float fadedist = zoomDiff / (timeDiff / duration);
|
||||
|
||||
if (isnan(fadedist)) fprintf(stderr, "fadedist should never be NaN");
|
||||
if (isnan(fadedist)) fprintf(stderr, "fadedist should never be NaN\n");
|
||||
|
||||
// At end of a zoom when the zoom stops changing continue pretending to zoom at that speed
|
||||
// bump is how much farther it would have been if it had continued zooming at the same rate
|
||||
|
||||
Reference in New Issue
Block a user