Transform needs only a single TransformState

This commit is contained in:
John Firebaugh
2015-04-22 16:10:31 -07:00
parent 3396bcaaac
commit 4ba78b6bcf
3 changed files with 85 additions and 100 deletions

View File

@@ -37,17 +37,17 @@ bool Transform::resize(const uint16_t w, const uint16_t h, const float ratio,
const uint16_t fb_w, const uint16_t fb_h) {
std::lock_guard<std::recursive_mutex> lock(mtx);
if (final.width != w || final.height != h || final.pixelRatio != ratio ||
final.framebuffer[0] != fb_w || final.framebuffer[1] != fb_h) {
if (state.width != w || state.height != h || state.pixelRatio != ratio ||
state.framebuffer[0] != fb_w || state.framebuffer[1] != fb_h) {
view.notifyMapChange(MapChangeRegionWillChange);
current.width = final.width = w;
current.height = final.height = h;
current.pixelRatio = final.pixelRatio = ratio;
current.framebuffer[0] = final.framebuffer[0] = fb_w;
current.framebuffer[1] = final.framebuffer[1] = fb_h;
constrain(current.scale, current.y);
state.width = w;
state.height = h;
state.pixelRatio = ratio;
state.framebuffer[0] = fb_w;
state.framebuffer[1] = fb_h;
constrain(state.scale, state.y);
view.notifyMapChange(MapChangeRegionDidChange);
@@ -76,27 +76,27 @@ void Transform::_moveBy(const double dx, const double dy, const Duration duratio
MapChangeRegionWillChangeAnimated :
MapChangeRegionWillChange);
final.x = current.x + std::cos(current.angle) * dx + std::sin(current.angle) * dy;
final.y = current.y + std::cos(current.angle) * dy + std::sin(-current.angle) * dx;
double x = state.x + std::cos(state.angle) * dx + std::sin( state.angle) * dy;
double y = state.y + std::cos(state.angle) * dy + std::sin(-state.angle) * dx;
constrain(final.scale, final.y);
constrain(state.scale, y);
if (duration == Duration::zero()) {
current.x = final.x;
current.y = final.y;
state.x = x;
state.y = y;
} else {
const double startX = current.x;
const double startY = current.y;
current.panning = true;
const double startX = state.x;
const double startY = state.y;
state.panning = true;
startTransition(
[=](double t) {
current.x = util::interpolate(startX, final.x, t);
current.y = util::interpolate(startY, final.y, t);
state.x = util::interpolate(startX, x, t);
state.y = util::interpolate(startY, y, t);
return Update::Nothing;
},
[=] {
current.panning = false;
state.panning = false;
}, duration);
}
@@ -116,10 +116,10 @@ void Transform::setLatLng(const LatLng latLng, const Duration duration) {
const double m = 1 - 1e-15;
const double f = std::fmin(std::fmax(std::sin(util::DEG2RAD * latLng.latitude), -m), m);
double xn = -latLng.longitude * current.Bc;
double yn = 0.5 * current.Cc * std::log((1 + f) / (1 - f));
double xn = -latLng.longitude * state.Bc;
double yn = 0.5 * state.Cc * std::log((1 + f) / (1 - f));
_setScaleXY(current.scale, xn, yn, duration);
_setScaleXY(state.scale, xn, yn, duration);
}
void Transform::setLatLngZoom(const LatLng latLng, const double zoom, const Duration duration) {
@@ -132,14 +132,14 @@ void Transform::setLatLngZoom(const LatLng latLng, const double zoom, const Dura
double new_scale = std::pow(2.0, zoom);
const double s = new_scale * util::tileSize;
current.Bc = s / 360;
current.Cc = s / util::M2PI;
state.Bc = s / 360;
state.Cc = s / util::M2PI;
const double m = 1 - 1e-15;
const double f = std::fmin(std::fmax(std::sin(util::DEG2RAD * latLng.latitude), -m), m);
double xn = -latLng.longitude * current.Bc;
double yn = 0.5 * current.Cc * std::log((1 + f) / (1 - f));
double xn = -latLng.longitude * state.Bc;
double yn = 0.5 * state.Cc * std::log((1 + f) / (1 - f));
_setScaleXY(new_scale, xn, yn, duration);
}
@@ -155,7 +155,7 @@ void Transform::scaleBy(const double ds, const double cx, const double cy, const
std::lock_guard<std::recursive_mutex> lock(mtx);
// clamp scale to min/max values
double new_scale = current.scale * ds;
double new_scale = state.scale * ds;
if (new_scale < min_scale) {
new_scale = min_scale;
} else if (new_scale > max_scale) {
@@ -189,18 +189,18 @@ void Transform::setZoom(const double zoom, const Duration duration) {
double Transform::getZoom() const {
std::lock_guard<std::recursive_mutex> lock(mtx);
return final.getZoom();
return state.getZoom();
}
double Transform::getScale() const {
std::lock_guard<std::recursive_mutex> lock(mtx);
return final.scale;
return state.scale;
}
double Transform::getMinZoom() const {
double test_scale = current.scale;
double test_y = current.y;
double test_scale = state.scale;
double test_y = state.y;
constrain(test_scale, test_y);
return std::log2(std::fmin(min_scale, test_scale));
@@ -222,23 +222,23 @@ void Transform::_setScale(double new_scale, double cx, double cy, const Duration
// Zoom in on the center if we don't have click or gesture anchor coordinates.
if (cx < 0 || cy < 0) {
cx = static_cast<double>(current.width) / 2.0;
cy = static_cast<double>(current.height) / 2.0;
cx = static_cast<double>(state.width) / 2.0;
cy = static_cast<double>(state.height) / 2.0;
}
// Account for the x/y offset from the center (= where the user clicked or pinched)
const double factor = new_scale / current.scale;
const double dx = (cx - static_cast<double>(current.width) / 2.0) * (1.0 - factor);
const double dy = (cy - static_cast<double>(current.height) / 2.0) * (1.0 - factor);
const double factor = new_scale / state.scale;
const double dx = (cx - static_cast<double>(state.width) / 2.0) * (1.0 - factor);
const double dy = (cy - static_cast<double>(state.height) / 2.0) * (1.0 - factor);
// Account for angle
const double angle_sin = std::sin(-current.angle);
const double angle_cos = std::cos(-current.angle);
const double angle_sin = std::sin(-state.angle);
const double angle_cos = std::cos(-state.angle);
const double ax = angle_cos * dx - angle_sin * dy;
const double ay = angle_sin * dx + angle_cos * dy;
const double xn = current.x * factor + ax;
const double yn = current.y * factor + ay;
const double xn = state.x * factor + ax;
const double yn = state.y * factor + ay;
_setScaleXY(new_scale, xn, yn, duration);
}
@@ -251,39 +251,39 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
MapChangeRegionWillChangeAnimated :
MapChangeRegionWillChange);
final.scale = new_scale;
final.x = xn;
final.y = yn;
double scale = new_scale;
double x = xn;
double y = yn;
constrain(final.scale, final.y);
constrain(scale, y);
if (duration == Duration::zero()) {
current.scale = final.scale;
current.x = final.x;
current.y = final.y;
const double s = current.scale * util::tileSize;
current.Bc = s / 360;
current.Cc = s / util::M2PI;
state.scale = scale;
state.x = x;
state.y = y;
const double s = state.scale * util::tileSize;
state.Bc = s / 360;
state.Cc = s / util::M2PI;
} else {
const double startS = current.scale;
const double startX = current.x;
const double startY = current.y;
current.panning = true;
current.scaling = true;
const double startS = state.scale;
const double startX = state.x;
const double startY = state.y;
state.panning = true;
state.scaling = true;
startTransition(
[=](double t) {
current.scale = util::interpolate(startS, final.scale, t);
current.x = util::interpolate(startX, final.x, t);
current.y = util::interpolate(startY, final.y, t);
const double s = current.scale * util::tileSize;
current.Bc = s / 360;
current.Cc = s / util::M2PI;
state.scale = util::interpolate(startS, scale, t);
state.x = util::interpolate(startX, x, t);
state.y = util::interpolate(startY, y, t);
const double s = state.scale * util::tileSize;
state.Bc = s / 360;
state.Cc = s / util::M2PI;
return Update::Zoom;
},
[=] {
current.panning = false;
current.scaling = false;
state.panning = false;
state.scaling = false;
}, duration);
}
@@ -297,10 +297,10 @@ void Transform::_setScaleXY(const double new_scale, const double xn, const doubl
void Transform::constrain(double& scale, double& y) const {
// Constrain minimum zoom to avoid zooming out far enough to show off-world areas.
if (scale < (current.height / util::tileSize)) scale = (current.height / util::tileSize);
if (scale < (state.height / util::tileSize)) scale = (state.height / util::tileSize);
// Constrain min/max vertical pan to avoid showing off-world areas.
double max_y = ((scale * util::tileSize) - current.height) / 2;
double max_y = ((scale * util::tileSize) - state.height) / 2;
if (y > max_y) y = max_y;
if (y < -max_y) y = -max_y;
@@ -316,7 +316,7 @@ void Transform::rotateBy(const double start_x, const double start_y, const doubl
std::lock_guard<std::recursive_mutex> lock(mtx);
double center_x = static_cast<double>(current.width) / 2.0, center_y = static_cast<double>(current.height) / 2.0;
double center_x = static_cast<double>(state.width) / 2.0, center_y = static_cast<double>(state.height) / 2.0;
const double begin_center_x = start_x - center_x;
const double begin_center_y = start_y - center_y;
@@ -338,7 +338,7 @@ void Transform::rotateBy(const double start_x, const double start_y, const doubl
const double first_x = start_x - center_x, first_y = start_y - center_y;
const double second_x = end_x - center_x, second_y = end_y - center_y;
const double ang = current.angle + util::angle_between(first_x, first_y, second_x, second_y);
const double ang = state.angle + util::angle_between(first_x, first_y, second_x, second_y);
_setAngle(ang, duration);
}
@@ -363,8 +363,8 @@ void Transform::setAngle(const double new_angle, const double cx, const double c
double dx = 0, dy = 0;
if (cx >= 0 && cy >= 0) {
dx = (static_cast<double>(final.width) / 2.0) - cx;
dy = (static_cast<double>(final.height) / 2.0) - cy;
dx = (static_cast<double>(state.width) / 2.0) - cx;
dy = (static_cast<double>(state.height) / 2.0) - cy;
_moveBy(dx, dy, Duration::zero());
}
@@ -382,22 +382,22 @@ void Transform::_setAngle(double new_angle, const Duration duration) {
MapChangeRegionWillChangeAnimated :
MapChangeRegionWillChange);
final.angle = _normalizeAngle(new_angle, current.angle);
current.angle = _normalizeAngle(current.angle, final.angle);
double angle = _normalizeAngle(new_angle, state.angle);
state.angle = _normalizeAngle(state.angle, angle);
if (duration == Duration::zero()) {
current.angle = final.angle;
state.angle = angle;
} else {
const double startA = current.angle;
current.rotating = true;
const double startA = state.angle;
state.rotating = true;
startTransition(
[=](double t) {
current.angle = util::interpolate(startA, final.angle, t);
state.angle = util::interpolate(startA, angle, t);
return Update::Nothing;
},
[=] {
current.rotating = false;
state.rotating = false;
}, duration);
}
@@ -410,7 +410,7 @@ void Transform::_setAngle(double new_angle, const Duration duration) {
double Transform::getAngle() const {
std::lock_guard<std::recursive_mutex> lock(mtx);
return final.angle;
return state.angle;
}
@@ -467,7 +467,7 @@ void Transform::cancelTransitions() {
void Transform::setGestureInProgress(bool inProgress) {
std::lock_guard<std::recursive_mutex> lock(mtx);
current.gestureInProgress = inProgress;
state.gestureInProgress = inProgress;
}
#pragma mark - Transform state
@@ -475,11 +475,5 @@ void Transform::setGestureInProgress(bool inProgress) {
const TransformState Transform::currentState() const {
std::lock_guard<std::recursive_mutex> lock(mtx);
return current;
}
const TransformState Transform::finalState() const {
std::lock_guard<std::recursive_mutex> lock(mtx);
return final;
return state;
}

View File

@@ -31,7 +31,7 @@ public:
void moveBy(double dx, double dy, Duration = Duration::zero());
void setLatLng(LatLng latLng, Duration = Duration::zero());
void setLatLngZoom(LatLng latLng, double zoom, Duration = Duration::zero());
inline const LatLng getLatLng() const { return current.getLatLng(); }
inline const LatLng getLatLng() const { return state.getLatLng(); }
// Zoom
void scaleBy(double ds, double cx = -1, double cy = -1, Duration = Duration::zero());
@@ -58,7 +58,6 @@ public:
// Transform state
const TransformState currentState() const;
const TransformState finalState() const;
private:
// Functions prefixed with underscores will *not* perform any locks. It is the caller's
@@ -74,13 +73,7 @@ private:
mutable std::recursive_mutex mtx;
// This reflects the current state of the transform, representing the actual position of the
// map. After calling a transform function with a timer, this will likely remain the same until
// you render a new frame.
TransformState current;
// This reflects the final position of the transform, after all possible transition took place.
TransformState final;
TransformState state;
// Limit the amount of zooming possible on the map.
const double min_scale = std::pow(2, 0);

View File

@@ -76,9 +76,8 @@
0,
@"compass should not be visible when map is unrotated");
XCTAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
[NSValue valueWithCGAffineTransform:CGAffineTransformIdentity],
@"compass rotation should indicate map rotation");
XCTAssert(CGAffineTransformEqualToTransform(tester.compass.transform, CGAffineTransformIdentity),
@"compass rotation should indicate map rotation");
}
- (void)testDirectionReset {
@@ -100,9 +99,8 @@
0,
@"compass should not be visible when map is unrotated");
XCTAssertEqualObjects([NSValue valueWithCGAffineTransform:tester.compass.transform],
[NSValue valueWithCGAffineTransform:CGAffineTransformIdentity],
@"compass rotation should indicate map rotation");
XCTAssert(CGAffineTransformEqualToTransform(tester.compass.transform, CGAffineTransformIdentity),
@"compass rotation should indicate map rotation");
}
- (void)testZoom {