Merge branch 'master' into node
This commit is contained in:
@@ -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;
|
||||
|
||||
@@ -65,9 +65,8 @@ android {
|
||||
}
|
||||
|
||||
release {
|
||||
minifyEnabled false
|
||||
jniDebuggable false
|
||||
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.txt'
|
||||
consumerProguardFiles 'proguard-rules.pro'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
24
android/java/MapboxGLAndroidSDK/proguard-rules.pro
vendored
Normal file
24
android/java/MapboxGLAndroidSDK/proguard-rules.pro
vendored
Normal 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** {
|
||||
*;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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 |
@@ -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'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
1
android/java/MapboxGLAndroidSDKTestApp/proguard-rules.pro
vendored
Normal file
1
android/java/MapboxGLAndroidSDKTestApp/proguard-rules.pro
vendored
Normal file
@@ -0,0 +1 @@
|
||||
# Proguard configuration is handeld in the SDK
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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 */
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -17,6 +17,7 @@ public:
|
||||
double latitude = 0;
|
||||
double zoom = 0;
|
||||
double bearing = 0;
|
||||
double pitch = 0;
|
||||
|
||||
MGLUserTrackingMode userTrackingMode = MGLUserTrackingModeNone;
|
||||
bool showsUserLocation = false;
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
@@ -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];
|
||||
|
||||
@@ -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];
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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) {
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
@@ -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([¢er](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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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();
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
|
||||
@@ -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 };
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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}};
|
||||
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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;
|
||||
|
||||
};
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
|
||||
@@ -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
52
src/mbgl/util/mat2.cpp
Normal 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
41
src/mbgl/util/mat2.hpp
Normal 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
|
||||
@@ -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];
|
||||
|
||||
@@ -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);
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
|
||||
29
src/mbgl/util/tile_coordinate.hpp
Normal file
29
src/mbgl/util/tile_coordinate.hpp
Normal 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
|
||||
@@ -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
33
src/mbgl/util/vec4.cpp
Normal 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
40
src/mbgl/util/vec4.hpp
Normal 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
|
||||
@@ -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
|
||||
|
||||
@@ -49,6 +49,7 @@ public:
|
||||
Request redoPlacement(
|
||||
TileWorker&,
|
||||
float angle,
|
||||
float pitch,
|
||||
bool collisionDebug,
|
||||
std::function<void ()> callback);
|
||||
|
||||
|
||||
@@ -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,
|
||||
|
||||
@@ -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 {
|
||||
|
||||
@@ -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">
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
Submodule test/suite updated: b6828212d3...083dbe5f3d
Reference in New Issue
Block a user