mirror of
https://github.com/LawnchairLauncher/lawnchair.git
synced 2026-02-20 11:18:21 +00:00
Merge "Extract PreviewPositionHelper to shared library" into tm-qpr-dev
This commit is contained in:
committed by
Android (Google) Code Review
commit
3e7c611358
@@ -46,9 +46,9 @@ import com.android.launcher3.util.TraceHelper;
|
||||
import com.android.quickstep.AnimatedFloat;
|
||||
import com.android.quickstep.BaseActivityInterface;
|
||||
import com.android.quickstep.TaskAnimationManager;
|
||||
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
|
||||
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
import com.android.systemui.shared.system.SyncRtSurfaceTransactionApplierCompat.SurfaceParams.Builder;
|
||||
|
||||
@@ -317,9 +317,9 @@ public class TaskViewSimulator implements TransformParams.BuilderProxy {
|
||||
// mIsRecentsRtl is the inverse of TaskView RTL.
|
||||
boolean isRtlEnabled = !mIsRecentsRtl;
|
||||
mPositionHelper.updateThumbnailMatrix(
|
||||
mThumbnailPosition, mThumbnailData,
|
||||
mTaskRect.width(), mTaskRect.height(),
|
||||
mDp, mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
|
||||
mThumbnailPosition, mThumbnailData, mTaskRect.width(), mTaskRect.height(),
|
||||
mDp.widthPx, mDp.taskbarSize, mDp.isTablet,
|
||||
mOrientationState.getRecentsActivityRotation(), isRtlEnabled);
|
||||
mPositionHelper.getMatrix().invert(mInversePositionMatrix);
|
||||
if (DEBUG) {
|
||||
Log.d(TAG, " taskRect: " + mTaskRect);
|
||||
|
||||
@@ -16,10 +16,12 @@
|
||||
|
||||
package com.android.quickstep.views;
|
||||
|
||||
import static android.app.WindowConfiguration.WINDOWING_MODE_FULLSCREEN;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_NAVIGATION_BARS;
|
||||
import static android.view.WindowInsetsController.APPEARANCE_LIGHT_STATUS_BARS;
|
||||
|
||||
import static com.android.systemui.shared.recents.utilities.PreviewPositionHelper.MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT;
|
||||
import static com.android.systemui.shared.recents.utilities.Utilities.isRelativePercentDifferenceGreaterThan;
|
||||
|
||||
import android.content.Context;
|
||||
import android.graphics.Bitmap;
|
||||
import android.graphics.BitmapShader;
|
||||
@@ -39,7 +41,6 @@ import android.os.Build;
|
||||
import android.util.AttributeSet;
|
||||
import android.util.FloatProperty;
|
||||
import android.util.Property;
|
||||
import android.view.Surface;
|
||||
import android.view.View;
|
||||
import android.widget.ImageView;
|
||||
|
||||
@@ -58,6 +59,7 @@ import com.android.quickstep.TaskOverlayFactory.TaskOverlay;
|
||||
import com.android.quickstep.views.TaskView.FullscreenDrawParams;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
|
||||
|
||||
/**
|
||||
* A task in the Recents view.
|
||||
@@ -65,7 +67,6 @@ import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
public class TaskThumbnailView extends View {
|
||||
private static final MainThreadInitializedObject<FullscreenDrawParams> TEMP_PARAMS =
|
||||
new MainThreadInitializedObject<>(FullscreenDrawParams::new);
|
||||
private static final float MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT = 0.1f;
|
||||
|
||||
public static final Property<TaskThumbnailView, Float> DIM_ALPHA =
|
||||
new FloatProperty<TaskThumbnailView>("dimAlpha") {
|
||||
@@ -417,7 +418,7 @@ public class TaskThumbnailView extends View {
|
||||
float thumbnailDataAspect =
|
||||
mThumbnailData.thumbnail.getWidth() / (float) mThumbnailData.thumbnail.getHeight();
|
||||
|
||||
return Utilities.isRelativePercentDifferenceGreaterThan(thumbnailViewAspect,
|
||||
return isRelativePercentDifferenceGreaterThan(thumbnailViewAspect,
|
||||
thumbnailDataAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
|
||||
}
|
||||
|
||||
@@ -441,8 +442,8 @@ public class TaskThumbnailView extends View {
|
||||
*/
|
||||
private void refreshOverlay() {
|
||||
if (mOverlayEnabled) {
|
||||
getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.mMatrix,
|
||||
mPreviewPositionHelper.mIsOrientationChanged);
|
||||
getTaskOverlay().initOverlay(mTask, mThumbnailData, mPreviewPositionHelper.getMatrix(),
|
||||
mPreviewPositionHelper.isOrientationChanged());
|
||||
} else {
|
||||
getTaskOverlay().reset();
|
||||
}
|
||||
@@ -463,18 +464,19 @@ public class TaskThumbnailView extends View {
|
||||
}
|
||||
|
||||
private void updateThumbnailMatrix() {
|
||||
mPreviewPositionHelper.mIsOrientationChanged = false;
|
||||
mPreviewPositionHelper.setOrientationChanged(false);
|
||||
if (mBitmapShader != null && mThumbnailData != null) {
|
||||
mPreviewRect.set(0, 0, mThumbnailData.thumbnail.getWidth(),
|
||||
mThumbnailData.thumbnail.getHeight());
|
||||
int currentRotation = getTaskView().getRecentsView().getPagedViewOrientedState()
|
||||
.getRecentsActivityRotation();
|
||||
boolean isRtl = getLayoutDirection() == LAYOUT_DIRECTION_RTL;
|
||||
DeviceProfile dp = mActivity.getDeviceProfile();
|
||||
mPreviewPositionHelper.updateThumbnailMatrix(mPreviewRect, mThumbnailData,
|
||||
getMeasuredWidth(), getMeasuredHeight(), mActivity.getDeviceProfile(),
|
||||
currentRotation, isRtl);
|
||||
getMeasuredWidth(), getMeasuredHeight(), dp.widthPx, dp.taskbarSize,
|
||||
dp.isTablet, currentRotation, isRtl);
|
||||
|
||||
mBitmapShader.setLocalMatrix(mPreviewPositionHelper.mMatrix);
|
||||
mBitmapShader.setLocalMatrix(mPreviewPositionHelper.getMatrix());
|
||||
mPaint.setShader(mBitmapShader);
|
||||
}
|
||||
getTaskView().updateCurrentFullscreenParams(mPreviewPositionHelper);
|
||||
@@ -514,199 +516,4 @@ public class TaskThumbnailView extends View {
|
||||
}
|
||||
return mThumbnailData.isRealSnapshot && !mTask.isLocked;
|
||||
}
|
||||
|
||||
/**
|
||||
* Utility class to position the thumbnail in the TaskView
|
||||
*/
|
||||
public static class PreviewPositionHelper {
|
||||
|
||||
private static final RectF EMPTY_RECT_F = new RectF();
|
||||
|
||||
// Contains the portion of the thumbnail that is unclipped when fullscreen progress = 1.
|
||||
private final RectF mClippedInsets = new RectF();
|
||||
private final Matrix mMatrix = new Matrix();
|
||||
private boolean mIsOrientationChanged;
|
||||
|
||||
public Matrix getMatrix() {
|
||||
return mMatrix;
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the matrix based on the provided parameters
|
||||
*/
|
||||
public void updateThumbnailMatrix(Rect thumbnailBounds, ThumbnailData thumbnailData,
|
||||
int canvasWidth, int canvasHeight, DeviceProfile dp, int currentRotation,
|
||||
boolean isRtl) {
|
||||
boolean isRotated = false;
|
||||
boolean isOrientationDifferent;
|
||||
|
||||
int thumbnailRotation = thumbnailData.rotation;
|
||||
int deltaRotate = getRotationDelta(currentRotation, thumbnailRotation);
|
||||
RectF thumbnailClipHint = new RectF();
|
||||
float canvasScreenRatio = canvasWidth / (float) dp.widthPx;
|
||||
float scaledTaskbarSize = dp.taskbarSize * canvasScreenRatio;
|
||||
thumbnailClipHint.bottom = dp.isTablet ? scaledTaskbarSize : 0;
|
||||
|
||||
float scale = thumbnailData.scale;
|
||||
final float thumbnailScale;
|
||||
|
||||
// Landscape vs portrait change.
|
||||
// Note: Disable rotation in grid layout.
|
||||
boolean windowingModeSupportsRotation =
|
||||
thumbnailData.windowingMode == WINDOWING_MODE_FULLSCREEN && !dp.isTablet;
|
||||
isOrientationDifferent = isOrientationChange(deltaRotate)
|
||||
&& windowingModeSupportsRotation;
|
||||
if (canvasWidth == 0 || canvasHeight == 0 || scale == 0) {
|
||||
// If we haven't measured , skip the thumbnail drawing and only draw the background
|
||||
// color
|
||||
thumbnailScale = 0f;
|
||||
} else {
|
||||
// Rotate the screenshot if not in multi-window mode
|
||||
isRotated = deltaRotate > 0 && windowingModeSupportsRotation;
|
||||
|
||||
float surfaceWidth = thumbnailBounds.width() / scale;
|
||||
float surfaceHeight = thumbnailBounds.height() / scale;
|
||||
float availableWidth = surfaceWidth
|
||||
- (thumbnailClipHint.left + thumbnailClipHint.right);
|
||||
float availableHeight = surfaceHeight
|
||||
- (thumbnailClipHint.top + thumbnailClipHint.bottom);
|
||||
|
||||
float canvasAspect = canvasWidth / (float) canvasHeight;
|
||||
float availableAspect = isRotated
|
||||
? availableHeight / availableWidth
|
||||
: availableWidth / availableHeight;
|
||||
boolean isAspectLargelyDifferent =
|
||||
Utilities.isRelativePercentDifferenceGreaterThan(canvasAspect,
|
||||
availableAspect, MAX_PCT_BEFORE_ASPECT_RATIOS_CONSIDERED_DIFFERENT);
|
||||
if (isRotated && isAspectLargelyDifferent) {
|
||||
// Do not rotate thumbnail if it would not improve fit
|
||||
isRotated = false;
|
||||
isOrientationDifferent = false;
|
||||
}
|
||||
|
||||
if (isAspectLargelyDifferent) {
|
||||
// Crop letterbox insets if insets isn't already clipped
|
||||
thumbnailClipHint.left = thumbnailData.letterboxInsets.left;
|
||||
thumbnailClipHint.right = thumbnailData.letterboxInsets.right;
|
||||
thumbnailClipHint.top = thumbnailData.letterboxInsets.top;
|
||||
thumbnailClipHint.bottom = thumbnailData.letterboxInsets.bottom;
|
||||
availableWidth = surfaceWidth
|
||||
- (thumbnailClipHint.left + thumbnailClipHint.right);
|
||||
availableHeight = surfaceHeight
|
||||
- (thumbnailClipHint.top + thumbnailClipHint.bottom);
|
||||
}
|
||||
|
||||
final float targetW, targetH;
|
||||
if (isOrientationDifferent) {
|
||||
targetW = canvasHeight;
|
||||
targetH = canvasWidth;
|
||||
} else {
|
||||
targetW = canvasWidth;
|
||||
targetH = canvasHeight;
|
||||
}
|
||||
float targetAspect = targetW / targetH;
|
||||
|
||||
// Update the clipHint such that
|
||||
// > the final clipped position has same aspect ratio as requested by canvas
|
||||
// > first fit the width and crop the extra height
|
||||
// > if that will leave empty space, fit the height and crop the width instead
|
||||
float croppedWidth = availableWidth;
|
||||
float croppedHeight = croppedWidth / targetAspect;
|
||||
if (croppedHeight > availableHeight) {
|
||||
croppedHeight = availableHeight;
|
||||
if (croppedHeight < targetH) {
|
||||
croppedHeight = Math.min(targetH, surfaceHeight);
|
||||
}
|
||||
croppedWidth = croppedHeight * targetAspect;
|
||||
|
||||
// One last check in case the task aspect radio messed up something
|
||||
if (croppedWidth > surfaceWidth) {
|
||||
croppedWidth = surfaceWidth;
|
||||
croppedHeight = croppedWidth / targetAspect;
|
||||
}
|
||||
}
|
||||
|
||||
// Update the clip hints. Align to 0,0, crop the remaining.
|
||||
if (isRtl) {
|
||||
thumbnailClipHint.left += availableWidth - croppedWidth;
|
||||
if (thumbnailClipHint.right < 0) {
|
||||
thumbnailClipHint.left += thumbnailClipHint.right;
|
||||
thumbnailClipHint.right = 0;
|
||||
}
|
||||
} else {
|
||||
thumbnailClipHint.right += availableWidth - croppedWidth;
|
||||
if (thumbnailClipHint.left < 0) {
|
||||
thumbnailClipHint.right += thumbnailClipHint.left;
|
||||
thumbnailClipHint.left = 0;
|
||||
}
|
||||
}
|
||||
thumbnailClipHint.bottom += availableHeight - croppedHeight;
|
||||
if (thumbnailClipHint.top < 0) {
|
||||
thumbnailClipHint.bottom += thumbnailClipHint.top;
|
||||
thumbnailClipHint.top = 0;
|
||||
} else if (thumbnailClipHint.bottom < 0) {
|
||||
thumbnailClipHint.top += thumbnailClipHint.bottom;
|
||||
thumbnailClipHint.bottom = 0;
|
||||
}
|
||||
|
||||
thumbnailScale = targetW / (croppedWidth * scale);
|
||||
}
|
||||
|
||||
if (!isRotated) {
|
||||
mMatrix.setTranslate(
|
||||
-thumbnailClipHint.left * scale,
|
||||
-thumbnailClipHint.top * scale);
|
||||
} else {
|
||||
setThumbnailRotation(deltaRotate, thumbnailBounds);
|
||||
}
|
||||
|
||||
mClippedInsets.set(0, 0, 0, scaledTaskbarSize);
|
||||
|
||||
mMatrix.postScale(thumbnailScale, thumbnailScale);
|
||||
mIsOrientationChanged = isOrientationDifferent;
|
||||
}
|
||||
|
||||
private int getRotationDelta(int oldRotation, int newRotation) {
|
||||
int delta = newRotation - oldRotation;
|
||||
if (delta < 0) delta += 4;
|
||||
return delta;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param deltaRotation the number of 90 degree turns from the current orientation
|
||||
* @return {@code true} if the change in rotation results in a shift from landscape to
|
||||
* portrait or vice versa, {@code false} otherwise
|
||||
*/
|
||||
private boolean isOrientationChange(int deltaRotation) {
|
||||
return deltaRotation == Surface.ROTATION_90 || deltaRotation == Surface.ROTATION_270;
|
||||
}
|
||||
|
||||
private void setThumbnailRotation(int deltaRotate, Rect thumbnailPosition) {
|
||||
float translateX = 0;
|
||||
float translateY = 0;
|
||||
|
||||
mMatrix.setRotate(90 * deltaRotate);
|
||||
switch (deltaRotate) { /* Counter-clockwise */
|
||||
case Surface.ROTATION_90:
|
||||
translateX = thumbnailPosition.height();
|
||||
break;
|
||||
case Surface.ROTATION_270:
|
||||
translateY = thumbnailPosition.width();
|
||||
break;
|
||||
case Surface.ROTATION_180:
|
||||
translateX = thumbnailPosition.width();
|
||||
translateY = thumbnailPosition.height();
|
||||
break;
|
||||
}
|
||||
mMatrix.postTranslate(translateX, translateY);
|
||||
}
|
||||
|
||||
/**
|
||||
* Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
|
||||
*/
|
||||
public RectF getInsetsToDrawInFullscreen(DeviceProfile dp) {
|
||||
return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps
|
||||
? mClippedInsets : EMPTY_RECT_F;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,9 +98,9 @@ import com.android.quickstep.util.RecentsOrientedState;
|
||||
import com.android.quickstep.util.SplitSelectStateController;
|
||||
import com.android.quickstep.util.TaskCornerRadius;
|
||||
import com.android.quickstep.util.TransformParams;
|
||||
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper;
|
||||
import com.android.systemui.shared.recents.model.Task;
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData;
|
||||
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper;
|
||||
import com.android.systemui.shared.system.ActivityManagerWrapper;
|
||||
import com.android.systemui.shared.system.QuickStepContract;
|
||||
import com.android.systemui.shared.system.RemoteAnimationTargetCompat;
|
||||
@@ -121,6 +121,8 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
private static final String TAG = TaskView.class.getSimpleName();
|
||||
private static final boolean DEBUG = false;
|
||||
|
||||
private static final RectF EMPTY_RECT_F = new RectF();
|
||||
|
||||
public static final int FLAG_UPDATE_ICON = 1;
|
||||
public static final int FLAG_UPDATE_THUMBNAIL = FLAG_UPDATE_ICON << 1;
|
||||
|
||||
@@ -1572,7 +1574,7 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
*/
|
||||
public void setProgress(float fullscreenProgress, float parentScale, float taskViewScale,
|
||||
int previewWidth, DeviceProfile dp, PreviewPositionHelper pph) {
|
||||
RectF insets = pph.getInsetsToDrawInFullscreen(dp);
|
||||
RectF insets = getInsetsToDrawInFullscreen(pph, dp);
|
||||
|
||||
float currentInsetsLeft = insets.left * fullscreenProgress;
|
||||
float currentInsetsTop = insets.top * fullscreenProgress;
|
||||
@@ -1591,6 +1593,14 @@ public class TaskView extends FrameLayout implements Reusable {
|
||||
mScale = previewWidth / (previewWidth + currentInsetsLeft + currentInsetsRight);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Insets to used for clipping the thumbnail (in case it is drawing outside its own space)
|
||||
*/
|
||||
private static RectF getInsetsToDrawInFullscreen(PreviewPositionHelper pph, DeviceProfile dp) {
|
||||
return dp.isTaskbarPresent && !dp.isTaskbarPresentInApps
|
||||
? pph.getClippedInsets() : EMPTY_RECT_F;
|
||||
}
|
||||
}
|
||||
|
||||
public class TaskIdAttributeContainer {
|
||||
|
||||
@@ -20,26 +20,34 @@ import android.graphics.RectF
|
||||
import androidx.test.ext.junit.runners.AndroidJUnit4
|
||||
import androidx.test.filters.SmallTest
|
||||
import com.android.launcher3.DeviceProfileBaseTest
|
||||
import com.android.quickstep.views.TaskThumbnailView.PreviewPositionHelper
|
||||
import com.android.quickstep.views.TaskView.FullscreenDrawParams
|
||||
import com.android.systemui.shared.recents.model.ThumbnailData
|
||||
import com.android.systemui.shared.recents.utilities.PreviewPositionHelper
|
||||
import com.google.common.truth.Truth.assertThat
|
||||
import org.junit.Before
|
||||
import org.junit.Test
|
||||
import org.junit.runner.RunWith
|
||||
import org.mockito.Mockito.mock
|
||||
|
||||
/**
|
||||
* Test for TaskThumbnailView class.
|
||||
* Test for FullscreenDrawParams class.
|
||||
*/
|
||||
@SmallTest
|
||||
@RunWith(AndroidJUnit4::class)
|
||||
class TaskThumbnailViewTest : DeviceProfileBaseTest() {
|
||||
class FullscreenDrawParamsTest : DeviceProfileBaseTest() {
|
||||
|
||||
private var mThumbnailData: ThumbnailData = mock(ThumbnailData::class.java)
|
||||
|
||||
private val mPreviewPositionHelper = PreviewPositionHelper()
|
||||
private lateinit var params: FullscreenDrawParams
|
||||
|
||||
@Before
|
||||
fun setup() {
|
||||
params = FullscreenDrawParams(context)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getInsetsToDrawInFullscreen_clipTaskbarSizeFromBottomForTablets() {
|
||||
fun setFullProgress_currentDrawnInsets_clipTaskbarSizeFromBottomForTablets() {
|
||||
initializeVarsForTablet()
|
||||
val dp = newDP()
|
||||
val previewRect = Rect(0, 0, 100, 100)
|
||||
@@ -49,15 +57,18 @@ class TaskThumbnailViewTest : DeviceProfileBaseTest() {
|
||||
val isRtl = false
|
||||
|
||||
mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
|
||||
canvasHeight, dp, currentRotation, isRtl)
|
||||
canvasHeight, dp.widthPx, dp.taskbarSize, dp.isTablet, currentRotation,
|
||||
isRtl)
|
||||
params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
|
||||
/* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper)
|
||||
|
||||
val expectedClippedInsets = RectF(0f, 0f, 0f, dp.taskbarSize / 2f)
|
||||
assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp))
|
||||
assertThat(params.mCurrentDrawnInsets)
|
||||
.isEqualTo(expectedClippedInsets)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun getInsetsToDrawInFullscreen_doNotClipTaskbarSizeFromBottomForPhones() {
|
||||
fun setFullProgress_currentDrawnInsets_doNotClipTaskbarSizeFromBottomForPhones() {
|
||||
initializeVarsForPhone()
|
||||
val dp = newDP()
|
||||
val previewRect = Rect(0, 0, 100, 100)
|
||||
@@ -67,10 +78,13 @@ class TaskThumbnailViewTest : DeviceProfileBaseTest() {
|
||||
val isRtl = false
|
||||
|
||||
mPreviewPositionHelper.updateThumbnailMatrix(previewRect, mThumbnailData, canvasWidth,
|
||||
canvasHeight, dp, currentRotation, isRtl)
|
||||
canvasHeight, dp.widthPx, dp.taskbarSize, dp.isTablet, currentRotation,
|
||||
isRtl)
|
||||
params.setProgress(/* fullscreenProgress= */ 1.0f, /* parentScale= */ 1.0f,
|
||||
/* taskViewScale= */ 1.0f, /* previewWidth= */ 0, dp, mPreviewPositionHelper)
|
||||
|
||||
val expectedClippedInsets = RectF(0f, 0f, 0f, 0f)
|
||||
assertThat(mPreviewPositionHelper.getInsetsToDrawInFullscreen(dp))
|
||||
assertThat(params.mCurrentDrawnInsets)
|
||||
.isEqualTo(expectedClippedInsets)
|
||||
}
|
||||
}
|
||||
@@ -818,16 +818,6 @@ public final class Utilities {
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Compares the ratio of two quantities and returns whether that ratio is greater than the
|
||||
* provided bound. Order of quantities does not matter. Bound should be a decimal representation
|
||||
* of a percentage.
|
||||
*/
|
||||
public static boolean isRelativePercentDifferenceGreaterThan(float first, float second,
|
||||
float bound) {
|
||||
return (Math.abs(first - second) / Math.abs((first + second) / 2.0f)) > bound;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rotates `inOutBounds` by `delta` 90-degree increments. Rotation is visually CCW. Parent
|
||||
* sizes represent the "space" that will rotate carrying inOutBounds along with it to determine
|
||||
|
||||
Reference in New Issue
Block a user