Example #1
0
void TouchUi::touchesMoved( app::TouchEvent& event )
{
	if ( !isEventValid( event ) ) {
		return;
	}

	mTouches.clear();
	if ( mEnabledTap ) {
		bool tapped = false;
		for ( const TouchEvent::Touch& touch : event.getTouches() ) {
			const vec2& tap = touch.getPos();
			if ( mMask.contains( tap ) && glm::distance( tap, mTapPosition ) < mTapThreshold ) {
				tapped = true;
				pushTouch( touch );
				break;
			}
		}
		if ( !tapped ) {
			resetTap();
		}
	}
	
	const vec2 panSpeed( 
		mPanSpeed.x * pow( ( mScaleMax.x + mScaleMin.x ) - mScale.x, 0.0002f ), 
		mPanSpeed.y * pow( ( mScaleMax.y + mScaleMin.y ) - mScale.y, 0.0002f ) );

	bool applyPan		= false;
	bool applyRotation	= false;
	bool applyScale		= false;
	float panX			= 0.0f;
	float panY			= 0.0f;
	float scaleX		= 0.0f;
	float scaleY		= 0.0f;
	float rotation		= 0.0f;

	// Pan
	if ( mEnabledPan ) {
		for ( const TouchEvent::Touch& touch : event.getTouches() ) {
			const vec2 a( touch.getPos() );
			const vec2 b( touch.getPrevPos() );
			if ( mMask.contains( b ) ) {
				panX = a.x - b.x;
				panY = a.y - b.y;
				pushTouch( touch );
				break;
			}
		}
	}
	
	// Multi-touch
	TouchEvent::Touch a;
	TouchEvent::Touch b;
	size_t numTouches = event.getTouches().size();
	if ( numTouches > 1 && mNumTouchPointsMax > 1 ) {
		a = *event.getTouches().begin();
		b = *( event.getTouches().begin() + 1 );
		const vec2 ap0( a.getPos() );
		const vec2 ap1( a.getPrevPos() );
		const vec2 bp0( b.getPos() );
		const vec2 bp1( b.getPrevPos() );
		if ( mMask.contains( bp0 ) && mMask.contains( bp1 ) ) {

			// Scale
			if ( mEnabledScale ) {
				float dx0 = glm::distance( ap0.x, bp0.x );
				float dx1 = glm::distance( ap1.x, bp1.x );
				scaleX = dx0 - dx1;

				float dy0 = glm::distance( ap0.y, bp0.y );
				float dy1 = glm::distance( ap1.y, bp1.y );
				scaleY = dy0 - dy1;
			}

			// Rotation
			if ( mEnabledRotation ) {
				float a0 = atan2( ap0.y - bp0.y, ap0.x - bp0.x );
				float a1 = atan2( ap1.y - bp1.y, ap1.x - bp1.x );
				rotation = wrapAngle( a0 - a1 );
			}
		}
	}

	vector<Motion> motions = {
		{ MotionType_PanX, abs( panX ) / mPanThreshold.x },
		{ MotionType_PanY, abs( panY ) / mPanThreshold.y },
		{ MotionType_Rotation, abs( rotation ) / mRotationThreshold },
		{ MotionType_ScaleX, abs( scaleX ) / mScaleThreshold.x },
		{ MotionType_ScaleY, abs( scaleY ) / mScaleThreshold.y },
	};

	auto evaluateMotion = [ &applyPan, &applyRotation, &applyScale ]( const Motion& motion )
	{
		MotionType t = motion.first;
		if ( motion.second > 1.0f ) {
			if ( t == MotionType_PanX || t == MotionType_PanY ) {
				applyPan = true;
			} else if ( t == MotionType_Rotation ) {
				applyRotation = true;
			} else if ( t == MotionType_ScaleX || t == MotionType_ScaleY ) {
				applyScale = true;
			}
		}
	};

	if ( mEnabledConstrain ) {
		sort( motions.begin(), motions.end(), []( const Motion& a, const Motion& b ) -> bool
		{
			return a.second > b.second;
		} );
		evaluateMotion( *motions.begin() );
	} else {
		for ( const Motion& motion : motions ) {
			evaluateMotion( motion );
		}
	}
	
	if ( numTouches > 1 && ( applyPan || applyRotation || applyScale ) ) {
		pushTouch( a );
		pushTouch( b );
	}
	
	if ( applyPan ) {
		mPanTarget.x += panX * panSpeed.x;
		mPanTarget.y += panY * panSpeed.y;
	}
	if ( applyRotation ) {
		mRotationTarget.z += rotation * mRotationSpeed;
	}
	if ( applyScale ) {
		if ( mScaleSymmetry ) {
			mScaleTarget	+= vec2( ( scaleX * mScaleSpeed.x + scaleY * mScaleSpeed.y ) * 0.5f );
		} else {
			mScaleTarget	+= vec2( scaleX * mScaleSpeed.x, scaleY * mScaleSpeed.y );
		}
	}
}