Extract PreviewPositionHelper to shared library
Moves Launcher's PreviewPositionHelper to shared library between SystemUI and Launcher to reuse it in the future in the partial screensharing recents selector. There should be no functional changes in the code itself. Bug: 240924926 Test: presubmit Change-Id: Ib38b6f9db91e63a2598bf81229e3cd3e1a49ca60
This commit is contained in:
parent
5e47e78a17
commit
cbfb35edc7
|
@ -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
|
||||
|
|
Loading…
Reference in New Issue