Sitelet https://github.com/Nothing-Developer-Programme/GlyphMatrix-Developer-Kit/issues/4
Skip to content

GlyphMatrixObject.Builder()'s transformation functions behaving unhelpfully. #4

Description

@PixHammer

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.

val pixelSize:Int = (MATRIX_SIZE * currentScaleFactor).roundToInt(); // currentScaleFactor = 0.75
val imageObject = GlyphMatrixObject.Builder()
    .setImageSource(bitmap)
    .setScale((100.0 * currentScaleFactor).roundToInt())
    .setPosition(CENTER-(pixelSize/2),CENTER-(pixelSize/2))
    .setOrientation(currentRotation)
    .build()
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.

Activity

  1. PixHammer commented on Jul 22, 2025

    @PixHammer
    Author

    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.

  2. PixHammer commented on Jul 26, 2025

    @PixHammer
    Author

    pretty 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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions