From d4558aefd50a1a83cdcca11e5b5e1164bbdb05e4 Mon Sep 17 00:00:00 2001 From: Hannes Achleitner Date: Fri, 26 Dec 2025 17:30:34 +0100 Subject: [PATCH] Utils Kotlin --- .../mikephil/charting/data/BaseDataSet.kt | 2 +- .../charting/renderer/XAxisRenderer.kt | 2 +- .../github/mikephil/charting/utils/Utils.java | 329 ----------------- .../github/mikephil/charting/utils/Utils.kt | 333 ++++++++++++++++++ 4 files changed, 335 insertions(+), 331 deletions(-) delete mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java create mode 100644 MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.kt diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.kt index cd64f94889..c291bfcbfc 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.kt +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/data/BaseDataSet.kt @@ -255,7 +255,7 @@ abstract class BaseDataSet() : IDataSet { override var valueFormatter: IValueFormatter get() = if (needsFormatter()) - Utils.getDefaultValueFormatter() + Utils.defaultValueFormatter else mValueFormatter!! set(value) { diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.kt index 2e13cfc45e..b1238eb03c 100644 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.kt +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/renderer/XAxisRenderer.kt @@ -222,7 +222,7 @@ open class XAxisRenderer( } protected fun drawLabel(canvas: Canvas, formattedLabel: String?, x: Float, y: Float, anchor: MPPointF, angleDegrees: Float) { - Utils.drawXAxisValue(canvas, formattedLabel, x, y, paintAxisLabels, anchor, angleDegrees) + formattedLabel?.let { Utils.drawXAxisValue(canvas, it, x, y, paintAxisLabels, anchor, angleDegrees) } } protected open var renderGridLinesPath: Path = Path() diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java deleted file mode 100644 index 9a303a4e11..0000000000 --- a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.java +++ /dev/null @@ -1,329 +0,0 @@ - -package com.github.mikephil.charting.utils; - -import android.content.Context; -import android.graphics.Canvas; -import android.graphics.Paint; -import android.graphics.Rect; -import android.graphics.drawable.Drawable; -import android.view.MotionEvent; -import android.view.VelocityTracker; -import android.view.ViewConfiguration; - -import com.github.mikephil.charting.formatter.DefaultValueFormatter; -import com.github.mikephil.charting.formatter.IValueFormatter; - -import androidx.annotation.NonNull; - -/** - * Utilities class that has some helper methods. Needs to be initialized by - * calling Utils.init(...) before usage. Inside the Chart.init() method, this is - * done, if the Utils are used before that, Utils.init(...) needs to be called - * manually. - * - * @author Philipp Jahoda - */ -@SuppressWarnings("JavaDoc") -public abstract class Utils { - - public static int minimumFlingVelocity = 50; - public static int maximumFlingVelocity = 8000; - public final static double DEG2RAD = (Math.PI / 180.0); - public final static float FDEG2RAD = ((float) Math.PI / 180.f); - - @SuppressWarnings("unused") - public final static double DOUBLE_EPSILON = Double.longBitsToDouble(1); - - @SuppressWarnings("unused") - public final static float FLOAT_EPSILON = Float.intBitsToFloat(1); - - /** - * initialize method, called inside the Chart.init() method. - */ - @SuppressWarnings("deprecation") - public static void init(@NonNull Context context) { - ViewConfiguration viewConfiguration = ViewConfiguration.get(context); - minimumFlingVelocity = viewConfiguration.getScaledMinimumFlingVelocity(); - maximumFlingVelocity = viewConfiguration.getScaledMaximumFlingVelocity(); - } - - /** - * calculates the approximate width of a text, depending on a demo text - * avoid repeated calls (e.g. inside drawing methods) - */ - public static int calcTextWidth(Paint paint, String demoText) { - return (int) paint.measureText(demoText); - } - - private static final Rect mCalcTextHeightRect = new Rect(); - - /** - * calculates the approximate height of a text, depending on a demo text - * avoid repeated calls (e.g. inside drawing methods) - */ - public static int calcTextHeight(Paint paint, String demoText) { - - Rect r = mCalcTextHeightRect; - r.set(0, 0, 0, 0); - paint.getTextBounds(demoText, 0, demoText.length(), r); - return r.height(); - } - - private static final Paint.FontMetrics mFontMetrics = new Paint.FontMetrics(); - - public static float getLineHeight(Paint paint) { - return getLineHeight(paint, mFontMetrics); - } - - public static float getLineHeight(Paint paint, Paint.FontMetrics fontMetrics) { - paint.getFontMetrics(fontMetrics); - return fontMetrics.descent - fontMetrics.ascent; - } - - public static float getLineSpacing(Paint paint) { - return getLineSpacing(paint, mFontMetrics); - } - - public static float getLineSpacing(Paint paint, Paint.FontMetrics fontMetrics) { - paint.getFontMetrics(fontMetrics); - return fontMetrics.ascent - fontMetrics.top + fontMetrics.bottom; - } - - /** - * Returns a recyclable FSize instance. - * calculates the approximate size of a text, depending on a demo text - * avoid repeated calls (e.g. inside drawing methods) - * - * @param paint - * @param demoText - * @return A Recyclable FSize instance - */ - public static FSize calcTextSize(Paint paint, String demoText) { - - FSize result = FSize.Companion.getInstance(0, 0); - calcTextSize(paint, demoText, result); - return result; - } - - private static final Rect mCalcTextSizeRect = new Rect(); - - /** - * calculates the approximate size of a text, depending on a demo text - * avoid repeated calls (e.g. inside drawing methods) - * - * @param paint - * @param demoText - * @param outputFSize An output variable, modified by the function. - */ - public static void calcTextSize(Paint paint, String demoText, FSize outputFSize) { - - Rect r = mCalcTextSizeRect; - r.set(0, 0, 0, 0); - paint.getTextBounds(demoText, 0, demoText.length(), r); - outputFSize.setWidth(r.width()); - outputFSize.setHeight(r.height()); - - } - - /** - * Math.pow(...) is very expensive, so avoid calling it and create it - * yourself. - */ - static final int[] POW_10 = { - 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 - }; - - private static final IValueFormatter mDefaultValueFormatter = generateDefaultValueFormatter(); - - private static IValueFormatter generateDefaultValueFormatter() { - return new DefaultValueFormatter(1); - } - - /// - returns: The default value formatter used for all chart components that needs a default - public static IValueFormatter getDefaultValueFormatter() { - return mDefaultValueFormatter; - } - - /** - * Returns a recyclable MPPointF instance. - * Calculates the position around a center point, depending on the distance - * from the center, and the angle of the position around the center. - * - * @param center - * @param dist - * @param angle in degrees, converted to radians internally - * @return - */ - public static MPPointF getPosition(MPPointF center, float dist, float angle) { - MPPointF p = MPPointF.Companion.getInstance(0, 0); - getPosition(center, dist, angle, p); - return p; - } - - public static void getPosition(MPPointF center, float dist, float angle, MPPointF outputPoint) { - outputPoint.setX((float) (center.getX() + dist * Math.cos(Math.toRadians(angle)))); - outputPoint.setY((float) (center.getY() + dist * Math.sin(Math.toRadians(angle)))); - } - - public static void velocityTrackerPointerUpCleanUpIfNecessary(MotionEvent ev, VelocityTracker tracker) { - - // Check the dot product of current velocities. - // If the pointer that left was opposing another velocity vector, clear. - tracker.computeCurrentVelocity(1000, maximumFlingVelocity); - final int upIndex = ev.getActionIndex(); - final int id1 = ev.getPointerId(upIndex); - final float x1 = tracker.getXVelocity(id1); - final float y1 = tracker.getYVelocity(id1); - for (int i = 0, count = ev.getPointerCount(); i < count; i++) { - if (i == upIndex) { - continue; - } - - final int id2 = ev.getPointerId(i); - final float x = x1 * tracker.getXVelocity(id2); - final float y = y1 * tracker.getYVelocity(id2); - - final float dot = x + y; - if (dot < 0) { - tracker.clear(); - break; - } - } - } - - /** - * returns an angle between 0.f < 360.f (not less than zero, less than 360) - */ - public static float getNormalizedAngle(float angle) { - while (angle < 0.f) { - angle += 360.f; - } - - return angle % 360.f; - } - - private static final Rect mDrawableBoundsCache = new Rect(); - - public static void drawImage(Canvas canvas, Drawable drawable, int x, int y) { - - int width = drawable.getIntrinsicWidth(); - int height = drawable.getIntrinsicHeight(); - - MPPointF drawOffset = MPPointF.Companion.getInstance(); - drawOffset.setX(x - (width / 2)); - drawOffset.setY(y - (height / 2)); - - drawable.copyBounds(mDrawableBoundsCache); - drawable.setBounds( - mDrawableBoundsCache.left, - mDrawableBoundsCache.top, - mDrawableBoundsCache.left + width, - mDrawableBoundsCache.top + width); - - int saveId = canvas.save(); - // translate to the correct position and draw - canvas.translate(drawOffset.getX(), drawOffset.getY()); - drawable.draw(canvas); - canvas.restoreToCount(saveId); - } - - private static final Rect mDrawTextRectBuffer = new Rect(); - private static final Paint.FontMetrics mFontMetricsBuffer = new Paint.FontMetrics(); - - public static void drawXAxisValue(Canvas canvas, String text, float x, float y, - Paint paint, - MPPointF anchor, float angleDegrees) { - - float drawOffsetX = 0.f; - float drawOffsetY = 0.f; - - final float lineHeight = paint.getFontMetrics(mFontMetricsBuffer); - paint.getTextBounds(text, 0, text.length(), mDrawTextRectBuffer); - - // Android sometimes has pre-padding - drawOffsetX -= mDrawTextRectBuffer.left; - - // Android does not snap the bounds to line boundaries, - // and draws from bottom to top. - // And we want to normalize it. - drawOffsetY -= mFontMetricsBuffer.ascent; - - // To have a consistent point of reference, we always draw left-aligned - Paint.Align originalTextAlign = paint.getTextAlign(); - paint.setTextAlign(Paint.Align.LEFT); - - if (angleDegrees != 0.f) { - - // Move the text drawing rect in a way that it always rotates around its center - drawOffsetX -= mDrawTextRectBuffer.width() * 0.5f; - drawOffsetY -= lineHeight * 0.5f; - - float translateX = x; - float translateY = y; - - // Move the "outer" rect relative to the anchor, assuming its centered - if (anchor.getX() != 0.5f || anchor.getY() != 0.5f) { - final FSize rotatedSize = getSizeOfRotatedRectangleByDegrees( - mDrawTextRectBuffer.width(), - lineHeight, - angleDegrees); - - translateX -= rotatedSize.getWidth() * (anchor.getX() - 0.5f); - translateY -= rotatedSize.getHeight() * (anchor.getY() - 0.5f); - FSize.Companion.recycleInstance(rotatedSize); - } - - canvas.save(); - canvas.translate(translateX, translateY); - canvas.rotate(angleDegrees); - - canvas.drawText(text, drawOffsetX, drawOffsetY, paint); - - canvas.restore(); - } else { - if (anchor.getX() != 0.f || anchor.getY() != 0.f) { - - drawOffsetX -= mDrawTextRectBuffer.width() * anchor.getX(); - drawOffsetY -= lineHeight * anchor.getY(); - } - - drawOffsetX += x; - drawOffsetY += y; - - canvas.drawText(text, drawOffsetX, drawOffsetY, paint); - } - - paint.setTextAlign(originalTextAlign); - } - - /** - * Returns a recyclable FSize instance. - * Represents size of a rotated rectangle by degrees. - * - * @param rectangleWidth - * @param rectangleHeight - * @param degrees - * @return A Recyclable FSize instance - */ - public static FSize getSizeOfRotatedRectangleByDegrees(float rectangleWidth, float rectangleHeight, float degrees) { - final float radians = degrees * FDEG2RAD; - return getSizeOfRotatedRectangleByRadians(rectangleWidth, rectangleHeight, radians); - } - - /** - * Returns a recyclable FSize instance. - * Represents size of a rotated rectangle by radians. - * - * @param rectangleWidth - * @param rectangleHeight - * @param radians - * @return A Recyclable FSize instance - */ - public static FSize getSizeOfRotatedRectangleByRadians(float rectangleWidth, float rectangleHeight, float radians) { - return FSize.Companion.getInstance( - Math.abs(rectangleWidth * (float) Math.cos(radians)) + Math.abs(rectangleHeight * (float) Math.sin(radians)), - Math.abs(rectangleWidth * (float) Math.sin(radians)) + Math.abs(rectangleHeight * (float) Math.cos(radians)) - ); - } - -} diff --git a/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.kt b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.kt new file mode 100644 index 0000000000..a90d8c7624 --- /dev/null +++ b/MPChartLib/src/main/java/com/github/mikephil/charting/utils/Utils.kt @@ -0,0 +1,333 @@ +package com.github.mikephil.charting.utils + +import android.content.Context +import android.graphics.Canvas +import android.graphics.Paint +import android.graphics.Paint.Align +import android.graphics.Rect +import android.graphics.drawable.Drawable +import android.view.MotionEvent +import android.view.VelocityTracker +import android.view.ViewConfiguration +import com.github.mikephil.charting.formatter.DefaultValueFormatter +import com.github.mikephil.charting.formatter.IValueFormatter +import com.github.mikephil.charting.utils.FSize.Companion.recycleInstance +import com.github.mikephil.charting.utils.MPPointF.Companion.instance +import java.lang.Double +import java.lang.Float +import kotlin.Int +import kotlin.IntArray +import kotlin.String +import kotlin.Suppress +import kotlin.intArrayOf +import kotlin.math.abs +import kotlin.math.cos +import kotlin.math.sin + +/** + * Utilities class that has some helper methods. Needs to be initialized by + * calling Utils.init(...) before usage. Inside the Chart.init() method, this is + * done, if the Utils are used before that, Utils.init(...) needs to be called + * manually. + * + * @author Philipp Jahoda + */ +object Utils { + var minimumFlingVelocity: Int = 50 + var maximumFlingVelocity: Int = 8000 + val DEG2RAD: kotlin.Double = (Math.PI / 180.0) + val FDEG2RAD: kotlin.Float = (Math.PI.toFloat() / 180f) + + @Suppress("unused") + val DOUBLE_EPSILON: kotlin.Double = Double.longBitsToDouble(1) + + @Suppress("unused") + val FLOAT_EPSILON: kotlin.Float = Float.intBitsToFloat(1) + + /** + * initialize method, called inside the Chart.init() method. + */ + @Suppress("deprecation") + fun init(context: Context) { + val viewConfiguration = ViewConfiguration.get(context) + minimumFlingVelocity = viewConfiguration.scaledMinimumFlingVelocity + maximumFlingVelocity = viewConfiguration.scaledMaximumFlingVelocity + } + + /** + * calculates the approximate width of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + */ + fun calcTextWidth(paint: Paint, demoText: String?): Int { + return paint.measureText(demoText).toInt() + } + + private val mCalcTextHeightRect = Rect() + + /** + * calculates the approximate height of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + */ + fun calcTextHeight(paint: Paint, demoText: String): Int { + val r = mCalcTextHeightRect + r.set(0, 0, 0, 0) + paint.getTextBounds(demoText, 0, demoText.length, r) + return r.height() + } + + private val mFontMetrics = Paint.FontMetrics() + + fun getLineHeight(paint: Paint): kotlin.Float { + return getLineHeight(paint, mFontMetrics) + } + + fun getLineHeight(paint: Paint, fontMetrics: Paint.FontMetrics): kotlin.Float { + paint.getFontMetrics(fontMetrics) + return fontMetrics.descent - fontMetrics.ascent + } + + fun getLineSpacing(paint: Paint): kotlin.Float { + return getLineSpacing(paint, mFontMetrics) + } + + fun getLineSpacing(paint: Paint, fontMetrics: Paint.FontMetrics): kotlin.Float { + paint.getFontMetrics(fontMetrics) + return fontMetrics.ascent - fontMetrics.top + fontMetrics.bottom + } + + /** + * Returns a recyclable FSize instance. + * calculates the approximate size of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @return A Recyclable FSize instance + */ + fun calcTextSize(paint: Paint, demoText: String): FSize { + val result = FSize.getInstance(0f, 0f) + calcTextSize(paint, demoText, result) + return result + } + + private val mCalcTextSizeRect = Rect() + + /** + * calculates the approximate size of a text, depending on a demo text + * avoid repeated calls (e.g. inside drawing methods) + * + * @param paint + * @param demoText + * @param outputFSize An output variable, modified by the function. + */ + fun calcTextSize(paint: Paint, demoText: String, outputFSize: FSize) { + val r = mCalcTextSizeRect + r.set(0, 0, 0, 0) + paint.getTextBounds(demoText, 0, demoText.length, r) + outputFSize.width = r.width().toFloat() + outputFSize.height = r.height().toFloat() + } + + /** + * Math.pow(...) is very expensive, so avoid calling it and create it + * yourself. + */ + val POW_10: IntArray = intArrayOf( + 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000, 100000000, 1000000000 + ) + + /** - returns: The default value formatter used for all chart components that needs a default */ + val defaultValueFormatter: IValueFormatter = generateDefaultValueFormatter() + + private fun generateDefaultValueFormatter(): IValueFormatter { + return DefaultValueFormatter(1) + } + + /** + * Returns a recyclable MPPointF instance. + * Calculates the position around a center point, depending on the distance + * from the center, and the angle of the position around the center. + * + * @param center + * @param dist + * @param angle in degrees, converted to radians internally + * @return + */ + fun getPosition(center: MPPointF, dist: kotlin.Float, angle: kotlin.Float): MPPointF { + val p = MPPointF.getInstance(0f, 0f) + getPosition(center, dist, angle, p) + return p + } + + fun getPosition(center: MPPointF, dist: kotlin.Float, angle: kotlin.Float, outputPoint: MPPointF) { + outputPoint.x = (center.x + dist * cos(Math.toRadians(angle.toDouble()))).toFloat() + outputPoint.y = (center.y + dist * sin(Math.toRadians(angle.toDouble()))).toFloat() + } + + fun velocityTrackerPointerUpCleanUpIfNecessary(ev: MotionEvent, tracker: VelocityTracker) { + // Check the dot product of current velocities. + // If the pointer that left was opposing another velocity vector, clear. + + tracker.computeCurrentVelocity(1000, maximumFlingVelocity.toFloat()) + val upIndex = ev.actionIndex + val id1 = ev.getPointerId(upIndex) + val x1 = tracker.getXVelocity(id1) + val y1 = tracker.getYVelocity(id1) + var i = 0 + val count = ev.pointerCount + while (i < count) { + if (i == upIndex) { + i++ + continue + } + + val id2 = ev.getPointerId(i) + val x = x1 * tracker.getXVelocity(id2) + val y = y1 * tracker.getYVelocity(id2) + + val dot = x + y + if (dot < 0) { + tracker.clear() + break + } + i++ + } + } + + /** + * returns an angle between 0.f < 360.f (not less than zero, less than 360) + */ + fun getNormalizedAngle(angle: kotlin.Float): kotlin.Float { + var angle = angle + while (angle < 0f) { + angle += 360f + } + + return angle % 360f + } + + private val mDrawableBoundsCache = Rect() + + fun drawImage(canvas: Canvas, drawable: Drawable, x: Int, y: Int) { + val width = drawable.intrinsicWidth + val height = drawable.intrinsicHeight + + val drawOffset = instance + drawOffset.x = x - (width / 2).toFloat() + drawOffset.y = y - (height / 2).toFloat() + + drawable.copyBounds(mDrawableBoundsCache) + drawable.setBounds( + mDrawableBoundsCache.left, + mDrawableBoundsCache.top, + mDrawableBoundsCache.left + width, + mDrawableBoundsCache.top + width + ) + + val saveId = canvas.save() + // translate to the correct position and draw + canvas.translate(drawOffset.x, drawOffset.y) + drawable.draw(canvas) + canvas.restoreToCount(saveId) + } + + private val mDrawTextRectBuffer = Rect() + private val mFontMetricsBuffer = Paint.FontMetrics() + + fun drawXAxisValue( + canvas: Canvas, text: String, x: kotlin.Float, y: kotlin.Float, + paint: Paint, + anchor: MPPointF, angleDegrees: kotlin.Float + ) { + var drawOffsetX = 0f + var drawOffsetY = 0f + + val lineHeight = paint.getFontMetrics(mFontMetricsBuffer) + paint.getTextBounds(text, 0, text.length, mDrawTextRectBuffer) + + // Android sometimes has pre-padding + drawOffsetX -= mDrawTextRectBuffer.left.toFloat() + + // Android does not snap the bounds to line boundaries, + // and draws from bottom to top. + // And we want to normalize it. + drawOffsetY -= mFontMetricsBuffer.ascent + + // To have a consistent point of reference, we always draw left-aligned + val originalTextAlign = paint.textAlign + paint.textAlign = Align.LEFT + + if (angleDegrees != 0f) { + // Move the text drawing rect in a way that it always rotates around its center + + drawOffsetX -= mDrawTextRectBuffer.width() * 0.5f + drawOffsetY -= lineHeight * 0.5f + + var translateX = x + var translateY = y + + // Move the "outer" rect relative to the anchor, assuming its centered + if (anchor.x != 0.5f || anchor.y != 0.5f) { + val rotatedSize = getSizeOfRotatedRectangleByDegrees( + mDrawTextRectBuffer.width().toFloat(), + lineHeight, + angleDegrees + ) + + translateX -= rotatedSize.width * (anchor.x - 0.5f) + translateY -= rotatedSize.height * (anchor.y - 0.5f) + recycleInstance(rotatedSize) + } + + canvas.save() + canvas.translate(translateX, translateY) + canvas.rotate(angleDegrees) + + canvas.drawText(text, drawOffsetX, drawOffsetY, paint) + + canvas.restore() + } else { + if (anchor.x != 0f || anchor.y != 0f) { + drawOffsetX -= mDrawTextRectBuffer.width() * anchor.x + drawOffsetY -= lineHeight * anchor.y + } + + drawOffsetX += x + drawOffsetY += y + + canvas.drawText(text, drawOffsetX, drawOffsetY, paint) + } + + paint.textAlign = originalTextAlign + } + + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by degrees. + * + * @param rectangleWidth + * @param rectangleHeight + * @param degrees + * @return A Recyclable FSize instance + */ + fun getSizeOfRotatedRectangleByDegrees(rectangleWidth: kotlin.Float, rectangleHeight: kotlin.Float, degrees: kotlin.Float): FSize { + val radians = degrees * FDEG2RAD + return getSizeOfRotatedRectangleByRadians(rectangleWidth, rectangleHeight, radians) + } + + /** + * Returns a recyclable FSize instance. + * Represents size of a rotated rectangle by radians. + * + * @param rectangleWidth + * @param rectangleHeight + * @param radians + * @return A Recyclable FSize instance + */ + fun getSizeOfRotatedRectangleByRadians(rectangleWidth: kotlin.Float, rectangleHeight: kotlin.Float, radians: kotlin.Float): FSize { + return FSize.getInstance( + abs(rectangleWidth * cos(radians.toDouble()).toFloat()) + abs(rectangleHeight * sin(radians.toDouble()).toFloat()), + abs(rectangleWidth * sin(radians.toDouble()).toFloat()) + abs(rectangleHeight * cos(radians.toDouble()).toFloat()) + ) + } +}