Merge branch 'master' into node

This commit is contained in:
John Firebaugh
2015-08-31 16:45:21 -07:00
80 changed files with 1318 additions and 401 deletions

View File

@@ -50,6 +50,13 @@ jfieldID latLngZoomLatitudeId = nullptr;
jfieldID latLngZoomLongitudeId = nullptr;
jfieldID latLngZoomZoomId = nullptr;
jclass bboxClass = nullptr;
jmethodID bboxConstructorId = nullptr;
jfieldID bboxLatNorthId = nullptr;
jfieldID bboxLatSouthId = nullptr;
jfieldID bboxLonEastId = nullptr;
jfieldID bboxLonWestId = nullptr;
jclass markerClass = nullptr;
jmethodID markerConstructorId = nullptr;
jfieldID markerPositionId = nullptr;
@@ -970,6 +977,50 @@ void JNICALL nativeRemoveAnnotations(JNIEnv *env, jobject obj, jlong nativeMapVi
nativeMapView->getMap().removeAnnotations(ids);
}
jlongArray JNICALL nativeGetAnnotationsInBounds(JNIEnv *env, jobject obj, jlong nativeMapViewPtr, jobject bbox) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeGetAnnotationsInBounds");
assert(nativeMapViewPtr != 0);
NativeMapView *nativeMapView = reinterpret_cast<NativeMapView *>(nativeMapViewPtr);
if (env->ExceptionCheck() || (bbox == nullptr)) {
env->ExceptionDescribe();
return nullptr;
}
jdouble swLat = env->GetDoubleField(bbox, bboxLatSouthId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
return nullptr;
}
jdouble swLon = env->GetDoubleField(bbox, bboxLonWestId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
return nullptr;
}
jdouble neLat = env->GetDoubleField(bbox, bboxLatNorthId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
return nullptr;
}
jdouble neLon = env->GetDoubleField(bbox, bboxLonEastId);
if (env->ExceptionCheck()) {
env->ExceptionDescribe();
return nullptr;
}
mbgl::LatLngBounds bounds;
bounds.sw = { swLat, swLon };
bounds.ne = { neLat, neLon };
// assume only points for now
std::vector<uint32_t> annotations = nativeMapView->getMap().getAnnotationsInBounds(bounds, mbgl::AnnotationType::Point);
return std_vector_uint_to_jobject(env, annotations);
}
void JNICALL nativeSetSprite(JNIEnv *env, jobject obj, jlong nativeMapViewPtr,
jstring symbol, jint width, jint height, jfloat scale, jbyteArray jpixels) {
mbgl::Log::Debug(mbgl::Event::JNI, "nativeSetSprite");
@@ -1246,6 +1297,42 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_ERR;
}
bboxClass = env->FindClass("com/mapbox/mapboxgl/geometry/BoundingBox");
if (bboxClass == nullptr) {
env->ExceptionDescribe();
return JNI_ERR;
}
bboxConstructorId = env->GetMethodID(bboxClass, "<init>", "(DDDD)V");
if (bboxConstructorId == nullptr) {
env->ExceptionDescribe();
return JNI_ERR;
}
bboxLatNorthId = env->GetFieldID(bboxClass, "mLatNorth", "D");
if (bboxLatNorthId == nullptr) {
env->ExceptionDescribe();
return JNI_ERR;
}
bboxLatSouthId = env->GetFieldID(bboxClass, "mLatSouth", "D");
if (bboxLatSouthId == nullptr) {
env->ExceptionDescribe();
return JNI_ERR;
}
bboxLonEastId = env->GetFieldID(bboxClass, "mLonEast", "D");
if (bboxLonEastId == nullptr) {
env->ExceptionDescribe();
return JNI_ERR;
}
bboxLonWestId = env->GetFieldID(bboxClass, "mLonWest", "D");
if (bboxLonWestId == nullptr) {
env->ExceptionDescribe();
return JNI_ERR;
}
markerClass = env->FindClass("com/mapbox/mapboxgl/annotations/Marker");
if (markerClass == nullptr) {
env->ExceptionDescribe();
@@ -1596,6 +1683,8 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
reinterpret_cast<void *>(&nativeAddPolygons)},
{"nativeRemoveAnnotation", "(JJ)V", reinterpret_cast<void *>(&nativeRemoveAnnotation)},
{"nativeRemoveAnnotations", "(J[J)V", reinterpret_cast<void *>(&nativeRemoveAnnotations)},
{"nativeGetAnnotationsInBounds", "(JLcom/mapbox/mapboxgl/geometry/BoundingBox;)[J",
reinterpret_cast<void *>(&nativeGetAnnotationsInBounds)},
{"nativeSetSprite", "(JLjava/lang/String;IIF[B)V", reinterpret_cast<void *>(&nativeSetSprite)},
{"nativeOnLowMemory", "(J)V", reinterpret_cast<void *>(&nativeOnLowMemory)},
{"nativeSetDebug", "(JZ)V", reinterpret_cast<void *>(&nativeSetDebug)},
@@ -1639,6 +1728,13 @@ extern "C" JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *reserved) {
return JNI_ERR;
}
bboxClass = reinterpret_cast<jclass>(env->NewGlobalRef(bboxClass));
if (bboxClass == nullptr) {
env->ExceptionDescribe();
env->DeleteGlobalRef(bboxClass);
return JNI_ERR;
}
markerClass = reinterpret_cast<jclass>(env->NewGlobalRef(markerClass));
if (markerClass == nullptr) {
env->ExceptionDescribe();
@@ -1795,6 +1891,14 @@ extern "C" JNIEXPORT void JNICALL JNI_OnUnload(JavaVM *vm, void *reserved) {
latLngZoomLatitudeId = nullptr;
latLngZoomZoomId = nullptr;
env->DeleteGlobalRef(bboxClass);
bboxClass = nullptr;
bboxConstructorId = nullptr;
bboxLatNorthId = nullptr;
bboxLatSouthId = nullptr;
bboxLonEastId = nullptr;
bboxLonWestId = nullptr;
env->DeleteGlobalRef(markerClass);
markerClass = nullptr;
markerConstructorId = nullptr;

View File

@@ -65,9 +65,8 @@ android {
}
release {
minifyEnabled false
jniDebuggable false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
consumerProguardFiles 'proguard-rules.pro'
}
}
}

View File

@@ -0,0 +1,24 @@
# By default, the flags in this file are appended to flags specified
# in ../sdk/tools/proguard/proguard-android.txt,
# contents of this file will be appended into proguard-android.txt
# Square okio, ignoring warnings,
# see https://github.com/square/okio/issues/60
-dontwarn okio.**
# Package: annotations
-keep class com.mapbox.mapboxgl.annotations.** { *; }
# Package: geometry
-keep class com.mapbox.mapboxgl.geometry.** { *; }
# Package: http
-keep class com.mapbox.mapboxgl.http.** { *; }
# Package views
-keep class com.mapbox.mapboxgl.views.MapView**
-keep class com.mapbox.mapboxgl.views.NativeMapView**
-keepclassmembers class com.mapbox.mapboxgl.views.NativeMapView** {
*;
}

View File

@@ -50,6 +50,7 @@ import com.mapbox.mapboxgl.annotations.Polygon;
import com.mapbox.mapboxgl.annotations.PolygonOptions;
import com.mapbox.mapboxgl.annotations.Polyline;
import com.mapbox.mapboxgl.annotations.PolylineOptions;
import com.mapbox.mapboxgl.geometry.BoundingBox;
import com.mapbox.mapboxgl.geometry.LatLng;
import com.mapbox.mapboxgl.geometry.LatLngZoom;
import com.mapzen.android.lost.api.LocationListener;
@@ -159,6 +160,25 @@ public class MapView extends FrameLayout implements LocationListener {
// Used to manage Event Listeners
private ArrayList<OnMapChangedListener> mOnMapChangedListener;
public interface OnFlingListener {
void onFling();
}
public interface OnScrollListener {
void onScroll();
}
private OnFlingListener onFlingListener;
private OnScrollListener onScrollListener;
public void setOnScrollListener(OnScrollListener onScrollListener) {
this.onScrollListener = onScrollListener;
}
public void setOnFlingListener(OnFlingListener onFlingListener) {
this.onFlingListener = onFlingListener;
}
//
// Properties
//
@@ -287,7 +307,7 @@ public class MapView extends FrameLayout implements LocationListener {
mCompassListener = new CompassListener();
mCompassView = new ImageView(mContext);
mCompassView.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.compass));
mCompassView.setImageDrawable(ContextCompat.getDrawable(getContext(), R.drawable.compass_custom_flat));
mCompassView.setContentDescription(getResources().getString(R.string.compassContentDescription));
LayoutParams lp = new FrameLayout.LayoutParams((int)(48 * mScreenDensity), (int)(48 * mScreenDensity));
lp.gravity = Gravity.TOP | Gravity.END;
@@ -428,6 +448,25 @@ public class MapView extends FrameLayout implements LocationListener {
return Collections.unmodifiableList(mAnnotations);
}
public List<Annotation> getAnnotationsInBounds(BoundingBox bbox) {
List<Annotation> annotations = new ArrayList<>();
long[] ids = mNativeMapView.getAnnotationsInBounds(bbox);
List<Long> idsList = new ArrayList<>();
for(int i = 0; i < ids.length; i++) {
idsList.add(new Long(ids[i]));
}
for(int i = 0; i < mAnnotations.size(); i++) {
Annotation annotation = mAnnotations.get(i);
if (annotation instanceof Marker && idsList.contains(annotation.getId())) {
annotations.add(annotation);
}
}
for(int i = 0; i < annotations.size(); i++) {
Log.d(TAG, "tapped: " + Long.toString(annotations.get(i).getId()));
}
return annotations;
}
//
// Property methods
//
@@ -919,6 +958,24 @@ public class MapView extends FrameLayout implements LocationListener {
// Cancel any animation
mNativeMapView.cancelTransitions();
// Select or deselect point annotations
PointF tapPoint = new PointF(e.getX(), e.getY());
float toleranceWidth = 60 * mScreenDensity;
float toleranceHeight = 80 * mScreenDensity;
PointF tr = new PointF(tapPoint.x + toleranceWidth / 2, tapPoint.y + 2 * toleranceHeight / 3);
PointF bl = new PointF(tapPoint.x - toleranceWidth / 2, tapPoint.y - 1 * toleranceHeight / 3);
LatLng sw = fromScreenLocation(bl);
LatLng ne = fromScreenLocation(tr);
BoundingBox bbox = new BoundingBox(ne, sw);
List<Annotation> annotations = getAnnotationsInBounds(bbox);
performClick();
return true;
}
@@ -932,6 +989,7 @@ public class MapView extends FrameLayout implements LocationListener {
@Override
public void onLongPress(MotionEvent e) {
// TODO
performLongClick();
}
// Called for flings
@@ -959,6 +1017,10 @@ public class MapView extends FrameLayout implements LocationListener {
mNativeMapView.moveBy(velocityX * duration / 2.0 / mScreenDensity, velocityY * duration / 2.0 / mScreenDensity, (long) (duration * 1000.0f));
if(onFlingListener != null){
onFlingListener.onFling();
}
return true;
}
@@ -976,6 +1038,11 @@ public class MapView extends FrameLayout implements LocationListener {
// Scroll the map
mNativeMapView.moveBy(-distanceX / mScreenDensity, -distanceY / mScreenDensity);
if(onScrollListener != null){
onScrollListener.onScroll();
}
return true;
}
}

View File

@@ -3,13 +3,16 @@ package com.mapbox.mapboxgl.views;
import android.graphics.PointF;
import android.view.Surface;
import com.mapbox.mapboxgl.annotations.Annotation;
import com.mapbox.mapboxgl.annotations.Marker;
import com.mapbox.mapboxgl.annotations.Polygon;
import com.mapbox.mapboxgl.annotations.Polyline;
import com.mapbox.mapboxgl.geometry.BoundingBox;
import com.mapbox.mapboxgl.geometry.LatLng;
import com.mapbox.mapboxgl.geometry.LatLngZoom;
import com.mapbox.mapboxgl.geometry.ProjectedMeters;
import java.util.ArrayList;
import java.util.List;
// Class that wraps the native methods for convenience
@@ -353,6 +356,10 @@ class NativeMapView {
nativeRemoveAnnotations(mNativeMapViewPtr, ids);
}
public long[] getAnnotationsInBounds(BoundingBox bbox) {
return nativeGetAnnotationsInBounds(mNativeMapViewPtr, bbox);
}
public void setSprite(String symbol, int width, int height, float scale, byte[] pixels) {
nativeSetSprite(mNativeMapViewPtr, symbol, width, height, scale, pixels);
}
@@ -563,6 +570,8 @@ class NativeMapView {
private native void nativeRemoveAnnotations(long nativeMapViewPtr, long[] id);
private native long[] nativeGetAnnotationsInBounds(long mNativeMapViewPtr, BoundingBox bbox);
private native void nativeSetSprite(long nativeMapViewPtr, String symbol,
int width, int height, float scale, byte[] pixels);

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 KiB

View File

@@ -73,7 +73,7 @@ android {
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}

View File

@@ -0,0 +1 @@
# Proguard configuration is handeld in the SDK

View File

@@ -54,6 +54,7 @@
android:layout_margin="@dimen/fab_margin"
app:backgroundTint="@color/white"
app:borderWidth="0dp"
app:elevation="0dp"
/>
</android.support.design.widget.CoordinatorLayout>

View File

@@ -43,6 +43,13 @@ extern jfieldID latLngZoomLatitudeId;
extern jfieldID latLngZoomLongitudeId;
extern jfieldID latLngZoomZoomId;
extern jclass bboxClass;
extern jmethodID bboxConstructorId;
extern jfieldID bboxLatNorthId;
extern jfieldID bboxLatSouthId;
extern jfieldID bboxLonEastId;
extern jfieldID bboxLonWestId;
extern jclass markerClass;
extern jmethodID markerConstructorId;
extern jfieldID markerPositionId;

View File

@@ -74,6 +74,13 @@ IB_DESIGNABLE
* The default value of this property is `YES`. */
@property(nonatomic, getter=isRotateEnabled) BOOL rotateEnabled;
/** A Boolean value that determines whether the user may change the pitch (tilt) of the map.
*
* This property controls only user interactions with the map. If you set the value of this property to `NO`, you may still change the pitch of the map programmatically.
*
* The default value of this property is `YES`. */
@property(nonatomic, getter=isPitchEnabled) BOOL pitchEnabled;
/** The compass image view shown in the upper-right when the map is rotated. */
@property (nonatomic, readonly) UIImageView *compassView;
@@ -173,6 +180,20 @@ IB_DESIGNABLE
/** Resets the map rotation to a northern heading. */
- (IBAction)resetNorth;
/** The pitch of the map (measured in degrees).
*
* The default value `0` shows a completely flat map. Maximum value is `60`. */
@property (nonatomic) double pitch;
/** Changes the pitch of the map.
* @param pitch The pitch of the map (measured in degrees) relative to top-down.
*
* Changing the pitch tilts the map without changing the current center coordinate or zoom level. */
- (void)setPitch:(double)pitch;
/** Resets the map pitch to head-on. */
- (IBAction)resetPitch;
#pragma mark - Converting Map Coordinates
/** @name Converting Map Coordinates */

View File

@@ -123,6 +123,10 @@ public:
double getBearing() const;
void resetNorth();
// Pitch
void setPitch(double pitch);
double getPitch() const;
// Size
uint16_t getWidth() const;
uint16_t getHeight() const;

View File

@@ -17,6 +17,7 @@ public:
double latitude = 0;
double zoom = 0;
double bearing = 0;
double pitch = 0;
MGLUserTrackingMode userTrackingMode = MGLUserTrackingModeNone;
bool showsUserLocation = false;

View File

@@ -27,16 +27,19 @@
namespace mbgl {
typedef std::array<float, 16> mat4;
typedef std::array<double, 16> mat4;
namespace matrix {
void identity(mat4& out);
void ortho(mat4& out, float left, float right, float bottom, float top, float near, float far);
bool invert(mat4& out, mat4& a);
void ortho(mat4& out, double left, double right, double bottom, double top, double near, double far);
void perspective(mat4& out, double fovy, double aspect, double near, double far);
void copy(mat4& out, const mat4& a);
void translate(mat4& out, const mat4& a, float x, float y, float z);
void rotate_z(mat4& out, const mat4& a, float rad);
void scale(mat4& out, const mat4& a, float x, float y, float z);
void translate(mat4& out, const mat4& a, double x, double y, double z);
void rotate_x(mat4& out, const mat4& a, double rad);
void rotate_z(mat4& out, const mat4& a, double rad);
void scale(mat4& out, const mat4& a, double x, double y, double z);
void multiply(mat4& out, const mat4& a, const mat4& b);
}

View File

@@ -86,6 +86,7 @@ mbgl::Settings_NSUserDefaults *settings = nullptr;
settings->latitude = self.mapView.centerCoordinate.latitude;
settings->zoom = self.mapView.zoomLevel;
settings->bearing = self.mapView.direction;
settings->pitch = self.mapView.pitch;
settings->debug = self.mapView.isDebugActive;
settings->userTrackingMode = self.mapView.userTrackingMode;
settings->showsUserLocation = self.mapView.showsUserLocation;
@@ -99,6 +100,7 @@ mbgl::Settings_NSUserDefaults *settings = nullptr;
settings->load();
[self.mapView setCenterCoordinate:CLLocationCoordinate2DMake(settings->latitude, settings->longitude) zoomLevel:settings->zoom animated:NO];
self.mapView.direction = settings->bearing;
self.mapView.pitch = settings->pitch;
self.mapView.userTrackingMode = settings->userTrackingMode;
self.mapView.showsUserLocation = settings->showsUserLocation;
[self.mapView setDebugActive:settings->debug];

View File

@@ -11,6 +11,7 @@ Settings_NSUserDefaults::Settings_NSUserDefaults()
@"latitude" : @(latitude),
@"zoom" : @(zoom),
@"bearing" : @(bearing),
@"pitch" : @(pitch),
@"userTrackingMode" : @(userTrackingMode),
@"showsUserLocation" : @(showsUserLocation),
@"debug" : @(debug),
@@ -26,9 +27,10 @@ void Settings_NSUserDefaults::load()
latitude = [settings[@"latitude"] doubleValue];
zoom = [settings[@"zoom"] doubleValue];
bearing = [settings[@"bearing"] doubleValue];
pitch = [settings[@"pitch"] doubleValue];
debug = [settings[@"debug"] boolValue];
unsigned uncheckedTrackingMode = [settings[@"trackingMode"] unsignedIntValue];
unsigned uncheckedTrackingMode = [settings[@"userTrackingMode"] unsignedIntValue];
if (uncheckedTrackingMode > MGLUserTrackingModeNone &&
uncheckedTrackingMode <= MGLUserTrackingModeFollowWithCourse)
{
@@ -44,6 +46,7 @@ void Settings_NSUserDefaults::save()
@"latitude" : @(latitude),
@"zoom" : @(zoom),
@"bearing" : @(bearing),
@"pitch" : @(pitch),
@"userTrackingMode" : @(userTrackingMode),
@"showsUserLocation" : @(showsUserLocation),
@"debug" : @(debug),
@@ -54,4 +57,4 @@ void Settings_NSUserDefaults::save()
void Settings_NSUserDefaults::clear()
{
[NSUserDefaults resetStandardUserDefaults];
}
}

View File

@@ -85,6 +85,7 @@ CLLocationDegrees MGLDegreesFromRadians(CGFloat radians)
@property (nonatomic) UIPinchGestureRecognizer *pinch;
@property (nonatomic) UIRotationGestureRecognizer *rotate;
@property (nonatomic) UILongPressGestureRecognizer *quickZoom;
@property (nonatomic) UIPanGestureRecognizer *twoFingerDrag;
@property (nonatomic) NSMapTable *annotationIDsByAnnotation;
@property (nonatomic) NS_MUTABLE_DICTIONARY_OF(NSString *, MGLAnnotationImage *) *annotationImages;
@property (nonatomic) std::vector<uint32_t> annotationsNearbyLastTap;
@@ -279,7 +280,7 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
[self addSubview:_attributionButton];
_attributionButtonConstraints = [NSMutableArray array];
_attributionSheet = [[UIActionSheet alloc] initWithTitle:@"Mapbox for iOS"
_attributionSheet = [[UIActionSheet alloc] initWithTitle:@"Mapbox iOS SDK"
delegate:self
cancelButtonTitle:@"Cancel"
destructiveButtonTitle:nil
@@ -338,6 +339,15 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
[twoFingerTap requireGestureRecognizerToFail:_pinch];
[twoFingerTap requireGestureRecognizerToFail:_rotate];
[self addGestureRecognizer:twoFingerTap];
_twoFingerDrag = [[UIPanGestureRecognizer alloc] initWithTarget:self action:@selector(handleTwoFingerDragGesture:)];
_twoFingerDrag.minimumNumberOfTouches = 2;
_twoFingerDrag.maximumNumberOfTouches = 2;
_twoFingerDrag.delegate = self;
[_twoFingerDrag requireGestureRecognizerToFail:twoFingerTap];
[_twoFingerDrag requireGestureRecognizerToFail:_pan];
[self addGestureRecognizer:_twoFingerDrag];
_pitchEnabled = YES;
if ([[UIDevice currentDevice] userInterfaceIdiom] == UIUserInterfaceIdiomPhone)
{
@@ -1326,6 +1336,59 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
}
}
- (void)handleTwoFingerDragGesture:(UIPanGestureRecognizer *)twoFingerDrag
{
if ( ! self.isPitchEnabled) return;
_mbglMap->cancelTransitions();
if (twoFingerDrag.state == UIGestureRecognizerStateBegan)
{
[self trackGestureEvent:MGLEventGesturePitchStart forRecognizer:twoFingerDrag];
}
else if (twoFingerDrag.state == UIGestureRecognizerStateBegan || twoFingerDrag.state == UIGestureRecognizerStateChanged)
{
CGFloat gestureDistance = CGPoint([twoFingerDrag translationInView:twoFingerDrag.view]).y;
double currentPitch = _mbglMap->getPitch();
double minPitch = 0;
double maxPitch = 60.0;
double slowdown = 20.0;
double pitchNew = fmax(fmin(currentPitch - (gestureDistance / slowdown), maxPitch), minPitch);
_mbglMap->setPitch(pitchNew);
}
else if (twoFingerDrag.state == UIGestureRecognizerStateEnded || twoFingerDrag.state == UIGestureRecognizerStateCancelled)
{
[self unrotateIfNeededAnimated:YES];
//[self notifyMapChange:(mbgl::MapChangeRegionDidChange)];
}
}
- (BOOL)gestureRecognizerShouldBegin:(UIGestureRecognizer *)gestureRecognizer
{
if ([gestureRecognizer isKindOfClass:[UIPanGestureRecognizer class]])
{
UIPanGestureRecognizer *panGesture = (UIPanGestureRecognizer *)gestureRecognizer;
if (panGesture.minimumNumberOfTouches == 2)
{
CGPoint velocity = [panGesture velocityInView:panGesture.view];
double gestureAngle = MGLDegreesFromRadians(atan(velocity.y / velocity.x));
double horizontalToleranceDegrees = 20.0;
// cancel if gesture angle is not 90º±20º (more or less vertical)
if ( ! (fabs((fabs(gestureAngle) - 90.0)) < horizontalToleranceDegrees))
{
return NO;
}
}
}
return YES;
}
- (void)handleCalloutAccessoryTapGesture:(UITapGestureRecognizer *)tap
{
if ([self.delegate respondsToSelector:@selector(mapView:annotation:calloutAccessoryControlTapped:)])
@@ -1419,6 +1482,11 @@ std::chrono::steady_clock::duration secondsAsDuration(float duration)
return [NSSet setWithObject:@"allowsRotating"];
}
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingPitchEnabled
{
return [NSSet setWithObject:@"allowsPitching"];
}
- (void)setDebugActive:(BOOL)debugActive
{
_mbglMap->setDebug(debugActive);
@@ -1617,6 +1685,25 @@ mbgl::LatLngBounds MGLLatLngBoundsFromCoordinateBounds(MGLCoordinateBounds coord
[self setDirection:direction animated:NO];
}
- (double)pitch
{
return _mbglMap->getPitch();
}
- (void)setPitch:(double)pitch
{
// constrain pitch to between 0º and 60º
//
_mbglMap->setPitch(fmax(fmin(pitch, 60), 0));
//[self notifyMapChange:(mbgl::MapChangeRegionDidChange)];
}
- (void)resetPitch
{
[self setPitch:0];
}
- (CLLocationCoordinate2D)convertPoint:(CGPoint)point toCoordinateFromView:(nullable UIView *)view
{
CGPoint convertedPoint = [self convertPoint:point fromView:view];
@@ -2214,7 +2301,15 @@ CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng)
[NSException raise:@"Missing Location Services usage description" format:
@"In iOS 8 and above, this app must have a value for NSLocationWhenInUseUsageDescription or NSLocationAlwaysUsageDescription in its Info.plist."];
}
[self.locationManager requestWhenInUseAuthorization];
// request location permissions, if both keys exist ask for less permissive
if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationWhenInUseUsageDescription"])
{
[self.locationManager requestWhenInUseAuthorization];
}
else if ([[NSBundle mainBundle] objectForInfoDictionaryKey:@"NSLocationAlwaysUsageDescription"])
{
[self.locationManager requestAlwaysAuthorization];
}
}
#endif
@@ -2302,6 +2397,7 @@ CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng)
break;
}
case MGLUserTrackingModeFollow:
case MGLUserTrackingModeFollowWithCourse:
{
self.showsUserLocation = YES;
@@ -2315,7 +2411,6 @@ CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng)
break;
}
case MGLUserTrackingModeFollowWithHeading:
case MGLUserTrackingModeFollowWithCourse:
{
self.showsUserLocation = YES;
@@ -2622,10 +2717,11 @@ CLLocationCoordinate2D MGLLocationCoordinate2DFromLatLng(mbgl::LatLng latLng)
{
[self updateCompass];
if (self.pan.state == UIGestureRecognizerStateChanged ||
self.pinch.state == UIGestureRecognizerStateChanged ||
self.rotate.state == UIGestureRecognizerStateChanged ||
self.quickZoom.state == UIGestureRecognizerStateChanged) return;
if (self.pan.state == UIGestureRecognizerStateChanged ||
self.pinch.state == UIGestureRecognizerStateChanged ||
self.rotate.state == UIGestureRecognizerStateChanged ||
self.quickZoom.state == UIGestureRecognizerStateChanged ||
self.twoFingerDrag.state == UIGestureRecognizerStateChanged) return;
if (self.isAnimatingGesture) return;
@@ -3059,6 +3155,21 @@ class MBGLView : public mbgl::View
self.rotateEnabled = allowsRotating;
}
+ (NS_SET_OF(NSString *) *)keyPathsForValuesAffectingAllowsPitching
{
return [NSSet setWithObject:@"pitchEnabled"];
}
- (BOOL)allowsPitching
{
return self.pitchEnabled;
}
- (void)setAllowsPitching:(BOOL)allowsPitching
{
self.pitchEnabled = allowsPitching;
}
- (void)didReceiveMemoryWarning
{
_mbglMap->onLowMemory();

View File

@@ -30,6 +30,7 @@ extern NSString *const MGLEventGestureQuickZoom;
extern NSString *const MGLEventGesturePanStart;
extern NSString *const MGLEventGesturePinchStart;
extern NSString *const MGLEventGestureRotateStart;
extern NSString *const MGLEventGesturePitchStart;
typedef NS_DICTIONARY_OF(NSString *, id) MGLMapboxEventAttributes;
typedef NS_MUTABLE_DICTIONARY_OF(NSString *, id) MGLMutableMapboxEventAttributes;

View File

@@ -45,6 +45,7 @@ NSString *const MGLEventGestureQuickZoom = @"QuickZoom";
NSString *const MGLEventGesturePanStart = @"Pan";
NSString *const MGLEventGesturePinchStart = @"Pinch";
NSString *const MGLEventGestureRotateStart = @"Rotation";
NSString *const MGLEventGesturePitchStart = @"Pitch";
const NSUInteger MGLMaximumEventsPerFlush = 20;
const NSTimeInterval MGLFlushInterval = 60;

View File

@@ -25,9 +25,7 @@ LiveTileData::LiveTileData(const TileID& id_,
style_,
style_.layers,
state,
std::make_unique<CollisionTile>(id_.z, 4096,
source_.tile_size * id.overscaling,
0, false)) {
std::make_unique<CollisionTile>(0, 0, false)) {
state = State::loaded;
if (!tile) {

View File

@@ -271,6 +271,18 @@ void Map::resetNorth() {
}
#pragma mark - Pitch
void Map::setPitch(double pitch) {
transform->setPitch(std::min(pitch, 60.0) * M_PI / 180);
update(Update::Repaint);
}
double Map::getPitch() const {
return transform->getPitch() / M_PI * 180;
}
#pragma mark - Projection
void Map::getWorldBoundsMeters(ProjectedMeters& sw, ProjectedMeters& ne) const {
@@ -294,11 +306,11 @@ const LatLng Map::latLngForProjectedMeters(const ProjectedMeters projectedMeters
}
const vec2<double> Map::pixelForLatLng(const LatLng latLng) const {
return transform->getState().pixelForLatLng(latLng);
return transform->getState().latLngToPoint(latLng);
}
const LatLng Map::latLngForPixel(const vec2<double> pixel) const {
return transform->getState().latLngForPixel(pixel);
return transform->getState().pointToLatLng(pixel);
}
#pragma mark - Annotations

View File

@@ -336,7 +336,6 @@ bool MapContext::renderSync(const TransformState& state, const FrameData& frame)
if (!painter) {
painter = std::make_unique<Painter>(data);
painter->setup();
painter->updateRenderOrder(*style);
}
painter->setDebug(data.getDebug());
@@ -403,10 +402,6 @@ void MapContext::setSprite(const std::string& name, std::shared_ptr<const Sprite
void MapContext::onTileDataChanged() {
assert(util::ThreadContext::currentlyOn(util::ThreadType::Map));
if (painter) {
painter->updateRenderOrder(*style);
}
updateFlags |= Update::Repaint;
asyncUpdate->send();
}

View File

@@ -9,6 +9,7 @@
#include <mbgl/storage/response.hpp>
#include <mbgl/util/math.hpp>
#include <mbgl/util/box.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/util/mapbox.hpp>
#include <mbgl/storage/file_source.hpp>
#include <mbgl/style/style_layer.hpp>
@@ -284,8 +285,8 @@ TileData::State Source::addTile(MapData& data,
// If we don't find working tile data, we're just going to load it.
if (info.type == SourceType::Vector) {
auto tileData = std::make_shared<VectorTileData>(normalized_id, style, info,
transformState.getAngle(), data.getCollisionDebug());
auto tileData = std::make_shared<VectorTileData>(normalized_id, style, info,
transformState.getAngle(), transformState.getPitch(), data.getCollisionDebug());
tileData->request(data.pixelRatio, callback);
new_tile.data = tileData;
} else if (info.type == SourceType::Raster) {
@@ -332,14 +333,14 @@ std::forward_list<TileID> Source::coveringTiles(const TransformState& state) con
// Map four viewport corners to pixel coordinates
box points = state.cornersToBox(z);
const vec2<double>& center = points.center;
const TileCoordinate center = state.pointToCoordinate({ state.getWidth() / 2.0f, state.getHeight()/ 2.0f }).zoomTo(z);
std::forward_list<TileID> covering_tiles = tileCover(z, points, reparseOverscaled ? actualZ : z);
covering_tiles.sort([&center](const TileID& a, const TileID& b) {
// Sorts by distance from the box center
return std::fabs(a.x - center.x) + std::fabs(a.y - center.y) <
std::fabs(b.x - center.x) + std::fabs(b.y - center.y);
return std::fabs(a.x - center.column) + std::fabs(a.y - center.row) <
std::fabs(b.x - center.column) + std::fabs(b.y - center.row);
});
return covering_tiles;
@@ -506,7 +507,7 @@ bool Source::update(MapData& data,
updateTilePtrs();
for (auto& tilePtr : tilePtrs) {
tilePtr->data->redoPlacement(transformState.getAngle(), data.getCollisionDebug());
tilePtr->data->redoPlacement(transformState.getAngle(), transformState.getPitch(), data.getCollisionDebug());
}
updated = data.getAnimationTime();
@@ -529,14 +530,9 @@ void Source::invalidateTiles(const std::unordered_set<TileID, TileID::Hash>& ids
}
void Source::updateTilePtrs() {
std::vector<Tile*> newPtrs;
tilePtrs.clear();
for (const auto& pair : tiles) {
newPtrs.push_back(pair.second.get());
}
if (tilePtrs != newPtrs) {
tilePtrs = newPtrs;
emitTileLoaded(true);
tilePtrs.push_back(pair.second.get());
}
}
@@ -568,7 +564,7 @@ void Source::tileLoadingCompleteCallback(const TileID& normalized_id, const Tran
return;
}
data->redoPlacement(transformState.getAngle(), collisionDebug);
data->redoPlacement(transformState.getAngle(), transformState.getPitch(), collisionDebug);
emitTileLoaded(true);
}

View File

@@ -72,7 +72,7 @@ public:
virtual Bucket* getBucket(const StyleLayer&) = 0;
virtual void redoPlacement(float, bool) {}
virtual void redoPlacement(float, float, bool) {}
bool isReady() const {
return isReadyState(state);

View File

@@ -25,7 +25,7 @@ TileWorker::TileWorker(TileID id_,
maxZoom(maxZoom_),
style(style_),
state(state_),
collision(std::move(collision_)) {
collisionTile(std::move(collision_)) {
assert(style.sprite);
}
@@ -55,13 +55,12 @@ TileParseResult TileWorker::parse(const GeometryTile& geometryTile) {
return partialParse ? TileData::State::partial : TileData::State::parsed;
}
void TileWorker::redoPlacement(float angle, bool collisionDebug) {
collision->reset(angle, 0);
collision->setDebug(collisionDebug);
void TileWorker::redoPlacement(float angle, float pitch, bool collisionDebug) {
collisionTile = std::make_unique<CollisionTile>(angle, pitch, collisionDebug);
for (auto i = layers.rbegin(); i != layers.rend(); i++) {
auto bucket = getBucket(**i);
if (bucket) {
bucket->placeFeatures();
bucket->placeFeatures(*collisionTile);
}
}
}
@@ -224,7 +223,7 @@ std::unique_ptr<Bucket> TileWorker::createCircleBucket(const GeometryTileLayer&
std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer& layer,
const StyleBucket& bucket_desc) {
auto bucket = std::make_unique<SymbolBucket>(*collision, id.overscaling);
auto bucket = std::make_unique<SymbolBucket>(id.overscaling, id.z);
const float z = id.z;
auto& layout = bucket->layout;
@@ -283,7 +282,7 @@ std::unique_ptr<Bucket> TileWorker::createSymbolBucket(const GeometryTileLayer&
}
bucket->addFeatures(reinterpret_cast<uintptr_t>(this), *style.spriteAtlas, *style.glyphAtlas,
*style.glyphStore);
*style.glyphStore, *collisionTile);
return bucket->hasData() ? std::move(bucket) : nullptr;
}

View File

@@ -44,7 +44,7 @@ public:
Bucket* getBucket(const StyleLayer&) const;
TileParseResult parse(const GeometryTile&);
void redoPlacement(float angle, bool collisionDebug);
void redoPlacement(float angle, float pitch, bool collisionDebug);
std::vector<util::ptr<StyleLayer>> layers;
@@ -75,7 +75,7 @@ private:
TriangleElementsBuffer triangleElementsBuffer;
LineElementsBuffer lineElementsBuffer;
std::unique_ptr<CollisionTile> collision;
std::unique_ptr<CollisionTile> collisionTile;
// Contains all the Bucket objects for the tile. Buckets are render
// objects and they get added to this map as they get processed.

View File

@@ -347,6 +347,15 @@ double Transform::getAngle() const {
return state.angle;
}
#pragma mark - Pitch
void Transform::setPitch(double pitch) {
state.pitch = pitch;
}
double Transform::getPitch() const {
return state.pitch;
}
#pragma mark - Transition

View File

@@ -41,6 +41,10 @@ public:
void setAngle(double angle, double cx, double cy);
double getAngle() const;
// Pitch
void setPitch(double pitch);
double getPitch() const;
// Transitions
Update updateTransitions(const TimePoint& now);
void cancelTransitions();

View File

@@ -1,8 +1,9 @@
#include <mbgl/map/transform_state.hpp>
#include <mbgl/map/tile_id.hpp>
#include <mbgl/util/projection.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/box.hpp>
#include <mbgl/util/tile_coordinate.hpp>
#include <mbgl/util/interpolate.hpp>
using namespace mbgl;
@@ -10,52 +11,44 @@ using namespace mbgl;
void TransformState::matrixFor(mat4& matrix, const TileID& id, const int8_t z) const {
const double tile_scale = std::pow(2, z);
const double tile_size = scale * util::tileSize / tile_scale;
double s = util::tileSize * scale / tile_scale;
matrix::identity(matrix);
matrix::translate(matrix, matrix, id.x * s, id.y * s, 0);
matrix::scale(matrix, matrix, s / 4096.0f, s / 4096.0f, 1);
}
matrix::translate(matrix, matrix, 0.5f * (float)width, 0.5f * (float)height, 0);
matrix::rotate_z(matrix, matrix, angle);
matrix::translate(matrix, matrix, -0.5f * (float)width, -0.5f * (float)height, 0);
void TransformState::getProjMatrix(mat4& projMatrix) const {
double halfFov = std::atan(0.5 / getAltitude());
double topHalfSurfaceDistance = std::sin(halfFov) * getAltitude() /
std::sin(M_PI / 2.0f - getPitch() - halfFov);
// Calculate z value of the farthest fragment that should be rendered.
double farZ = std::cos(M_PI / 2.0f - getPitch()) * topHalfSurfaceDistance + getAltitude();
matrix::translate(matrix, matrix, pixel_x() + id.x * tile_size, pixel_y() + id.y * tile_size, 0);
matrix::perspective(projMatrix, 2.0f * std::atan((getHeight() / 2.0f) / getAltitude()),
double(getWidth()) / getHeight(), 0.1, farZ);
// TODO: Get rid of the 8 (scaling from 4096 to tile size);
float factor = scale / tile_scale / (4096.0f / util::tileSize);
matrix::scale(matrix, matrix, factor, factor, 1);
matrix::translate(projMatrix, projMatrix, 0, 0, -getAltitude());
// After the rotateX, z values are in pixel units. Convert them to
// altitude unites. 1 altitude unit = the screen height.
matrix::scale(projMatrix, projMatrix, 1, -1, 1.0f / getHeight());
matrix::rotate_x(projMatrix, projMatrix, getPitch());
matrix::rotate_z(projMatrix, projMatrix, getAngle());
matrix::translate(projMatrix, projMatrix, pixel_x() - getWidth() / 2.0f,
pixel_y() - getHeight() / 2.0f, 0);
}
box TransformState::cornersToBox(uint32_t z) const {
const double ref_scale = std::pow(2, z);
const double angle_sin = std::sin(-angle);
const double angle_cos = std::cos(-angle);
const double w_2 = static_cast<double>(width) / 2.0;
const double h_2 = static_cast<double>(height) / 2.0;
const double ss_0 = scale * util::tileSize;
const double ss_1 = ref_scale / ss_0;
const double ss_2 = ss_0 / 2.0;
// Calculate the corners of the map view. The resulting coordinates will be
// in fractional tile coordinates.
box b;
b.tl.x = ((-w_2) * angle_cos - (-h_2) * angle_sin + ss_2 - x) * ss_1;
b.tl.y = ((-w_2) * angle_sin + (-h_2) * angle_cos + ss_2 - y) * ss_1;
b.tr.x = ((+w_2) * angle_cos - (-h_2) * angle_sin + ss_2 - x) * ss_1;
b.tr.y = ((+w_2) * angle_sin + (-h_2) * angle_cos + ss_2 - y) * ss_1;
b.bl.x = ((-w_2) * angle_cos - (+h_2) * angle_sin + ss_2 - x) * ss_1;
b.bl.y = ((-w_2) * angle_sin + (+h_2) * angle_cos + ss_2 - y) * ss_1;
b.br.x = ((+w_2) * angle_cos - (+h_2) * angle_sin + ss_2 - x) * ss_1;
b.br.y = ((+w_2) * angle_sin + (+h_2) * angle_cos + ss_2 - y) * ss_1;
b.center.x = (ss_2 - x) * ss_1;
b.center.y = (ss_2 - y) * ss_1;
double w = width;
double h = height;
box b(
pointToCoordinate({ 0, 0 }).zoomTo(z),
pointToCoordinate({ w, 0 }).zoomTo(z),
pointToCoordinate({ w, h }).zoomTo(z),
pointToCoordinate({ 0, h }).zoomTo(z));
return b;
}
@@ -154,91 +147,134 @@ float TransformState::getAngle() const {
return angle;
}
float TransformState::getAltitude() const {
return altitude;
}
float TransformState::getPitch() const {
return pitch;
}
#pragma mark - Projection
const vec2<double> TransformState::pixelForLatLng(const LatLng latLng) const {
LatLng ll = getLatLng();
double zoom = getZoom();
const double centerX = static_cast<double>(width) / 2.0;
const double centerY = static_cast<double>(height) / 2.0;
const double m = Projection::getMetersPerPixelAtLatitude(0, zoom);
const double angle_sin = std::sin(-angle);
const double angle_cos = std::cos(-angle);
const ProjectedMeters givenMeters = Projection::projectedMetersForLatLng(latLng);
const double givenAbsoluteX = givenMeters.easting / m;
const double givenAbsoluteY = givenMeters.northing / m;
const ProjectedMeters centerMeters = Projection::projectedMetersForLatLng(ll);
const double centerAbsoluteX = centerMeters.easting / m;
const double centerAbsoluteY = centerMeters.northing / m;
const double deltaX = givenAbsoluteX - centerAbsoluteX;
const double deltaY = givenAbsoluteY - centerAbsoluteY;
const double translatedX = deltaX + centerX;
const double translatedY = deltaY + centerY;
const double rotatedX = translatedX * angle_cos - translatedY * angle_sin;
const double rotatedY = translatedX * angle_sin + translatedY * angle_cos;
const double rotatedCenterX = centerX * angle_cos - centerY * angle_sin;
const double rotatedCenterY = centerX * angle_sin + centerY * angle_cos;
double x_ = rotatedX + (centerX - rotatedCenterX);
double y_ = rotatedY + (centerY - rotatedCenterY);
return vec2<double>(x_, y_);
float TransformState::lngX(float lng) const {
return (180.0f + lng) * worldSize() / 360.0f;
}
const LatLng TransformState::latLngForPixel(const vec2<double> pixel) const {
LatLng ll = getLatLng();
double zoom = getZoom();
float TransformState::latY(float lat) const {
float y_ = 180.0f / M_PI * std::log(std::tan(M_PI / 4 + lat * M_PI / 360.0f));
return (180.0f - y_) * worldSize() / 360.0f;
}
const double centerX = static_cast<double>(width) / 2.0;
const double centerY = static_cast<double>(height) / 2.0;
float TransformState::xLng(float x_, float worldSize_) const {
return x_ * 360.0f / worldSize_ - 180.0f;
}
const double m = Projection::getMetersPerPixelAtLatitude(0, zoom);
float TransformState::yLat(float y_, float worldSize_) const {
float y2 = 180.0f - y_ * 360.0f / worldSize_;
return 360.0f / M_PI * std::atan(std::exp(y2 * M_PI / 180.0f)) - 90.0f;
}
const double angle_sin = std::sin(angle);
const double angle_cos = std::cos(angle);
float TransformState::zoomScale(float zoom) const {
return std::pow(2.0f, zoom);
}
const double unrotatedCenterX = centerX * angle_cos - centerY * angle_sin;
const double unrotatedCenterY = centerX * angle_sin + centerY * angle_cos;
float TransformState::worldSize() const {
return util::tileSize * scale;
}
const double unrotatedX = pixel.x * angle_cos - pixel.y * angle_sin;
const double unrotatedY = pixel.x * angle_sin + pixel.y * angle_cos;
vec2<double> TransformState::latLngToPoint(const LatLng& latLng) const {
return coordinateToPoint(latLngToCoordinate(latLng));
}
const double givenX = unrotatedX + (centerX - unrotatedCenterX);
const double givenY = unrotatedY + (centerY - unrotatedCenterY);
LatLng TransformState::pointToLatLng(const vec2<double> point) const {
return coordinateToLatLng(pointToCoordinate(point));
}
const ProjectedMeters centerMeters = Projection::projectedMetersForLatLng(ll);
TileCoordinate TransformState::latLngToCoordinate(const LatLng& latLng) const {
const float tileZoom = getIntegerZoom();
const float k = zoomScale(tileZoom) / worldSize();
return {
lngX(latLng.longitude) * k,
latY(latLng.latitude) * k,
tileZoom
};
}
const double centerAbsoluteX = centerMeters.easting / m;
const double centerAbsoluteY = centerMeters.northing / m;
LatLng TransformState::coordinateToLatLng(const TileCoordinate& coord) const {
const float worldSize_ = zoomScale(coord.zoom);
LatLng latLng = {
yLat(coord.row, worldSize_),
xLng(coord.column, worldSize_)
};
while (latLng.longitude < 180.0f) latLng.longitude += 360.0f;
while (latLng.longitude > 180.0f) latLng.longitude -= 360.0f;
return latLng;
}
const double givenAbsoluteX = givenX + centerAbsoluteX - centerX;
const double givenAbsoluteY = givenY + centerAbsoluteY - centerY;
vec2<double> TransformState::coordinateToPoint(const TileCoordinate& coord) const {
mat4 mat = coordinatePointMatrix(coord.zoom);
matrix::vec4 p;
matrix::vec4 c = {{ coord.column, coord.row, 0, 1 }};
matrix::transformMat4(p, c, mat);
return { p[0] / p[3], height - p[1] / p[3] };
}
ProjectedMeters givenMeters = ProjectedMeters(givenAbsoluteY * m, givenAbsoluteX * m);
TileCoordinate TransformState::pointToCoordinate(const vec2<double> point) const {
// adjust for date line
ProjectedMeters sw, ne;
Projection::getWorldBoundsMeters(sw, ne);
double d = ne.easting - sw.easting;
if (ll.longitude > 0 && givenMeters.easting > centerMeters.easting) givenMeters.easting -= d;
float targetZ = 0;
const float tileZoom = getIntegerZoom();
// adjust for world wrap
while (givenMeters.easting < sw.easting) givenMeters.easting += d;
while (givenMeters.easting > ne.easting) givenMeters.easting -= d;
mat4 mat = coordinatePointMatrix(tileZoom);
return Projection::latLngForProjectedMeters(givenMeters);
mat4 inverted;
bool err = matrix::invert(inverted, mat);
if (err) throw std::runtime_error("failed to invert coordinatePointMatrix");
double flippedY = height - point.y;
// since we don't know the correct projected z value for the point,
// unproject two points to get a line and then find the point on that
// line with z=0
matrix::vec4 coord0;
matrix::vec4 coord1;
matrix::vec4 point0 = {{ point.x, flippedY, 0, 1 }};
matrix::vec4 point1 = {{ point.x, flippedY, 1, 1 }};
matrix::transformMat4(coord0, point0, inverted);
matrix::transformMat4(coord1, point1, inverted);
float w0 = coord0[3];
float w1 = coord1[3];
float x0 = coord0[0] / w0;
float x1 = coord1[0] / w1;
float y0 = coord0[1] / w0;
float y1 = coord1[1] / w1;
float z0 = coord0[2] / w0;
float z1 = coord1[2] / w1;
float t = z0 == z1 ? 0 : (targetZ - z0) / (z1 - z0);
return { util::interpolate(x0, x1, t), util::interpolate(y0, y1, t), tileZoom };
}
mat4 TransformState::coordinatePointMatrix(float z) const {
mat4 proj;
getProjMatrix(proj);
float s = util::tileSize * scale / std::pow(2, z);
matrix::scale(proj, proj, s , s, 1);
matrix::multiply(proj, getPixelMatrix(), proj);
return proj;
}
mat4 TransformState::getPixelMatrix() const {
mat4 m;
matrix::identity(m);
matrix::scale(m, m, width / 2.0f, -height / 2.0f, 1);
matrix::translate(m, m, 1, -1, 0);
return m;
}

View File

@@ -1,10 +1,11 @@
#ifndef MBGL_MAP_TRANSFORM_STATE
#define MBGL_MAP_TRANSFORM_STATE
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/geo.hpp>
#include <mbgl/util/constants.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/mat4.hpp>
#include <mbgl/util/vec4.hpp>
#include <cstdint>
#include <array>
@@ -14,6 +15,7 @@ namespace mbgl {
class TileID;
struct box;
struct TileCoordinate;
class TransformState {
friend class Transform;
@@ -21,6 +23,7 @@ class TransformState {
public:
// Matrix
void matrixFor(mat4& matrix, const TileID& id, const int8_t z) const;
void getProjMatrix(mat4& matrix) const;
box cornersToBox(uint32_t z) const;
// Dimensions
@@ -28,9 +31,6 @@ public:
uint16_t getWidth() const;
uint16_t getHeight() const;
float worldSize() const;
float lngX(float lon) const;
float latY(float lat) const;
std::array<float, 2> locationCoordinate(float lon, float lat) const;
void getLonLat(double &lon, double &lat) const;
@@ -49,13 +49,26 @@ public:
// Rotation
float getAngle() const;
// Projection
const vec2<double> pixelForLatLng(const LatLng latLng) const;
const LatLng latLngForPixel(const vec2<double> pixel) const;
float getAltitude() const;
float getPitch() const;
// Changing
bool isChanging() const;
double pixel_x() const;
double pixel_y() const;
// Conversion and projection
vec2<double> latLngToPoint(const LatLng& latLng) const;
LatLng pointToLatLng(const vec2<double> point) const;
TileCoordinate latLngToCoordinate(const LatLng& latLng) const;
LatLng coordinateToLatLng(const TileCoordinate& coord) const;
vec2<double> coordinateToPoint(const TileCoordinate& coord) const;
TileCoordinate pointToCoordinate(const vec2<double> point) const;
private:
void constrain(double& scale, double& y) const;
@@ -63,12 +76,19 @@ private:
double min_scale = std::pow(2, 0);
double max_scale = std::pow(2, 18);
double pixel_x() const;
double pixel_y() const;
// logical dimensions
uint16_t width = 0, height = 0;
float xLng(float x, float worldSize) const;
float yLat(float y, float worldSize) const;
float lngX(float lon) const;
float latY(float lat) const;
float zoomScale(float zoom) const;
float worldSize() const;
mat4 coordinatePointMatrix(float z) const;
mat4 getPixelMatrix() const;
private:
// animation state
bool rotating = false;
@@ -80,6 +100,8 @@ private:
double x = 0, y = 0;
double angle = 0;
double scale = 1;
double altitude = 1.5;
double pitch = 0.0;
// cache values for spherical mercator math
double Bc = (scale * util::tileSize) / 360;

View File

@@ -16,6 +16,7 @@ VectorTileData::VectorTileData(const TileID& id_,
Style& style_,
const SourceInfo& source_,
float angle,
float pitch,
bool collisionDebug)
: TileData(id_),
worker(style_.workers),
@@ -25,9 +26,7 @@ VectorTileData::VectorTileData(const TileID& id_,
style_,
style_.layers,
state,
std::make_unique<CollisionTile>(id_.z, 4096,
source_.tile_size * id.overscaling,
angle, collisionDebug)),
std::make_unique<CollisionTile>(angle, pitch, collisionDebug)),
source(source_),
lastAngle(angle),
currentAngle(angle) {
@@ -98,11 +97,14 @@ Bucket* VectorTileData::getBucket(const StyleLayer& layer) {
return tileWorker.getBucket(layer);
}
void VectorTileData::redoPlacement(float angle, bool collisionDebug) {
if (angle == currentAngle && collisionDebug == currentCollisionDebug)
void VectorTileData::redoPlacement(float angle, float pitch, bool collisionDebug) {
if (angle == currentAngle &&
pitch == currentPitch &&
collisionDebug == currentCollisionDebug)
return;
lastAngle = angle;
lastPitch = pitch;
lastCollisionDebug = collisionDebug;
if (state != State::parsed || redoingPlacement)
@@ -110,9 +112,10 @@ void VectorTileData::redoPlacement(float angle, bool collisionDebug) {
redoingPlacement = true;
currentAngle = angle;
currentPitch = pitch;
currentCollisionDebug = collisionDebug;
workRequest = worker.redoPlacement(tileWorker, angle, collisionDebug, [this] {
workRequest = worker.redoPlacement(tileWorker, angle, pitch, collisionDebug, [this] {
for (const auto& layer : tileWorker.layers) {
auto bucket = getBucket(*layer);
if (bucket) {
@@ -120,7 +123,7 @@ void VectorTileData::redoPlacement(float angle, bool collisionDebug) {
}
}
redoingPlacement = false;
redoPlacement(lastAngle, lastCollisionDebug);
redoPlacement(lastAngle, lastPitch, lastCollisionDebug);
});
}

View File

@@ -19,6 +19,7 @@ public:
Style&,
const SourceInfo&,
float angle_,
float pitch_,
bool collisionDebug_);
~VectorTileData();
@@ -29,7 +30,7 @@ public:
bool reparse(std::function<void ()> callback);
void redoPlacement(float angle, bool collisionDebug) override;
void redoPlacement(float angle, float pitch, bool collisionDebug) override;
void cancel() override;
@@ -43,6 +44,8 @@ private:
std::string data;
float lastAngle = 0;
float currentAngle;
float lastPitch = 0;
float currentPitch;
bool lastCollisionDebug = 0;
bool currentCollisionDebug = 0;
bool redoingPlacement = false;

View File

@@ -5,6 +5,8 @@
#include <mbgl/util/noncopyable.hpp>
#include <mbgl/util/mat4.hpp>
#include <atomic>
#define BUFFER_OFFSET(i) ((char*)nullptr + (i))
namespace mbgl {
@@ -12,9 +14,12 @@ namespace mbgl {
class Painter;
class StyleLayer;
class TileID;
class CollisionTile;
class Bucket : private util::noncopyable {
public:
Bucket() : uploaded(false) {}
// As long as this bucket has a Prepare render pass, this function is getting called. Typically,
// this only happens once when the bucket is being rendered for the first time.
virtual void upload() = 0;
@@ -29,11 +34,11 @@ public:
return !uploaded;
}
virtual void placeFeatures() {}
virtual void placeFeatures(CollisionTile&) {}
virtual void swapRenderData() {}
protected:
bool uploaded = false;
std::atomic<bool> uploaded;
};

View File

@@ -132,13 +132,11 @@ void Painter::lineWidth(float line_width) {
}
void Painter::changeMatrix() {
// Initialize projection matrix
matrix::ortho(projMatrix, 0, state.getWidth(), state.getHeight(), 0, -1, 1);
state.getProjMatrix(projMatrix);
// The extrusion matrix.
matrix::identity(extrudeMatrix);
matrix::multiply(extrudeMatrix, projMatrix, extrudeMatrix);
matrix::rotate_z(extrudeMatrix, extrudeMatrix, state.getAngle());
matrix::ortho(extrudeMatrix, 0, state.getWidth(), state.getHeight(), 0, 0, -1);
// The native matrix is a 1:1 matrix that paints the coordinates at the
// same screen position as the vertex specifies.
@@ -156,10 +154,6 @@ void Painter::clear() {
MBGL_CHECK_ERROR(glClear(GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT));
}
void Painter::setStrata(float value) {
strata = value;
}
void Painter::prepareTile(const Tile& tile) {
const GLint ref = (GLint)tile.clip.reference.to_ulong();
const GLuint mask = (GLuint)tile.clip.mask.to_ulong();
@@ -184,6 +178,9 @@ void Painter::render(const Style& style, TransformState state_, const FrameData&
resize();
changeMatrix();
// Figure out what buckets we have to draw and what order we have to draw them in.
const auto order = determineRenderOrder(style);
// - UPLOAD PASS -------------------------------------------------------------------------------
// Uploads all required buffers and images before we do any actual rendering.
{
@@ -202,6 +199,7 @@ void Painter::render(const Style& style, TransformState state_, const FrameData&
}
}
// - CLIPPING MASKS ----------------------------------------------------------------------------
// Draws the clipping masks to the stencil buffer.
{
@@ -225,19 +223,19 @@ void Painter::render(const Style& style, TransformState state_, const FrameData&
if (debug::renderTree) { Log::Info(Event::Render, "{"); indent++; }
// TODO: Correctly compute the number of layers recursively beforehand.
const float strataThickness = 1.0f / (order.size() + 1);
depthRangeSize = 1 - (order.size() + 2) * numSublayers * depthEpsilon;
// - OPAQUE PASS -------------------------------------------------------------------------------
// Render everything top-to-bottom by using reverse iterators. Render opaque objects first.
renderPass(RenderPass::Opaque,
order.rbegin(), order.rend(),
0, 1, strataThickness);
0, 1);
// - TRANSLUCENT PASS --------------------------------------------------------------------------
// Make a second pass, rendering translucent objects. This time, we render bottom-to-top.
renderPass(RenderPass::Translucent,
order.begin(), order.end(),
order.size() - 1, -1, strataThickness);
order.size() - 1, -1);
if (debug::renderTree) { Log::Info(Event::Render, "}"); indent--; }
@@ -268,12 +266,9 @@ void Painter::render(const Style& style, TransformState state_, const FrameData&
template <class Iterator>
void Painter::renderPass(RenderPass pass_,
Iterator it, Iterator end,
std::size_t i, int8_t increment,
const float strataThickness) {
std::size_t i, int8_t increment) {
pass = pass_;
const double zoom = state.getZoom();
MBGL_DEBUG_GROUP(pass == RenderPass::Opaque ? "opaque" : "translucent");
if (debug::renderTree) {
@@ -284,24 +279,16 @@ void Painter::renderPass(RenderPass pass_,
config.blend = pass == RenderPass::Translucent;
for (; it != end; ++it, i += increment) {
currentLayer = i;
const auto& item = *it;
if (item.bucket && item.tile) {
// Skip this layer if it's outside the range of min/maxzoom.
// This may occur when there /is/ a bucket created for this layer, but the min/max-zoom
// is set to a fractional value, or value that is larger than the source maxzoom.
if (item.layer.bucket->min_zoom > zoom ||
item.layer.bucket->max_zoom <= zoom) {
continue;
}
if (item.layer.hasRenderPass(pass)) {
MBGL_DEBUG_GROUP(item.layer.id + " - " + std::string(item.tile->id));
setStrata(i * strataThickness);
prepareTile(*item.tile);
item.bucket->render(*this, item.layer, item.tile->id, item.tile->matrix);
}
} else {
MBGL_DEBUG_GROUP("background");
setStrata(i * strataThickness);
renderBackground(item.layer);
}
}
@@ -311,8 +298,8 @@ void Painter::renderPass(RenderPass pass_,
}
}
void Painter::updateRenderOrder(const Style& style) {
order.clear();
std::vector<RenderItem> Painter::determineRenderOrder(const Style& style) {
std::vector<RenderItem> order;
for (const auto& layerPtr : style.layers) {
const auto& layer = *layerPtr;
@@ -349,6 +336,15 @@ void Painter::updateRenderOrder(const Style& style) {
continue;
}
// Skip this layer if it's outside the range of min/maxzoom.
// This may occur when there /is/ a bucket created for this layer, but the min/max-zoom
// is set to a fractional value, or value that is larger than the source maxzoom.
const double zoom = state.getZoom();
if (layer.bucket->min_zoom > zoom ||
layer.bucket->max_zoom <= zoom) {
continue;
}
const auto& tiles = source->getTiles();
for (auto tile : tiles) {
assert(tile);
@@ -362,6 +358,8 @@ void Painter::updateRenderOrder(const Style& style) {
}
}
}
return order;
}
void Painter::renderBackground(const StyleLayer &layer_desc) {
@@ -387,7 +385,7 @@ void Painter::renderBackground(const StyleLayer &layer_desc) {
patternShader->u_opacity = properties.opacity;
LatLng latLng = state.getLatLng();
vec2<double> center = state.pixelForLatLng(latLng);
vec2<double> center = state.latLngToPoint(latLng);
float scale = 1 / std::pow(2, zoomFraction);
std::array<float, 2> sizeA = imagePosA.size;
@@ -428,7 +426,8 @@ void Painter::renderBackground(const StyleLayer &layer_desc) {
config.stencilTest = false;
config.depthTest = true;
config.depthRange = { strata + strata_epsilon, 1.0f };
config.depthRange = { 1.0f, 1.0f };
MBGL_CHECK_ERROR(glDrawArrays(GL_TRIANGLE_STRIP, 0, 4));
}
@@ -457,3 +456,9 @@ mat4 Painter::translatedMatrix(const mat4& matrix, const std::array<float, 2> &t
return vtxMatrix;
}
}
void Painter::setDepthSublayer(int n) {
float nearDepth = ((1 + currentLayer) * numSublayers + n) * depthEpsilon;
float farDepth = nearDepth + depthRangeSize;
config.depthRange = { nearDepth, farDepth };
}

View File

@@ -123,9 +123,6 @@ public:
// Changes whether debug information is drawn onto the map
void setDebug(bool enabled);
// Configures the painter strata that is used for early z-culling of fragments.
void setStrata(float strata);
void drawClippingMasks(const std::set<Source*>&);
void drawClippingMask(const mat4& matrix, const ClipID& clip);
@@ -137,17 +134,16 @@ public:
bool needsAnimation() const;
void updateRenderOrder(const Style& style);
private:
void setupShaders();
mat4 translatedMatrix(const mat4& matrix, const std::array<float, 2> &translation, const TileID &id, TranslateAnchorType anchor);
std::vector<RenderItem> determineRenderOrder(const Style& style);
template <class Iterator>
void renderPass(RenderPass,
Iterator it, Iterator end,
std::size_t i, int8_t iIncrement,
const float strataThickness);
std::size_t i, int8_t increment);
void prepareTile(const Tile& tile);
@@ -162,6 +158,8 @@ private:
SDFShader& sdfShader,
void (SymbolBucket::*drawSDF)(SDFShader&));
void setDepthSublayer(int n);
public:
void useProgram(uint32_t program);
void lineWidth(float lineWidth);
@@ -199,12 +197,13 @@ private:
uint32_t gl_program = 0;
float gl_lineWidth = 0;
std::array<uint16_t, 2> gl_viewport = {{ 0, 0 }};
float strata = 0;
RenderPass pass = RenderPass::Opaque;
const float strata_epsilon = 1.0f / (1 << 16);
Color background = {{ 0, 0, 0, 0 }};
std::vector<RenderItem> order;
int numSublayers = 3;
size_t currentLayer;
float depthRangeSize;
const float depthEpsilon = 1.0f / (1 << 16);
public:
FrameHistory frameHistory;

View File

@@ -40,7 +40,7 @@ void Painter::renderCircle(CircleBucket& bucket,
useProgram(circleShader->program);
circleShader->u_matrix = vtxMatrix;
circleShader->u_exmatrix = projMatrix;
circleShader->u_exmatrix = extrudeMatrix;
circleShader->u_color = color;
circleShader->u_blur = std::max(properties.blur, antialiasing);
circleShader->u_size = properties.radius;

View File

@@ -55,7 +55,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
static_cast<float>(frame.framebufferSize[0]),
static_cast<float>(frame.framebufferSize[1])
}};
config.depthRange = { strata, 1.0f };
setDepthSublayer(0);
bucket.drawVertices(*outlineShader);
}
@@ -95,7 +95,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
// Draw the actual triangles into the color & stencil buffer.
config.depthMask = GL_TRUE;
config.depthRange = { strata, 1.0f };
setDepthSublayer(0);
bucket.drawElements(*patternShader);
}
}
@@ -112,7 +112,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
// Draw the actual triangles into the color & stencil buffer.
config.depthMask = GL_TRUE;
config.depthRange = { strata + strata_epsilon, 1.0f };
setDepthSublayer(1);
bucket.drawElements(*plainShader);
}
}
@@ -132,7 +132,7 @@ void Painter::renderFill(FillBucket& bucket, const StyleLayer &layer_desc, const
static_cast<float>(frame.framebufferSize[1])
}};
config.depthRange = { strata + strata_epsilon + strata_epsilon, 1.0f };
setDepthSublayer(2);
bucket.drawVertices(*outlineShader);
}
}

View File

@@ -11,6 +11,7 @@
#include <mbgl/shader/linepattern_shader.hpp>
#include <mbgl/geometry/sprite_atlas.hpp>
#include <mbgl/geometry/line_atlas.hpp>
#include <mbgl/util/mat2.hpp>
using namespace mbgl;
@@ -51,9 +52,22 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
color[2] *= properties.opacity;
color[3] *= properties.opacity;
float ratio = state.getScale() / std::pow(2, id.z) / 8.0 * id.overscaling;
mat2 antialiasingMatrix;
matrix::identity(antialiasingMatrix);
matrix::scale(antialiasingMatrix, antialiasingMatrix, 1.0, std::cos(state.getPitch()));
matrix::rotate(antialiasingMatrix, antialiasingMatrix, state.getAngle());
// calculate how much longer the real world distance is at the top of the screen
// than at the middle of the screen.
float topedgelength = std::sqrt(std::pow(state.getHeight(), 2) / 4 * (1 + std::pow(state.getAltitude(), 2)));
float x = state.getHeight() / 2.0f * std::tan(state.getPitch());
float extra = (topedgelength + x) / topedgelength - 1;
mat4 vtxMatrix = translatedMatrix(matrix, properties.translate, id, properties.translateAnchor);
config.depthRange = { strata, 1.0f };
setDepthSublayer(0);
if (!properties.dash_array.from.empty()) {
@@ -62,7 +76,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
linesdfShader->u_matrix = vtxMatrix;
linesdfShader->u_exmatrix = extrudeMatrix;
linesdfShader->u_linewidth = {{ outset, inset }};
linesdfShader->u_ratio = data.pixelRatio;
linesdfShader->u_ratio = ratio;
linesdfShader->u_blur = blur;
linesdfShader->u_color = color;
@@ -83,6 +97,8 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
linesdfShader->u_image = 0;
linesdfShader->u_sdfgamma = lineAtlas->width / (properties.dash_line_width * std::min(posA.width, posB.width) * 256.0 * data.pixelRatio) / 2;
linesdfShader->u_mix = properties.dash_array.t;
linesdfShader->u_extra = extra;
linesdfShader->u_antialiasingmatrix = antialiasingMatrix;
bucket.drawLineSDF(*linesdfShader);
@@ -97,7 +113,7 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
linepatternShader->u_matrix = vtxMatrix;
linepatternShader->u_exmatrix = extrudeMatrix;
linepatternShader->u_linewidth = {{ outset, inset }};
linepatternShader->u_ratio = data.pixelRatio;
linepatternShader->u_ratio = ratio;
linepatternShader->u_blur = blur;
linepatternShader->u_pattern_size_a = {{imagePosA.size[0] * factor * properties.image.fromScale, imagePosA.size[1]}};
@@ -108,10 +124,11 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
linepatternShader->u_pattern_br_b = imagePosB.br;
linepatternShader->u_fade = properties.image.t;
linepatternShader->u_opacity = properties.opacity;
linepatternShader->u_extra = extra;
linepatternShader->u_antialiasingmatrix = antialiasingMatrix;
MBGL_CHECK_ERROR(glActiveTexture(GL_TEXTURE0));
spriteAtlas->bind(true);
config.depthRange = { strata + strata_epsilon, 1.0f }; // may or may not matter
bucket.drawLinePatterns(*linepatternShader);
@@ -121,8 +138,10 @@ void Painter::renderLine(LineBucket& bucket, const StyleLayer &layer_desc, const
lineShader->u_matrix = vtxMatrix;
lineShader->u_exmatrix = extrudeMatrix;
lineShader->u_linewidth = {{ outset, inset }};
lineShader->u_ratio = data.pixelRatio;
lineShader->u_ratio = ratio;
lineShader->u_blur = blur;
lineShader->u_extra = extra;
lineShader->u_antialiasingmatrix = antialiasingMatrix;
lineShader->u_color = color;

View File

@@ -24,7 +24,7 @@ void Painter::renderRaster(RasterBucket& bucket, const StyleLayer &layer_desc, c
config.stencilTest = true;
config.depthTest = true;
config.depthRange = { strata + strata_epsilon, 1.0f };
setDepthSublayer(0);
bucket.drawRaster(*rasterShader, tileStencilBuffer, coveringRasterArray);
}
}

View File

@@ -28,25 +28,40 @@ void Painter::renderSDF(SymbolBucket &bucket,
{
mat4 vtxMatrix = translatedMatrix(matrix, styleProperties.translate, id, styleProperties.translate_anchor);
mat4 exMatrix;
matrix::copy(exMatrix, projMatrix);
bool aligned_with_map = (bucketProperties.rotation_alignment == RotationAlignmentType::Map);
const float angleOffset = aligned_with_map ? state.getAngle() : 0;
bool skewed = aligned_with_map;
mat4 exMatrix;
float s;
float gammaScale;
if (angleOffset) {
matrix::rotate_z(exMatrix, exMatrix, angleOffset);
if (skewed) {
matrix::identity(exMatrix);
s = 4096.0f / util::tileSize / id.overscaling / std::pow(2, state.getZoom() - id.z);
gammaScale = 1.0f / std::cos(state.getPitch());
} else {
exMatrix = extrudeMatrix;
s = state.getAltitude();
gammaScale = 1.0f;
}
matrix::scale(exMatrix, exMatrix, s, s, 1);
// If layerStyle.size > bucket.info.fontSize then labels may collide
float fontSize = styleProperties.size;
float fontScale = fontSize / sdfFontSize;
matrix::scale(exMatrix, exMatrix, fontScale, fontScale, 1.0f);
// calculate how much longer the real world distance is at the top of the screen
// than at the middle of the screen.
float topedgelength = std::sqrt(std::pow(state.getHeight(), 2) / 4.0f * (1.0f + std::pow(state.getAltitude(), 2)));
float x = state.getHeight() / 2.0f * std::tan(state.getPitch());
float extra = (topedgelength + x) / topedgelength - 1;
useProgram(sdfShader.program);
sdfShader.u_matrix = vtxMatrix;
sdfShader.u_exmatrix = exMatrix;
sdfShader.u_texsize = texsize;
sdfShader.u_skewed = skewed;
sdfShader.u_extra = extra;
// adjust min/max zooms for variable font sies
float zoomAdjust = std::log(fontSize / bucketProperties.size) / std::log(2);
@@ -70,7 +85,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
// We're drawing in the translucent pass which is bottom-to-top, so we need
// to draw the halo first.
if (styleProperties.halo_color[3] > 0.0f && styleProperties.halo_width > 0.0f) {
sdfShader.u_gamma = styleProperties.halo_blur * blurOffset / fontScale / sdfPx + gamma;
sdfShader.u_gamma = (styleProperties.halo_blur * blurOffset / fontScale / sdfPx + gamma) * gammaScale;
if (styleProperties.opacity < 1.0f) {
Color color = styleProperties.halo_color;
@@ -85,13 +100,13 @@ void Painter::renderSDF(SymbolBucket &bucket,
sdfShader.u_buffer = (haloOffset - styleProperties.halo_width / fontScale) / sdfPx;
config.depthRange = { strata, 1.0f };
setDepthSublayer(0);
(bucket.*drawSDF)(sdfShader);
}
// Then, we draw the text/icon over the halo
if (styleProperties.color[3] > 0.0f) {
sdfShader.u_gamma = gamma;
sdfShader.u_gamma = gamma * gammaScale;
if (styleProperties.opacity < 1.0f) {
Color color = styleProperties.color;
@@ -106,7 +121,7 @@ void Painter::renderSDF(SymbolBucket &bucket,
sdfShader.u_buffer = (256.0f - 64.0f) / 256.0f;
config.depthRange = { strata + strata_epsilon, 1.0f };
setDepthSublayer(1);
(bucket.*drawSDF)(sdfShader);
}
}
@@ -120,7 +135,6 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
const auto &properties = layer_desc.getProperties<SymbolProperties>();
const auto &layout = bucket.layout;
config.depthTest = true;
config.depthMask = GL_FALSE;
if (bucket.hasCollisionBoxData()) {
@@ -133,7 +147,7 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
collisionBoxShader->u_maxzoom = (id.z + 1) * 10;
lineWidth(1.0f);
config.depthRange = { strata, 1.0f };
setDepthSublayer(0);
bucket.drawCollisionBoxes(*collisionBoxShader);
}
@@ -150,6 +164,8 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
config.stencilTest = drawAcrossEdges ? false : true;
if (bucket.hasIconData()) {
config.depthTest = layout.icon.rotation_alignment == RotationAlignmentType::Map;
bool sdf = bucket.sdfIcons;
const float angleOffset =
@@ -160,7 +176,8 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
const float fontSize = properties.icon.size;
const float fontScale = fontSize / 1.0f;
spriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line || angleOffset != 0 || fontScale != 1 || sdf);
spriteAtlas->bind(state.isChanging() || layout.placement == PlacementType::Line
|| angleOffset != 0 || fontScale != 1 || sdf || state.getPitch() != 0);
if (sdf) {
renderSDF(bucket,
@@ -175,19 +192,33 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
} else {
mat4 vtxMatrix = translatedMatrix(matrix, properties.icon.translate, id, properties.icon.translate_anchor);
bool skewed = layout.icon.rotation_alignment == RotationAlignmentType::Map;
mat4 exMatrix;
matrix::copy(exMatrix, projMatrix);
float s;
if (angleOffset) {
matrix::rotate_z(exMatrix, exMatrix, angleOffset);
if (skewed) {
matrix::identity(exMatrix);
s = 4096.0f / util::tileSize / id.overscaling / std::pow(2, state.getZoom() - id.z);
} else {
exMatrix = extrudeMatrix;
s = state.getAltitude();
}
matrix::scale(exMatrix, exMatrix, s, s, 1);
matrix::scale(exMatrix, exMatrix, fontScale, fontScale, 1.0f);
// calculate how much longer the real world distance is at the top of the screen
// than at the middle of the screen.
float topedgelength = std::sqrt(std::pow(state.getHeight(), 2) / 4.0f * (1.0f + std::pow(state.getAltitude(), 2)));
float x = state.getHeight() / 2.0f * std::tan(state.getPitch());
float extra = (topedgelength + x) / topedgelength - 1;
useProgram(iconShader->program);
iconShader->u_matrix = vtxMatrix;
iconShader->u_exmatrix = exMatrix;
iconShader->u_texsize = {{ float(spriteAtlas->getWidth()) / 4.0f, float(spriteAtlas->getHeight()) / 4.0f }};
iconShader->u_skewed = skewed;
iconShader->u_extra = extra;
// adjust min/max zooms for variable font sies
float zoomAdjust = std::log(fontSize / layout.icon.size) / std::log(2);
@@ -199,12 +230,14 @@ void Painter::renderSymbol(SymbolBucket &bucket, const StyleLayer &layer_desc, c
iconShader->u_fadezoom = state.getNormalizedZoom() * 10;
iconShader->u_opacity = properties.icon.opacity;
config.depthRange = { strata, 1.0f };
setDepthSublayer(0);
bucket.drawIcons(*iconShader);
}
}
if (bucket.hasTextData()) {
config.depthTest = layout.text.rotation_alignment == RotationAlignmentType::Map;
glyphAtlas->bind();
renderSDF(bucket,

View File

@@ -53,8 +53,8 @@ SymbolInstance::SymbolInstance(Anchor &anchor, const std::vector<Coordinate> &li
iconCollisionFeature(line, anchor, shapedIcon, iconBoxScale, iconPadding, iconAlongLine) {};
SymbolBucket::SymbolBucket(CollisionTile &collision_, float overscaling_)
: collision(collision_), overscaling(overscaling_) {
SymbolBucket::SymbolBucket(float overscaling_, float zoom_)
: overscaling(overscaling_), zoom(zoom_), tileSize(512 * overscaling_), tilePixelRatio(tileExtent / tileSize) {
}
SymbolBucket::~SymbolBucket() {
@@ -174,7 +174,8 @@ bool SymbolBucket::needsDependencies(const GeometryTileLayer& layer,
void SymbolBucket::addFeatures(uintptr_t tileUID,
SpriteAtlas& spriteAtlas,
GlyphAtlas& glyphAtlas,
GlyphStore& glyphStore) {
GlyphStore& glyphStore,
CollisionTile& collisionTile) {
float horizontalAlign = 0.5;
float verticalAlign = 0.5;
@@ -264,7 +265,7 @@ void SymbolBucket::addFeatures(uintptr_t tileUID,
features.clear();
placeFeatures(true);
placeFeatures(collisionTile, true);
}
@@ -275,13 +276,13 @@ void SymbolBucket::addFeature(const std::vector<std::vector<Coordinate>> &lines,
const float glyphSize = 24.0f;
const float fontScale = layout.text.size / glyphSize;
const float textBoxScale = collision.tilePixelRatio * fontScale;
const float textMaxBoxScale = collision.tilePixelRatio * layout.text.max_size / glyphSize;
const float iconBoxScale = collision.tilePixelRatio * layout.icon.size;
const float symbolSpacing = collision.tilePixelRatio * layout.spacing;
const float textBoxScale = tilePixelRatio * fontScale;
const float textMaxBoxScale = tilePixelRatio * layout.text.max_size / glyphSize;
const float iconBoxScale = tilePixelRatio * layout.icon.size;
const float symbolSpacing = tilePixelRatio * layout.spacing;
const bool avoidEdges = layout.avoid_edges && layout.placement != PlacementType::Line;
const float textPadding = layout.text.padding * collision.tilePixelRatio;
const float iconPadding = layout.icon.padding * collision.tilePixelRatio;
const float textPadding = layout.text.padding * tilePixelRatio;
const float iconPadding = layout.icon.padding * tilePixelRatio;
const float textMaxAngle = layout.text.max_angle * M_PI / 180;
const bool textAlongLine =
layout.text.rotation_alignment == RotationAlignmentType::Map &&
@@ -354,11 +355,11 @@ bool SymbolBucket::anchorIsTooClose(const std::u32string &text, const float repe
return false;
}
void SymbolBucket::placeFeatures() {
placeFeatures(false);
void SymbolBucket::placeFeatures(CollisionTile& collisionTile) {
placeFeatures(collisionTile, false);
}
void SymbolBucket::placeFeatures(bool swapImmediately) {
void SymbolBucket::placeFeatures(CollisionTile& collisionTile, bool swapImmediately) {
renderDataInProgress = std::make_unique<SymbolRenderData>();
@@ -380,8 +381,8 @@ void SymbolBucket::placeFeatures(bool swapImmediately) {
// Don't sort symbols that won't overlap because it isn't necessary and
// because it causes more labels to pop in and out when rotating.
if (mayOverlap) {
float sin = std::sin(collision.angle);
float cos = std::cos(collision.angle);
float sin = std::sin(collisionTile.angle);
float cos = std::cos(collisionTile.angle);
std::sort(symbolInstances.begin(), symbolInstances.end(), [sin, cos](SymbolInstance &a, SymbolInstance &b) {
const float aRotated = sin * a.x + cos * a.y;
@@ -401,9 +402,9 @@ void SymbolBucket::placeFeatures(bool swapImmediately) {
// Calculate the scales at which the text and icon can be placed without collision.
float glyphScale = hasText && !layout.text.allow_overlap ?
collision.placeFeature(symbolInstance.textCollisionFeature) : collision.minScale;
collisionTile.placeFeature(symbolInstance.textCollisionFeature) : collisionTile.minScale;
float iconScale = hasIcon && !layout.icon.allow_overlap ?
collision.placeFeature(symbolInstance.iconCollisionFeature) : collision.minScale;
collisionTile.placeFeature(symbolInstance.iconCollisionFeature) : collisionTile.minScale;
// Combine the scales for icons and text.
@@ -421,33 +422,32 @@ void SymbolBucket::placeFeatures(bool swapImmediately) {
if (hasText) {
if (!layout.text.ignore_placement) {
collision.insertFeature(symbolInstance.textCollisionFeature, glyphScale);
collisionTile.insertFeature(symbolInstance.textCollisionFeature, glyphScale);
}
if (glyphScale < collision.maxScale) {
if (glyphScale < collisionTile.maxScale) {
addSymbols<SymbolRenderData::TextBuffer, TextElementGroup>(renderDataInProgress->text,
symbolInstance.glyphQuads, glyphScale, layout.text.keep_upright, textAlongLine);
symbolInstance.glyphQuads, glyphScale, layout.text.keep_upright, textAlongLine, collisionTile.angle);
}
}
if (hasIcon) {
if (!layout.icon.ignore_placement) {
collision.insertFeature(symbolInstance.iconCollisionFeature, iconScale);
collisionTile.insertFeature(symbolInstance.iconCollisionFeature, iconScale);
}
if (iconScale < collision.maxScale) {
if (iconScale < collisionTile.maxScale) {
addSymbols<SymbolRenderData::IconBuffer, IconElementGroup>(renderDataInProgress->icon,
symbolInstance.iconQuads, iconScale, layout.icon.keep_upright, iconAlongLine);
symbolInstance.iconQuads, iconScale, layout.icon.keep_upright, iconAlongLine, collisionTile.angle);
}
}
}
if (collision.getDebug()) addToDebugBuffers();
if (collisionTile.getDebug()) addToDebugBuffers(collisionTile);
if (swapImmediately) swapRenderData();
}
template <typename Buffer, typename GroupType>
void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const bool alongLine) {
const float zoom = collision.zoom;
void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const bool alongLine, const float placementAngle) {
const float placementZoom = ::fmax(std::log(scale) / std::log(2) + zoom, 0);
@@ -461,10 +461,10 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float
float minZoom =
util::max(static_cast<float>(zoom + log(symbol.minScale) / log(2)), placementZoom);
float maxZoom = util::min(static_cast<float>(zoom + log(symbol.maxScale) / log(2)), 25.0f);
const auto &glyphAnchor = symbol.anchor;
const auto &anchorPoint = symbol.anchorPoint;
// drop upside down versions of glyphs
const float a = std::fmod(symbol.angle + collision.angle + M_PI, M_PI * 2);
const float a = std::fmod(symbol.angle + placementAngle + M_PI, M_PI * 2);
if (keepUpright && alongLine && (a <= M_PI / 2 || a > M_PI * 3 / 2)) continue;
@@ -491,13 +491,13 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float
uint32_t triangleIndex = triangleGroup.vertex_length;
// coordinates (2 triangles)
buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tl.x, tl.y, tex.x, tex.y, minZoom,
buffer.vertices.add(anchorPoint.x, anchorPoint.y, tl.x, tl.y, tex.x, tex.y, minZoom,
maxZoom, placementZoom);
buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, tr.x, tr.y, tex.x + tex.w, tex.y,
buffer.vertices.add(anchorPoint.x, anchorPoint.y, tr.x, tr.y, tex.x + tex.w, tex.y,
minZoom, maxZoom, placementZoom);
buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, bl.x, bl.y, tex.x, tex.y + tex.h,
buffer.vertices.add(anchorPoint.x, anchorPoint.y, bl.x, bl.y, tex.x, tex.y + tex.h,
minZoom, maxZoom, placementZoom);
buffer.vertices.add(glyphAnchor.x, glyphAnchor.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h,
buffer.vertices.add(anchorPoint.x, anchorPoint.y, br.x, br.y, tex.x + tex.w, tex.y + tex.h,
minZoom, maxZoom, placementZoom);
// add the two triangles, referencing the four coordinates we just inserted.
@@ -509,11 +509,10 @@ void SymbolBucket::addSymbols(Buffer &buffer, const SymbolQuads &symbols, float
}
}
void SymbolBucket::addToDebugBuffers() {
void SymbolBucket::addToDebugBuffers(CollisionTile &collisionTile) {
const float yStretch = 1.0f;
const float angle = collision.angle;
const float zoom = collision.zoom;
const float yStretch = collisionTile.yStretch;
const float angle = collisionTile.angle;
float angle_sin = std::sin(-angle);
float angle_cos = std::cos(-angle);
std::array<float, 4> matrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};

View File

@@ -64,7 +64,7 @@ class SymbolBucket : public Bucket {
typedef ElementGroup<1> CollisionBoxElementGroup;
public:
SymbolBucket(CollisionTile &collision, float overscaling);
SymbolBucket(float overscaling, float zoom);
~SymbolBucket() override;
void upload() override;
@@ -77,7 +77,8 @@ public:
void addFeatures(uintptr_t tileUID,
SpriteAtlas&,
GlyphAtlas&,
GlyphStore&);
GlyphStore&,
CollisionTile&);
void drawGlyphs(SDFShader& shader);
void drawIcons(SDFShader& shader);
@@ -88,7 +89,7 @@ public:
const FilterExpression&,
GlyphStore&,
Sprite&);
void placeFeatures() override;
void placeFeatures(CollisionTile&) override;
private:
void addFeature(const std::vector<std::vector<Coordinate>> &lines,
@@ -97,22 +98,28 @@ private:
bool anchorIsTooClose(const std::u32string &text, const float repeatDistance, Anchor &anchor);
std::map<std::u32string, std::vector<Anchor>> compareText;
void addToDebugBuffers();
void addToDebugBuffers(CollisionTile &collisionTile);
void placeFeatures(bool swapImmediately);
void placeFeatures(CollisionTile& collisionTile, bool swapImmediately);
void swapRenderData() override;
// Adds placed items to the buffer.
template <typename Buffer, typename GroupType>
void addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale, const bool keepUpright, const bool alongLine);
void addSymbols(Buffer &buffer, const SymbolQuads &symbols, float scale,
const bool keepUpright, const bool alongLine, const float placementAngle);
public:
StyleLayoutSymbol layout;
bool sdfIcons = false;
private:
CollisionTile &collision;
const float overscaling;
const float zoom;
const float tileSize;
const float tileExtent = 4096.0f;
const float tilePixelRatio;
std::vector<SymbolInstance> symbolInstances;
std::vector<SymbolFeature> features;

View File

@@ -14,6 +14,8 @@ uniform float u_minfadezoom;
uniform float u_maxfadezoom;
uniform float u_fadezoom;
uniform float u_opacity;
uniform bool u_skewed;
uniform float u_extra;
uniform vec2 u_texsize;
@@ -51,7 +53,15 @@ void main() {
// if label has been faded out, clip it
z += step(v_alpha, 0.0);
gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset / 64.0, z, 0);
if (u_skewed) {
vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, 0, 0);
gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1);
gl_Position.z += z * gl_Position.w;
} else {
vec4 extrude = u_exmatrix * vec4(a_offset / 64.0, z, 0);
gl_Position = u_matrix * vec4(a_pos, 0, 1) + extrude;
}
v_tex = a_tex / u_texsize;
v_alpha *= u_opacity;

View File

@@ -21,6 +21,8 @@ public:
Uniform<float> u_fadezoom = {"u_fadezoom", *this};
Uniform<float> u_opacity = {"u_opacity", *this};
Uniform<std::array<float, 2>> u_texsize = {"u_texsize", *this};
Uniform<int32_t> u_skewed = {"u_skewed", *this};
Uniform<float> u_extra = {"u_extra", *this};
private:
int32_t a_pos = -1;

View File

@@ -3,6 +3,7 @@ uniform vec4 u_color;
uniform float u_blur;
varying vec2 v_normal;
varying float v_gamma_scale;
void main() {
// Calculate the distance of the pixel from the line in pixels.
@@ -11,7 +12,8 @@ void main() {
// Calculate the antialiasing fade factor. This is either when fading in
// the line in case of an offset line (v_linewidth.t) or when fading out
// (v_linewidth.s)
float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0);
float blur = u_blur * v_gamma_scale;
float alpha = clamp(min(dist - (u_linewidth.t - blur), u_linewidth.s - dist) / blur, 0.0, 1.0);
gl_FragColor = u_color * alpha;
}

View File

@@ -9,17 +9,17 @@
attribute vec2 a_pos;
attribute vec4 a_data;
// matrix is for the vertex position, exmatrix is for rotating and projecting
// the extrusion vector.
uniform mat4 u_matrix;
uniform mat4 u_exmatrix;
// shared
uniform float u_ratio;
uniform vec2 u_linewidth;
uniform vec4 u_color;
uniform float u_extra;
uniform mat2 u_antialiasingmatrix;
varying vec2 v_normal;
varying float v_gamma_scale;
void main() {
vec2 a_extrude = a_data.xy;
@@ -34,11 +34,22 @@ void main() {
// Scale the extrusion vector down to a normal and then up by the line width
// of this vertex.
vec4 dist = vec4(u_linewidth.s * a_extrude * scale, 0.0, 0.0);
vec2 dist = u_linewidth.s * a_extrude * scale;
// Remove the texture normal bit of the position before scaling it with the
// model/view matrix. Add the extrusion vector *after* the model/view matrix
// because we're extruding the line in pixel space, regardless of the current
// tile's zoom level.
gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0.0, 1.0) + u_exmatrix * dist;
gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist / u_ratio, 0.0, 1.0);
// position of y on the screen
float y = gl_Position.y / gl_Position.w;
// how much features are squished in the y direction by the tilt
float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude);
// how much features are squished in all directions by the perspectiveness
float perspective_scale = 1.0 / (1.0 - min(y * u_extra, 0.90));
v_gamma_scale = perspective_scale * squish_scale;
}

View File

@@ -18,6 +18,8 @@ public:
Uniform<std::array<float, 2>> u_linewidth = {"u_linewidth", *this};
Uniform<float> u_ratio = {"u_ratio", *this};
Uniform<float> u_blur = {"u_blur", *this};
Uniform<float> u_extra = {"u_extra", *this};
UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this};
private:
int32_t a_pos = -1;

View File

@@ -1,5 +1,4 @@
uniform vec2 u_linewidth;
uniform float u_point;
uniform float u_blur;
uniform vec2 u_pattern_size_a;
@@ -15,17 +14,17 @@ uniform sampler2D u_image;
varying vec2 v_normal;
varying float v_linesofar;
varying float v_gamma_scale;
void main() {
// Calculate the distance of the pixel from the line in pixels.
float dist = length(v_normal) * (1.0 - u_point) + u_point * length(gl_PointCoord * 2.0 - 1.0);
dist *= u_linewidth.s;
float dist = length(v_normal) * u_linewidth.s;
// Calculate the antialiasing fade factor. This is either when fading in
// the line in case of an offset line (v_linewidth.t) or when fading out
// (v_linewidth.s)
float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0);
float blur = u_blur * v_gamma_scale;
float alpha = clamp(min(dist - (u_linewidth.t - blur), u_linewidth.s - dist) / blur, 0.0, 1.0);
float x_a = mod(v_linesofar / u_pattern_size_a.x, 1.0);
float y_a = 0.5 + (v_normal.y * u_linewidth.s / u_pattern_size_a.y);

View File

@@ -18,10 +18,13 @@ uniform mat4 u_exmatrix;
uniform float u_ratio;
uniform vec2 u_linewidth;
uniform vec4 u_color;
uniform float u_point;
uniform float u_extra;
uniform mat2 u_antialiasingmatrix;
varying vec2 v_normal;
varying float v_linesofar;
varying float v_gamma_scale;
void main() {
vec2 a_extrude = a_data.xy;
@@ -37,24 +40,23 @@ void main() {
// Scale the extrusion vector down to a normal and then up by the line width
// of this vertex.
vec2 extrude = a_extrude * scale;
vec2 dist = u_linewidth.s * extrude * (1.0 - u_point);
// If the x coordinate is the maximum integer, we move the z coordinates out
// of the view plane so that the triangle gets clipped. This makes it easier
// for us to create degenerate triangle strips.
float z = step(32767.0, a_pos.x);
// When drawing points, skip every other vertex
z += u_point * step(1.0, v_normal.y);
vec2 dist = u_linewidth.s * a_extrude * scale;
// Remove the texture normal bit of the position before scaling it with the
// model/view matrix. Add the extrusion vector *after* the model/view matrix
// because we're extruding the line in pixel space, regardless of the current
// tile's zoom level.
gl_Position = u_matrix * vec4(floor(a_pos / 2.0), 0.0, 1.0) + u_exmatrix * vec4(dist, z, 0.0);
v_linesofar = a_linesofar;// * u_ratio;
gl_Position = u_matrix * vec4(floor(a_pos / 2.0) + dist.xy / u_ratio, 0.0, 1.0);
v_linesofar = a_linesofar;
// position of y on the screen
float y = gl_Position.y / gl_Position.w;
gl_PointSize = 2.0 * u_linewidth.s - 1.0;
// how much features are squished in the y direction by the tilt
float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude);
// how much features are squished in all directions by the perspectiveness
float perspective_scale = 1.0 / (1.0 - y * u_extra);
v_gamma_scale = perspective_scale * squish_scale;
}

View File

@@ -26,6 +26,8 @@ public:
Uniform<float> u_blur = {"u_blur", *this};
Uniform<float> u_fade = {"u_fade", *this};
Uniform<float> u_opacity = {"u_opacity", *this};
Uniform<float> u_extra = {"u_extra", *this};
UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this};
private:
int32_t a_pos = -1;

View File

@@ -8,6 +8,7 @@ uniform float u_mix;
varying vec2 v_normal;
varying vec2 v_tex_a;
varying vec2 v_tex_b;
varying float v_gamma_scale;
void main() {
// Calculate the distance of the pixel from the line in pixels.
@@ -16,7 +17,8 @@ void main() {
// Calculate the antialiasing fade factor. This is either when fading in
// the line in case of an offset line (v_linewidth.t) or when fading out
// (v_linewidth.s)
float alpha = clamp(min(dist - (u_linewidth.t - u_blur), u_linewidth.s - dist) / u_blur, 0.0, 1.0);
float blur = u_blur * v_gamma_scale;
float alpha = clamp(min(dist - (u_linewidth.t - blur), u_linewidth.s - dist) / blur, 0.0, 1.0);
float sdfdist_a = texture2D(u_image, v_tex_a).a;
float sdfdist_b = texture2D(u_image, v_tex_b).a;

View File

@@ -22,9 +22,13 @@ uniform float u_tex_y_a;
uniform vec2 u_patternscale_b;
uniform float u_tex_y_b;
uniform float u_extra;
uniform mat2 u_antialiasingmatrix;
varying vec2 v_normal;
varying vec2 v_tex_a;
varying vec2 v_tex_b;
varying float v_gamma_scale;
void main() {
vec2 a_extrude = a_data.xy;
@@ -40,14 +44,25 @@ void main() {
// Scale the extrusion vector down to a normal and then up by the line width
// of this vertex.
vec4 dist = vec4(u_linewidth.s * a_extrude * scale, 0.0, 0.0);
vec2 dist = u_linewidth.s * a_extrude * scale;
// Remove the texture normal bit of the position before scaling it with the
// model/view matrix. Add the extrusion vector *after* the model/view matrix
// because we're extruding the line in pixel space, regardless of the current
// tile's zoom level.
gl_Position = u_matrix * vec4(floor(a_pos * 0.5), 0.0, 1.0) + u_exmatrix * dist;
gl_Position = u_matrix * vec4(floor(a_pos * 0.5) + dist / u_ratio, 0.0, 1.0);
v_tex_a = vec2(a_linesofar * u_patternscale_a.x, normal.y * u_patternscale_a.y + u_tex_y_a);
v_tex_b = vec2(a_linesofar * u_patternscale_b.x, normal.y * u_patternscale_b.y + u_tex_y_b);
// position of y on the screen
float y = gl_Position.y / gl_Position.w;
// how much features are squished in the y direction by the tilt
float squish_scale = length(a_extrude) / length(u_antialiasingmatrix * a_extrude);
// how much features are squished in all directions by the perspectiveness
float perspective_scale = 1.0 / (1.0 - min(y * u_extra, 0.9));
v_gamma_scale = perspective_scale * squish_scale;
}

View File

@@ -25,6 +25,8 @@ public:
Uniform<int32_t> u_image = {"u_image", *this};
Uniform<float> u_sdfgamma = {"u_sdfgamma", *this};
Uniform<float> u_mix = {"u_mix", *this};
Uniform<float> u_extra = {"u_extra", *this};
UniformMatrix<2> u_antialiasingmatrix = {"u_antialiasingmatrix", *this};
private:
int32_t a_pos = -1;

View File

@@ -6,5 +6,5 @@ varying vec2 v_pos;
void main() {
gl_Position = u_matrix * vec4(a_pos, 0, 1);
v_pos = (gl_Position.xy + 1.0) / 2.0 * u_world;
v_pos = (gl_Position.xy / gl_Position.w + 1.0) / 2.0 * u_world;
}

View File

@@ -5,9 +5,11 @@ uniform float u_gamma;
varying vec2 v_tex;
varying float v_alpha;
varying float v_gamma_scale;
void main() {
float dist = texture2D(u_texture, v_tex).a;
float alpha = smoothstep(u_buffer - u_gamma, u_buffer + u_gamma, dist) * v_alpha;
float gamma = u_gamma * v_gamma_scale;
float alpha = smoothstep(u_buffer - gamma, u_buffer + gamma, dist) * v_alpha;
gl_FragColor = u_color * alpha;
}

View File

@@ -13,11 +13,14 @@ uniform float u_fadedist;
uniform float u_minfadezoom;
uniform float u_maxfadezoom;
uniform float u_fadezoom;
uniform bool u_skewed;
uniform float u_extra;
uniform vec2 u_texsize;
varying vec2 v_tex;
varying float v_alpha;
varying float v_gamma_scale;
void main() {
vec2 a_tex = a_data1.xy;
@@ -48,6 +51,19 @@ void main() {
// if label has been faded out, clip it
show *= (1.0 - step(v_alpha, 0.0));
gl_Position = u_matrix * vec4(a_pos, 0, 1) + u_exmatrix * vec4(a_offset * show / 64.0, 0, 0);
if (u_skewed) {
vec4 extrude = u_exmatrix * vec4(a_offset * show / 64.0, 0, 0);
gl_Position = u_matrix * vec4(a_pos + extrude.xy, 0, 1);
} else {
vec4 extrude = u_exmatrix * vec4(a_offset * show / 64.0, 0, 0);
gl_Position = u_matrix * vec4(a_pos, 0, 1) + extrude;
}
// position of y on the screen
float y = gl_Position.y / gl_Position.w;
// how much features are squished in all directions by the perspectiveness
float perspective_scale = 1.0 / (1.0 - y * u_extra);
v_gamma_scale = perspective_scale;
v_tex = a_tex / u_texsize;
}

View File

@@ -23,6 +23,8 @@ public:
Uniform<float> u_minfadezoom = {"u_minfadezoom", *this};
Uniform<float> u_maxfadezoom = {"u_maxfadezoom", *this};
Uniform<float> u_fadezoom = {"u_fadezoom", *this};
Uniform<int32_t> u_skewed = {"u_skewed", *this};
Uniform<float> u_extra = {"u_extra", *this};
protected:
int32_t a_pos = -1;

View File

@@ -36,10 +36,16 @@ public:
location = MBGL_CHECK_ERROR(glGetUniformLocation(shader.program, name));
}
void operator=(const T& t) {
if (current != t) {
current = t;
bind(t);
void operator=(const std::array<double, C*R>& t) {
bool dirty = false;
for (unsigned int i = 0; i < C*R; i++) {
if (current[i] != t[i]) {
current[i] = t[i];
dirty = true;
}
}
if (dirty) {
bind(current);
}
}

View File

@@ -3,17 +3,17 @@
namespace mbgl {
void CollisionTile::reset(const float _angle, const float pitch) {
CollisionTile::CollisionTile(const float angle_, const float pitch, bool debug_) :
angle(angle_), debug(debug_) {
tree.clear();
angle = _angle;
// Compute the transformation matrix.
float angle_sin = std::sin(_angle);
float angle_cos = std::cos(_angle);
float angle_sin = std::sin(angle);
float angle_cos = std::cos(angle);
rotationMatrix = {{angle_cos, -angle_sin, angle_sin, angle_cos}};
// Stretch boxes in y direction to account for the map tilt.
const float _yStretch = 1.0f / std::cos(pitch / 180 * M_PI);
const float _yStretch = 1.0f / std::cos(pitch);
// The amount the map is squished depends on the y position.
// Sort of account for this by making all boxes a bit bigger.

View File

@@ -35,22 +35,18 @@ namespace mbgl {
class CollisionTile {
public:
inline explicit CollisionTile(float _zoom, float tileExtent, float tileSize, float angle_, bool debug_) :
zoom(_zoom), tilePixelRatio(tileExtent / tileSize), debug(debug_) { reset(angle_, 0); }
explicit CollisionTile(float angle_, float pitch_, bool debug_);
void reset(const float angle, const float pitch);
float placeFeature(const CollisionFeature &feature);
void insertFeature(CollisionFeature &feature, const float minPlacementScale);
void setDebug(bool debug_) { debug = debug_; }
bool getDebug() { return debug; }
const float zoom;
const float tilePixelRatio;
float angle = 0;
const float angle = 0;
const float minScale = 0.5f;
const float maxScale = 2.0f;
float yStretch;
private:
@@ -58,7 +54,6 @@ class CollisionTile {
Tree tree;
std::array<float, 4> rotationMatrix;
float yStretch;
bool debug;
};

View File

@@ -50,12 +50,12 @@ SymbolQuads getIconQuads(Anchor &anchor, const PositionedIcon &shapedIcon,
}
struct GlyphInstance {
explicit GlyphInstance(const vec2<float> &anchor_) : anchor(anchor_) {}
explicit GlyphInstance(const vec2<float> &anchor_, float offset_, float minScale_, float maxScale_,
explicit GlyphInstance(const vec2<float> &anchorPoint_) : anchorPoint(anchorPoint_) {}
explicit GlyphInstance(const vec2<float> &anchorPoint_, float offset_, float minScale_, float maxScale_,
float angle_)
: anchor(anchor_), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {}
: anchorPoint(anchorPoint_), offset(offset_), minScale(minScale_), maxScale(maxScale_), angle(angle_) {}
const vec2<float> anchor;
const vec2<float> anchorPoint;
const float offset = 0.0f;
const float minScale = globalMinScale;
const float maxScale = std::numeric_limits<float>::infinity();
@@ -65,19 +65,19 @@ struct GlyphInstance {
typedef std::vector<GlyphInstance> GlyphInstances;
void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &anchor,
float offset, const std::vector<Coordinate> &line, int segment, int8_t direction) {
float offset, const std::vector<Coordinate> &line, int segment, bool forward) {
const bool upsideDown = direction < 0;
const bool upsideDown = !forward;
if (offset < 0)
direction *= -1;
forward = !forward;
if (direction > 0)
if (forward)
segment++;
assert((int)line.size() > segment);
vec2<float> end = line[segment];
vec2<float> newAnchor = anchor;
vec2<float> newAnchorPoint = anchor;
float prevscale = std::numeric_limits<float>::infinity();
offset = std::fabs(offset);
@@ -85,16 +85,16 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &
const float placementScale = anchor.scale;
while (true) {
const float dist = util::dist<float>(newAnchor, end);
const float dist = util::dist<float>(newAnchorPoint, end);
const float scale = offset / dist;
float angle = std::atan2(end.y - newAnchor.y, end.x - newAnchor.x);
if (direction < 0)
float angle = std::atan2(end.y - newAnchorPoint.y, end.x - newAnchorPoint.x);
if (!forward)
angle += M_PI;
if (upsideDown)
angle += M_PI;
glyphs = GlyphInstance{
/* anchor */ newAnchor,
/* anchor */ newAnchorPoint,
/* offset */ static_cast<float>(upsideDown ? M_PI : 0.0),
/* minScale */ scale,
/* maxScale */ prevscale,
@@ -103,11 +103,11 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &
if (scale <= placementScale)
break;
newAnchor = end;
newAnchorPoint = end;
// skip duplicate nodes
while (newAnchor == end) {
segment += direction;
while (newAnchorPoint == end) {
segment += forward ? 1 : -1;
if ((int)line.size() <= segment || segment < 0) {
anchor.scale = scale;
return;
@@ -115,8 +115,8 @@ void getSegmentGlyphs(std::back_insert_iterator<GlyphInstances> glyphs, Anchor &
end = line[segment];
}
vec2<float> normal = util::normal<float>(newAnchor, end) * dist;
newAnchor = newAnchor - normal;
vec2<float> normal = util::normal<float>(newAnchorPoint, end) * dist;
newAnchorPoint = newAnchorPoint - normal;
prevscale = scale;
}
@@ -148,9 +148,9 @@ SymbolQuads getGlyphQuads(Anchor &anchor, const Shaping &shapedText,
GlyphInstances glyphInstances;
if (alongLine) {
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, 1);
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, true);
if (keepUpright)
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, -1);
getSegmentGlyphs(std::back_inserter(glyphInstances), anchor, centerX, line, anchor.segment, false);
} else {
glyphInstances.emplace_back(GlyphInstance{anchor});
@@ -194,7 +194,7 @@ SymbolQuads getGlyphQuads(Anchor &anchor, const Shaping &shapedText,
const float glyphMinScale = std::max(instance.minScale, anchor.scale);
const float glyphAngle = std::fmod((anchor.angle + textRotate + instance.offset + 2 * M_PI), (2 * M_PI));
quads.emplace_back(tl, tr, bl, br, rect, glyphAngle, instance.anchor, glyphMinScale, instance.maxScale);
quads.emplace_back(tl, tr, bl, br, rect, glyphAngle, instance.anchorPoint, glyphMinScale, instance.maxScale);
}

View File

@@ -11,7 +11,7 @@ namespace mbgl {
struct SymbolQuad {
explicit SymbolQuad(const vec2<float> &tl_, const vec2<float> &tr_,
const vec2<float> &bl_, const vec2<float> &br_,
const Rect<uint16_t> &tex_, float angle_, const vec2<float> &anchor_,
const Rect<uint16_t> &tex_, float angle_, const vec2<float> &anchorPoint_,
float minScale_, float maxScale_)
: tl(tl_),
tr(tr_),
@@ -19,14 +19,14 @@ namespace mbgl {
br(br_),
tex(tex_),
angle(angle_),
anchor(anchor_),
anchorPoint(anchorPoint_),
minScale(minScale_),
maxScale(maxScale_) {}
vec2<float> tl, tr, bl, br;
Rect<uint16_t> tex;
float angle;
vec2<float> anchor;
vec2<float> anchorPoint;
float minScale, maxScale;
};

View File

@@ -1,13 +1,14 @@
#ifndef MBGL_UTIL_BOX
#define MBGL_UTIL_BOX
#include <mbgl/util/vec.hpp>
#include <mbgl/util/tile_coordinate.hpp>
namespace mbgl {
struct box {
vec2<double> tl, tr, bl, br;
vec2<double> center;
box(TileCoordinate tl_, TileCoordinate tr_, TileCoordinate br_, TileCoordinate bl_) :
tl(tl_), tr(tr_), br(br_), bl(bl_) {}
TileCoordinate tl, tr, br, bl;
};
}

52
src/mbgl/util/mat2.cpp Normal file
View File

@@ -0,0 +1,52 @@
// This is an incomplete port of http://glmatrix.net/
//
// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the
// use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not claim
// that you wrote the original software. If you use this software in a
// product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
#include <mbgl/util/mat2.hpp>
#include <cmath>
using namespace mbgl;
void matrix::identity(mat2& out) {
out[0] = 1.0f;
out[1] = 0.0f;
out[2] = 0.0f;
out[3] = 1.0f;
}
void matrix::rotate(mat2& out, const mat2& a, double rad) {
double a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3],
s = std::sin(rad),
c = std::cos(rad);
out[0] = a0 * c + a2 * s;
out[1] = a1 * c + a3 * s;
out[2] = a0 * -s + a2 * c;
out[3] = a1 * -s + a3 * c;
};
void matrix::scale(mat2& out, const mat2& a, double v0, double v1) {
double a0 = a[0], a1 = a[1], a2 = a[2], a3 = a[3];
out[0] = a0 * v0;
out[1] = a1 * v0;
out[2] = a2 * v1;
out[3] = a3 * v1;
}

41
src/mbgl/util/mat2.hpp Normal file
View File

@@ -0,0 +1,41 @@
// This is an incomplete port of http://glmatrix.net/
//
// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the
// use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not claim
// that you wrote the original software. If you use this software in a
// product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
#ifndef MBGL_UTIL_MAT2
#define MBGL_UTIL_MAT2
#include <array>
namespace mbgl {
typedef std::array<double, 4> mat2;
namespace matrix {
void identity(mat2& out);
void rotate(mat2& out, const mat2& a, double rad);
void scale(mat2& out, const mat2& a, double v0, double v1);
}
}
#endif

View File

@@ -38,8 +38,8 @@ void matrix::identity(mat3& out) {
out[8] = 1.0f;
}
void matrix::translate(mat3& out, const mat3& a, float x, float y) {
float a00 = a[0], a01 = a[1], a02 = a[2],
void matrix::translate(mat3& out, const mat3& a, double x, double y) {
double a00 = a[0], a01 = a[1], a02 = a[2],
a10 = a[3], a11 = a[4], a12 = a[5],
a20 = a[6], a21 = a[7], a22 = a[8];
@@ -56,8 +56,8 @@ void matrix::translate(mat3& out, const mat3& a, float x, float y) {
out[8] = x * a02 + y * a12 + a22;
}
void matrix::rotate(mat3& out, const mat3& a, float rad) {
float s = std::sin(rad),
void matrix::rotate(mat3& out, const mat3& a, double rad) {
double s = std::sin(rad),
c = std::cos(rad),
a00 = a[0],
a01 = a[1],
@@ -82,7 +82,7 @@ void matrix::rotate(mat3& out, const mat3& a, float rad) {
out[8] = a22;
};
void matrix::scale(mat3& out, const mat3& a, float x, float y) {
void matrix::scale(mat3& out, const mat3& a, double x, double y) {
out[0] = x * a[0];
out[1] = x * a[1];
out[2] = x * a[2];

View File

@@ -27,14 +27,14 @@
namespace mbgl {
typedef std::array<float, 9> mat3;
typedef std::array<double, 9> mat3;
namespace matrix {
void identity(mat3& out);
void translate(mat3& out, const mat3& a, float x, float y);
void rotate(mat3& out, const mat3& a, float rad);
void scale(mat3& out, const mat3& a, float x, float y);
void translate(mat3& out, const mat3& a, double x, double y);
void rotate(mat3& out, const mat3& a, double rad);
void scale(mat3& out, const mat3& a, double x, double y);
}
}

View File

@@ -45,8 +45,55 @@ void matrix::identity(mat4& out) {
out[15] = 1.0f;
}
void matrix::ortho(mat4& out, float left, float right, float bottom, float top, float near, float far) {
float lr = 1.0f / (left - right),
bool matrix::invert(mat4& out, mat4& a) {
float a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15],
b00 = a00 * a11 - a01 * a10,
b01 = a00 * a12 - a02 * a10,
b02 = a00 * a13 - a03 * a10,
b03 = a01 * a12 - a02 * a11,
b04 = a01 * a13 - a03 * a11,
b05 = a02 * a13 - a03 * a12,
b06 = a20 * a31 - a21 * a30,
b07 = a20 * a32 - a22 * a30,
b08 = a20 * a33 - a23 * a30,
b09 = a21 * a32 - a22 * a31,
b10 = a21 * a33 - a23 * a31,
b11 = a22 * a33 - a23 * a32,
// Calculate the determinant
det = b00 * b11 - b01 * b10 + b02 * b09 + b03 * b08 - b04 * b07 + b05 * b06;
if (!det) {
return true;
}
det = 1.0 / det;
out[0] = (a11 * b11 - a12 * b10 + a13 * b09) * det;
out[1] = (a02 * b10 - a01 * b11 - a03 * b09) * det;
out[2] = (a31 * b05 - a32 * b04 + a33 * b03) * det;
out[3] = (a22 * b04 - a21 * b05 - a23 * b03) * det;
out[4] = (a12 * b08 - a10 * b11 - a13 * b07) * det;
out[5] = (a00 * b11 - a02 * b08 + a03 * b07) * det;
out[6] = (a32 * b02 - a30 * b05 - a33 * b01) * det;
out[7] = (a20 * b05 - a22 * b02 + a23 * b01) * det;
out[8] = (a10 * b10 - a11 * b08 + a13 * b06) * det;
out[9] = (a01 * b08 - a00 * b10 - a03 * b06) * det;
out[10] = (a30 * b04 - a31 * b02 + a33 * b00) * det;
out[11] = (a21 * b02 - a20 * b04 - a23 * b00) * det;
out[12] = (a11 * b07 - a10 * b09 - a12 * b06) * det;
out[13] = (a00 * b09 - a01 * b07 + a02 * b06) * det;
out[14] = (a31 * b01 - a30 * b03 - a32 * b00) * det;
out[15] = (a20 * b03 - a21 * b01 + a22 * b00) * det;
return false;
}
void matrix::ortho(mat4& out, double left, double right, double bottom, double top, double near, double far) {
double lr = 1.0f / (left - right),
bt = 1.0f / (bottom - top),
nf = 1.0f / (near - far);
out[0] = -2.0f * lr;
@@ -67,6 +114,27 @@ void matrix::ortho(mat4& out, float left, float right, float bottom, float top,
out[15] = 1.0f;
}
void matrix::perspective(mat4& out, double fovy, double aspect, double near, double far) {
double f = 1.0f / std::tan(fovy / 2.0f),
nf = 1.0f / (near - far);
out[0] = f / aspect;
out[1] = 0.0f;
out[2] = 0.0f;
out[3] = 0.0f;
out[4] = 0.0f;
out[5] = f;
out[6] = 0.0f;
out[7] = 0.0f;
out[8] = 0.0f;
out[9] = 0.0f;
out[10] = (far + near) * nf;
out[11] = -1.0f;
out[12] = 0.0f;
out[13] = 0.0f;
out[14] = (2.0f * far * near) * nf;
out[15] = 0.0f;
}
void matrix::copy(mat4& out, const mat4& a) {
out[0] = a[0];
out[1] = a[1];
@@ -86,14 +154,14 @@ void matrix::copy(mat4& out, const mat4& a) {
out[15] = a[15];
}
void matrix::translate(mat4& out, const mat4& a, float x, float y, float z) {
void matrix::translate(mat4& out, const mat4& a, double x, double y, double z) {
if (&a == &out) {
out[12] = a[0] * x + a[4] * y + a[8] * z + a[12];
out[13] = a[1] * x + a[5] * y + a[9] * z + a[13];
out[14] = a[2] * x + a[6] * y + a[10] * z + a[14];
out[15] = a[3] * x + a[7] * y + a[11] * z + a[15];
} else {
float a00, a01, a02, a03,
double a00, a01, a02, a03,
a10, a11, a12, a13,
a20, a21, a22, a23;
@@ -112,8 +180,42 @@ void matrix::translate(mat4& out, const mat4& a, float x, float y, float z) {
}
}
void matrix::rotate_z(mat4& out, const mat4& a, float rad) {
float s = std::sin(rad),
void matrix::rotate_x(mat4& out, const mat4& a, double rad) {
double s = std::sin(rad),
c = std::cos(rad),
a10 = a[4],
a11 = a[5],
a12 = a[6],
a13 = a[7],
a20 = a[8],
a21 = a[9],
a22 = a[10],
a23 = a[11];
if (&a != &out) { // If the source and destination differ, copy the unchanged rows
out[0] = a[0];
out[1] = a[1];
out[2] = a[2];
out[3] = a[3];
out[12] = a[12];
out[13] = a[13];
out[14] = a[14];
out[15] = a[15];
}
// Perform axis-specific matrix multiplication
out[4] = a10 * c + a20 * s;
out[5] = a11 * c + a21 * s;
out[6] = a12 * c + a22 * s;
out[7] = a13 * c + a23 * s;
out[8] = a20 * c - a10 * s;
out[9] = a21 * c - a11 * s;
out[10] = a22 * c - a12 * s;
out[11] = a23 * c - a13 * s;
}
void matrix::rotate_z(mat4& out, const mat4& a, double rad) {
double s = std::sin(rad),
c = std::cos(rad),
a00 = a[0],
a01 = a[1],
@@ -146,7 +248,7 @@ void matrix::rotate_z(mat4& out, const mat4& a, float rad) {
out[7] = a13 * c - a03 * s;
}
void matrix::scale(mat4& out, const mat4& a, float x, float y, float z) {
void matrix::scale(mat4& out, const mat4& a, double x, double y, double z) {
out[0] = a[0] * x;
out[1] = a[1] * x;
out[2] = a[2] * x;
@@ -166,13 +268,13 @@ void matrix::scale(mat4& out, const mat4& a, float x, float y, float z) {
}
void matrix::multiply(mat4& out, const mat4& a, const mat4& b) {
float a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
double a00 = a[0], a01 = a[1], a02 = a[2], a03 = a[3],
a10 = a[4], a11 = a[5], a12 = a[6], a13 = a[7],
a20 = a[8], a21 = a[9], a22 = a[10], a23 = a[11],
a30 = a[12], a31 = a[13], a32 = a[14], a33 = a[15];
// Cache only the current line of the second matrix
float b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
double b0 = b[0], b1 = b[1], b2 = b[2], b3 = b[3];
out[0] = b0 * a00 + b1 * a10 + b2 * a20 + b3 * a30;
out[1] = b0 * a01 + b1 * a11 + b2 * a21 + b3 * a31;
out[2] = b0 * a02 + b1 * a12 + b2 * a22 + b3 * a32;

View File

@@ -0,0 +1,29 @@
#ifndef MBGL_UTIL_TILE_COORDINATE
#define MBGL_UTIL_TILE_COORDINATE
#include <mbgl/util/vec.hpp>
namespace mbgl {
struct TileCoordinate {
float column;
float row;
float zoom;
TileCoordinate(float column_, float row_, float zoom_) :
column(column_), row(row_), zoom(zoom_) {}
TileCoordinate zoomTo(float targetZoom) {
float scale = std::pow(2, targetZoom - zoom);
return { column * scale, row * scale, targetZoom };
}
TileCoordinate operator-(TileCoordinate c) {
c = c.zoomTo(zoom);
return { column - c.column, row - c.row, zoom };
};
};
}
#endif

View File

@@ -1,6 +1,7 @@
#include <mbgl/util/tile_cover.hpp>
#include <mbgl/util/vec.hpp>
#include <mbgl/util/box.hpp>
#include <mbgl/util/tile_coordinate.hpp>
namespace mbgl {
@@ -78,13 +79,19 @@ std::forward_list<TileID> tileCover(int8_t z, const mbgl::box &bounds, int8_t ac
}
};
mbgl::vec2<double> tl = { bounds.tl.column, bounds.tl.row };
mbgl::vec2<double> tr = { bounds.tr.column, bounds.tr.row };
mbgl::vec2<double> br = { bounds.br.column, bounds.br.row };
mbgl::vec2<double> bl = { bounds.bl.column, bounds.bl.row };
// Divide the screen up in two triangles and scan each of them:
// \---+
// | \ |
// +---\.
scanTriangle(bounds.tl, bounds.tr, bounds.br, 0, tiles, scanLine);
scanTriangle(bounds.br, bounds.bl, bounds.tl, 0, tiles, scanLine);
scanTriangle(tl, tr, br, 0, tiles, scanLine);
scanTriangle(br, bl, tl, 0, tiles, scanLine);
t.sort();
t.unique();
return t;

33
src/mbgl/util/vec4.cpp Normal file
View File

@@ -0,0 +1,33 @@
// This is an incomplete port of http://glmatrix.net/
//
// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the
// use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not claim
// that you wrote the original software. If you use this software in a
// product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
#include <mbgl/util/vec4.hpp>
using namespace mbgl;
void matrix::transformMat4(vec4& out, vec4& a, mat4& m) {
float x = a[0], y = a[1], z = a[2], w = a[3];
out[0] = m[0] * x + m[4] * y + m[8] * z + m[12] * w;
out[1] = m[1] * x + m[5] * y + m[9] * z + m[13] * w;
out[2] = m[2] * x + m[6] * y + m[10] * z + m[14] * w;
out[3] = m[3] * x + m[7] * y + m[11] * z + m[15] * w;
}

40
src/mbgl/util/vec4.hpp Normal file
View File

@@ -0,0 +1,40 @@
// This is an incomplete port of http://glmatrix.net/
//
// Copyright (c) 2013 Brandon Jones, Colin MacKenzie IV
//
// This software is provided 'as-is', without any express or implied warranty.
// In no event will the authors be held liable for any damages arising from the
// use of this software.
//
// Permission is granted to anyone to use this software for any purpose,
// including commercial applications, and to alter it and redistribute it
// freely, subject to the following restrictions:
//
// 1. The origin of this software must not be misrepresented; you must not claim
// that you wrote the original software. If you use this software in a
// product, an acknowledgment in the product documentation would be
// appreciated but is not required.
//
// 2. Altered source versions must be plainly marked as such, and must not be
// misrepresented as being the original software.
//
// 3. This notice may not be removed or altered from any source distribution.
#ifndef MBGL_UTIL_VEC4
#define MBGL_UTIL_VEC4
#include <array>
#include <mbgl/util/mat4.hpp>
namespace mbgl {
namespace matrix {
typedef std::array<double, 4> vec4;
void transformMat4(vec4& out, vec4& a, mat4& m);
}
}
#endif

View File

@@ -46,8 +46,8 @@ public:
}
}
void redoPlacement(TileWorker* worker, float angle, bool collisionDebug, std::function<void ()> callback) {
worker->redoPlacement(angle, collisionDebug);
void redoPlacement(TileWorker* worker, float angle, float pitch, bool collisionDebug, std::function<void ()> callback) {
worker->redoPlacement(angle, pitch, collisionDebug);
callback();
}
};
@@ -76,9 +76,9 @@ std::unique_ptr<WorkRequest> Worker::parseLiveTile(TileWorker& worker, const Liv
return threads[current]->invokeWithCallback(&Worker::Impl::parseLiveTile, callback, &worker, &tile);
}
std::unique_ptr<WorkRequest> Worker::redoPlacement(TileWorker& worker, float angle, bool collisionDebug, std::function<void ()> callback) {
std::unique_ptr<WorkRequest> Worker::redoPlacement(TileWorker& worker, float angle, float pitch, bool collisionDebug, std::function<void ()> callback) {
current = (current + 1) % threads.size();
return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker, angle, collisionDebug);
return threads[current]->invokeWithCallback(&Worker::Impl::redoPlacement, callback, &worker, angle, pitch, collisionDebug);
}
} // end namespace mbgl

View File

@@ -49,6 +49,7 @@ public:
Request redoPlacement(
TileWorker&,
float angle,
float pitch,
bool collisionDebug,
std::function<void ()> callback);

View File

@@ -544,7 +544,7 @@
- (void)testUserTrackingModeFollow {
tester.mapView.userTrackingMode = MGLUserTrackingModeFollow;
[tester waitForTimeInterval:1];
[tester acknowledgeSystemAlert];
XCTAssertEqual(tester.mapView.userLocationVisible,
YES,
@@ -570,7 +570,7 @@
- (void)testUserTrackingModeFollowWithHeading {
tester.mapView.userTrackingMode = MGLUserTrackingModeFollowWithHeading;
[tester waitForTimeInterval:1];
[tester acknowledgeSystemAlert];
XCTAssertEqual(tester.mapView.userLocationVisible,
YES,

View File

@@ -119,7 +119,7 @@ const NSTimeInterval MGLFlushInterval = 60;
[[MGLMapboxEvents sharedManager] flush];
[self waitForExpectationsWithTimeout:1.0 handler:nil];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
XCTAssertNil([[MGLMapboxEvents sharedManager] timer]);
}
@@ -143,7 +143,7 @@ const NSTimeInterval MGLFlushInterval = 60;
id eventsMock = [OCMockObject partialMockForObject:[MGLMapboxEvents sharedManager]];
[[[eventsMock expect] andForwardToRealObject] startTimer];
[self pushFakeEvent];
[eventsMock verifyWithDelay:0.5];
[eventsMock verifyWithDelay:2.0];
XCTAssertEqual([[[MGLMapboxEvents sharedManager] eventQueue] count], 1);
XCTAssertNotNil([[MGLMapboxEvents sharedManager] timer]);
@@ -164,7 +164,7 @@ const NSTimeInterval MGLFlushInterval = 60;
[[MGLMapboxEvents sharedManager] flush];
[self waitForExpectationsWithTimeout:1.0 handler:nil];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
[self pushFakeEvent];
id eventsMock = [OCMockObject partialMockForObject:[MGLMapboxEvents sharedManager]];
@@ -178,7 +178,7 @@ const NSTimeInterval MGLFlushInterval = 60;
id eventsMock = [OCMockObject partialMockForObject:[MGLMapboxEvents sharedManager]];
[[eventsMock expect] postEvents:events];
[[MGLMapboxEvents sharedManager] flush];
[eventsMock verifyWithDelay:1.0];
[eventsMock verifyWithDelay:2.0];
XCTAssertTrue([[[MGLMapboxEvents sharedManager] eventQueue] count] == 0);
}
@@ -209,7 +209,7 @@ const NSTimeInterval MGLFlushInterval = 60;
[self pushFakeEvent];
[MGLMapboxEvents flush];
[self waitForExpectationsWithTimeout:1.0 handler:nil];
[self waitForExpectationsWithTimeout:2.0 handler:nil];
}
- (void)testPushEventAddsToEventQueue {

View File

@@ -37,10 +37,10 @@
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Release"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
buildConfiguration = "Release">
shouldUseLaunchSchemeArgsEnv = "YES">
<Testables>
<TestableReference
skipped = "NO">
@@ -58,6 +58,9 @@
<Test
Identifier = "MapViewTests/testUserTrackingModeFollowWithHeading">
</Test>
<Test
Identifier = "MetricsTests">
</Test>
<Test
Identifier = "MetricsTests/testFlushPostsEvents">
</Test>
@@ -79,15 +82,18 @@
ReferencedContainer = "container:ios-tests.xcodeproj">
</BuildableReference>
</MacroExpansion>
<AdditionalOptions>
</AdditionalOptions>
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
launchStyle = "0"
useCustomWorkingDirectory = "NO"
buildConfiguration = "Debug"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">
@@ -103,10 +109,10 @@
</AdditionalOptions>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
buildConfiguration = "Release"
debugDocumentVersioning = "YES">
<BuildableProductRunnable
runnableDebuggingMode = "0">

View File

@@ -105,3 +105,35 @@ TEST(Transform, InvalidBearing) {
ASSERT_DOUBLE_EQ(2, transform.getScale());
ASSERT_DOUBLE_EQ(2, transform.getAngle());
}
TEST(Transform, PerspectiveProjection) {
MockView view;
Transform transform(view);
transform.resize({{ 1000, 1000 }});
transform.setScale(1024);
transform.setPitch(0.9);
transform.setLatLng(LatLng(38, -77));
// expected values are from mapbox-gl-js
LatLng loc = transform.getState().pointToLatLng({ 500, 500 });
ASSERT_NEAR(-77, loc.longitude, 0.0001);
ASSERT_NEAR(38, loc.latitude, 0.0001);
loc = transform.getState().pointToLatLng({ 0, 1000 });
ASSERT_NEAR(-77.59198961199148, loc.longitude, 0.0002);
ASSERT_NEAR(38.74661326302018, loc.latitude, 0.0001);
loc = transform.getState().pointToLatLng({ 1000, 0 });
ASSERT_NEAR(-76.75823239205641, loc.longitude, 0.0001);
ASSERT_NEAR(37.692872969426375, loc.latitude, 0.0001);
vec2<double> point = transform.getState().latLngToPoint({38.74661326302018, -77.59198961199148});
ASSERT_NEAR(point.x, 0, 0.01);
ASSERT_NEAR(point.y, 1000, 0.01);
point = transform.getState().latLngToPoint({37.692872969426375, -76.75823239205641});
ASSERT_NEAR(point.x, 1000, 0.02);
ASSERT_NEAR(point.y, 0, 0.02);
}