Skip to content

Commit

Permalink
strict lints for two_fingers_touch.ts (#3535)
Browse files Browse the repository at this point in the history
* tsc-strict for two_fingers_touch

I tried to change only types without touching the code, but there were
a few places that required small changes. These all seem like cases that
"should never happen", but I tried to retain the existing behavior
exactly, even when it might not make sense (e.g. returning NaN).

* Check preconditions rather than propogate NaNs

* more robust fallback

With these defaults the zoom, rotate, and pitch gestures all function
even if their state isn't initialized.

I tested this by temporarily commenting out the call to `_start`,
verifying the gestures work and that the failed asserts were printed to
the console.

* pr feedback: use existing return type

* pr feedback: use consistent return

* pr feedback: use `!` instead of assert + graceful fallback

* fixup! pr feedback: use existing return type

* remove unnecessary non-null assertion.

_lastPoints is typed to have length 2, so if it's non-null, it has 2
elements.
  • Loading branch information
michaelkirk authored Jan 4, 2024
1 parent c9fa1c5 commit 8aaa935
Showing 1 changed file with 50 additions and 50 deletions.
100 changes: 50 additions & 50 deletions src/ui/handler/two_fingers_touch.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import Point from '@mapbox/point-geometry';
import {DOM} from '../../util/dom';
import type {Map} from '../map';
import {Handler} from '../handler_manager';
import {Handler, HandlerResult} from '../handler_manager';

/**
* An options object sent to the enable function of some of the handlers
Expand All @@ -20,27 +20,27 @@ export type AroundCenterOptions = {
*/
abstract class TwoFingersTouchHandler implements Handler {

_enabled: boolean;
_active: boolean;
_firstTwoTouches: [number, number];
_vector: Point;
_startVector: Point;
_aroundCenter: boolean;
_enabled?: boolean;
_active?: boolean;
_firstTwoTouches?: [number, number];
_vector?: Point;
_startVector?: Point;
_aroundCenter?: boolean;

/** @internal */
constructor() {
this.reset();
}

reset() {
reset(): void {
this._active = false;
delete this._firstTwoTouches;
}

abstract _start(points: [Point, Point]);
abstract _move(points: [Point, Point], pinchAround: Point, e: TouchEvent);
abstract _start(points: [Point, Point]): void;
abstract _move(points: [Point, Point], pinchAround: Point | null, e: TouchEvent): HandlerResult | void;

touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>): void {
//log('touchstart', points, e.target.innerHTML, e.targetTouches.length ? e.targetTouches[0].target.innerHTML: undefined);
if (this._firstTwoTouches || mapTouches.length < 2) return;

Expand All @@ -53,7 +53,7 @@ abstract class TwoFingersTouchHandler implements Handler {
this._start([points[0], points[1]]);
}

touchmove(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
touchmove(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>): HandlerResult | void {
if (!this._firstTwoTouches) return;

e.preventDefault();
Expand All @@ -69,7 +69,7 @@ abstract class TwoFingersTouchHandler implements Handler {

}

touchend(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
touchend(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>): void {
if (!this._firstTwoTouches) return;

const [idA, idB] = this._firstTwoTouches;
Expand All @@ -82,7 +82,7 @@ abstract class TwoFingersTouchHandler implements Handler {
this.reset();
}

touchcancel() {
touchcancel(): void {
this.reset();
}

Expand All @@ -94,7 +94,7 @@ abstract class TwoFingersTouchHandler implements Handler {
* map.touchPitch.enable();
* ```
*/
enable(options?: AroundCenterOptions | boolean | null) {
enable(options?: AroundCenterOptions | boolean | null): void {
this._enabled = true;
this._aroundCenter = !!options && (options as AroundCenterOptions).around === 'center';
}
Expand All @@ -107,7 +107,7 @@ abstract class TwoFingersTouchHandler implements Handler {
* map.touchPitch.disable();
* ```
*/
disable() {
disable(): void {
this._enabled = false;
this.reset();
}
Expand All @@ -117,31 +117,32 @@ abstract class TwoFingersTouchHandler implements Handler {
*
* @returns `true` if the "drag to pitch" interaction is enabled.
*/
isEnabled() {
return this._enabled;
isEnabled(): boolean {
return !!this._enabled;
}

/**
* Returns a Boolean indicating whether the "drag to pitch" interaction is active, i.e. currently being used.
*
* @returns `true` if the "drag to pitch" interaction is active.
*/
isActive() {
return this._active;
isActive(): boolean {
return !!this._active;
}
}

function getTouchById(mapTouches: Array<Touch>, points: Array<Point>, identifier: number) {
function getTouchById(mapTouches: Array<Touch>, points: Array<Point>, identifier: number): Point | undefined {
for (let i = 0; i < mapTouches.length; i++) {
if (mapTouches[i].identifier === identifier) return points[i];
}
return undefined;
}

/* ZOOM */

const ZOOM_THRESHOLD = 0.1;

function getZoomDelta(distance, lastDistance) {
function getZoomDelta(distance: number, lastDistance: number): number {
return Math.log(distance / lastDistance) / Math.LN2;
}

Expand All @@ -152,23 +153,23 @@ function getZoomDelta(distance, lastDistance) {
*/
export class TwoFingersTouchZoomHandler extends TwoFingersTouchHandler {

_distance: number;
_startDistance: number;
_distance?: number;
_startDistance?: number;

reset() {
super.reset();
delete this._distance;
delete this._startDistance;
}

_start(points: [Point, Point]) {
_start(points: [Point, Point]): void {
this._startDistance = this._distance = points[0].dist(points[1]);
}

_move(points: [Point, Point], pinchAround: Point) {
const lastDistance = this._distance;
_move(points: [Point, Point], pinchAround: Point | null): HandlerResult | void {
const lastDistance = this._distance!;
this._distance = points[0].dist(points[1]);
if (!this._active && Math.abs(getZoomDelta(this._distance, this._startDistance)) < ZOOM_THRESHOLD) return;
if (!this._active && Math.abs(getZoomDelta(this._distance, this._startDistance!)) < ZOOM_THRESHOLD) return;
this._active = true;
return {
zoomDelta: getZoomDelta(this._distance, lastDistance),
Expand All @@ -181,7 +182,7 @@ export class TwoFingersTouchZoomHandler extends TwoFingersTouchHandler {

const ROTATION_THRESHOLD = 25; // pixels along circumference of touch circle

function getBearingDelta(a, b) {
function getBearingDelta(a: Point, b: Point): number {
return a.angleWith(b) * 180 / Math.PI;
}

Expand All @@ -191,22 +192,22 @@ function getBearingDelta(a, b) {
* @group Handlers
*/
export class TwoFingersTouchRotateHandler extends TwoFingersTouchHandler {
_minDiameter: number;
_minDiameter?: number;

reset() {
reset(): void {
super.reset();
delete this._minDiameter;
delete this._startVector;
delete this._vector;
}

_start(points: [Point, Point]) {
_start(points: [Point, Point]): void {
this._startVector = this._vector = points[0].sub(points[1]);
this._minDiameter = points[0].dist(points[1]);
}

_move(points: [Point, Point], pinchAround: Point) {
const lastVector = this._vector;
_move(points: [Point, Point], pinchAround: Point | null, _e: TouchEvent): HandlerResult | void {
const lastVector = this._vector!;
this._vector = points[0].sub(points[1]);

if (!this._active && this._isBelowThreshold(this._vector)) return;
Expand All @@ -218,7 +219,7 @@ export class TwoFingersTouchRotateHandler extends TwoFingersTouchHandler {
};
}

_isBelowThreshold(vector: Point) {
_isBelowThreshold(vector: Point): boolean {
/*
* The threshold before a rotation actually happens is configured in
* pixels along the circumference of the circle formed by the two fingers.
Expand All @@ -229,18 +230,18 @@ export class TwoFingersTouchRotateHandler extends TwoFingersTouchHandler {
* when pinching in and out.
*/

this._minDiameter = Math.min(this._minDiameter, vector.mag());
this._minDiameter = Math.min(this._minDiameter!, vector.mag());
const circumference = Math.PI * this._minDiameter;
const threshold = ROTATION_THRESHOLD / circumference * 360;

const bearingDeltaSinceStart = getBearingDelta(vector, this._startVector);
const bearingDeltaSinceStart = getBearingDelta(vector, this._startVector!);
return Math.abs(bearingDeltaSinceStart) < threshold;
}
}

/* PITCH */

function isVertical(vector) {
function isVertical(vector: Point): boolean {
return Math.abs(vector.y) > Math.abs(vector.x);
}

Expand All @@ -253,46 +254,45 @@ const ALLOWED_SINGLE_TOUCH_TIME = 100;
*/
export class TwoFingersTouchPitchHandler extends TwoFingersTouchHandler {

_valid: boolean | void;
_firstMove: number;
_lastPoints: [Point, Point];
_valid?: boolean;
_firstMove?: number;
_lastPoints?: [Point, Point];
_map: Map;
_currentTouchCount: number;
_currentTouchCount: number = 0;

constructor(map: Map) {
super();
this._map = map;
}

reset() {
reset(): void {
super.reset();
this._valid = undefined;
delete this._firstMove;
delete this._lastPoints;
}

touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>) {
touchstart(e: TouchEvent, points: Array<Point>, mapTouches: Array<Touch>): void {
super.touchstart(e, points, mapTouches);
this._currentTouchCount = mapTouches.length;
}

_start(points: [Point, Point]) {
_start(points: [Point, Point]): void {
this._lastPoints = points;
if (isVertical(points[0].sub(points[1]))) {
// fingers are more horizontal than vertical
this._valid = false;

}
}

_move(points: [Point, Point], center: Point, e: TouchEvent) {
_move(points: [Point, Point], center: Point | null, e: TouchEvent): HandlerResult | void {
// If cooperative gestures is enabled, we need a 3-finger minimum for this gesture to register
if (this._map.cooperativeGestures.isEnabled() && this._currentTouchCount < 3) {
return;
}

const vectorA = points[0].sub(this._lastPoints[0]);
const vectorB = points[1].sub(this._lastPoints[1]);
const vectorA = points[0].sub(this._lastPoints![0]);
const vectorB = points[1].sub(this._lastPoints![1]);

this._valid = this.gestureBeginsVertically(vectorA, vectorB, e.timeStamp);
if (!this._valid) return;
Expand All @@ -306,7 +306,7 @@ export class TwoFingersTouchPitchHandler extends TwoFingersTouchHandler {
};
}

gestureBeginsVertically(vectorA: Point, vectorB: Point, timeStamp: number) {
gestureBeginsVertically(vectorA: Point, vectorB: Point, timeStamp: number): boolean | undefined {
if (this._valid !== undefined) return this._valid;

const threshold = 2;
Expand Down

0 comments on commit 8aaa935

Please sign in to comment.