Repository navigation
GlyphMatrixObject.Builder()'s transformation functions behaving unhelpfully. #4
Description
Activity
Until this is fixed, others can calculate this extra offset with the following in order to maintain your position with arbitrary rotations, but it's only needed at non 100% scales.
private fun rotationCorrectionOffset(widthInPixels: Double, angle: Int): Double{ // need radians val angleRad = Math.toRadians(angle.toDouble()); val cosAngle = abs(cos(angleRad)); val sinAngle = abs(sin(angleRad)); // size after extra rotation - since our images are always 1:1 we only need to do this once val newWidth = (widthInPixels * cosAngle + widthInPixels * sinAngle); // get just the extra val extraWidth = newWidth - widthInPixels; // The offset is half of the extra space (to center the image) val offset = extraWidth / 2.0; return offset; }Round down to int after you subtract this from any other double position representations you have.
Reacted by SebiAi and KenFeng04pretty big correction! My previous code has an issue, it appears when the rotated object's canvas exceeds 25x25 pixels, it is clamped to that size! This means that my code will not work on any rotated canvas that exceeds 25x25, here is a corrected version that will account for this:
// gets the position offset that must be applied to a rotated GlyphMatrixObject in order to maintain a consistent on screen position // scaleFactor here is a double representation of the image scale, with 100% scale being 1.0 private fun rotationCorrectionOffset(angle: Int, scaleFactor: Double): Double { // canvas size before rotation var oldPixelSize: Double = MATRIX_SIZE * scaleFactor; if (oldPixelSize > MATRIX_SIZE) { oldPixelSize = MATRIX_SIZE.toDouble() } // need to work in radians val angleRad: Double = Math.toRadians(angle.toDouble()) var cosAngle: Double = abs(cos(angleRad)) var sinAngle: Double = abs(sin(angleRad)) // figure out the new pixel size after rotation var newScale: Double = (cosAngle + sinAngle) var newPixelSize: Double = MATRIX_SIZE * newScale * scaleFactor; if (newPixelSize > MATRIX_SIZE) { newPixelSize = MATRIX_SIZE.toDouble() } // offset is half of the difference in canvas size after rotation return (newPixelSize - oldPixelSize) * 0.5 }A little frustrating to have to figure this out through trial and error! All in all, it looks like the internal canvas size of GlyphMatrixObject's can never exceed 25x25!
You can use this as follows:
// Function to create a glyph matrix object of a drawable, transformed and centered private fun renderDrawableCentred( drawable: Drawable, xPos: Int, yPos: Int, rotation: Int, scaleFactor: Double, ): GlyphMatrixObject { // get the bitmap from the drawable val bitmap = GlyphMatrixUtils.drawableToBitmap(drawable) // offset needed to centre the image val pixelExtent = (MATRIX_SIZE * scaleFactor * 0.5).toInt() // Calculate the offset needed to compensate for rotation bounds expansion var rotationOffset = rotationCorrectionOffset(rotation, scaleFactor).roundToInt() val outXPosition = xPos - pixelExtent - rotationOffset val outYPosition = yPos - pixelExtent - rotationOffset // create image object return GlyphMatrixObject.Builder() .setImageSource(bitmap) .setScale((100.0 * scaleFactor).roundToInt()) .setPosition(outXPosition, outYPosition) .setOrientation(rotation) .build() }MATRIX_SIZE = 25
While I am appreciating the extra utilities for rotating and scaling images, setOrientation is causing quite a bit of grief with non 100% scale, non 90 degree rotations.
At 100% scale it behaves as anticipated, rotating around the center of the image. However, for some reason, when using non 90 degree rotations and a non-100% scaling, the object is shifted partially to the right and down, and no longer rotates about the center of the object. (this is most likely due to the added canvas size to contain the bounds of the new rotated bitmap)
Attached is a video outlining my issue, this is a simple bitmap, rotating 5 degrees every frame.
VID20250723003916.mp4
It's likely that I could engineer a hack fix for this, by calculating the extra size that has been added by the rotation, and offsetting by half of that. However this is not a complete fix, as the problems only occurs at non-100% scales.