Matrix useful for physics, collision and loads more

Jimmy

Active Member
Tutorial Author
Joined
Jan 2, 2020
Cerberus X:
Import mojo2
#MOJO_AUTO_SUSPEND_ENABLED=False

Function Main()
    New MyApp
End Function

Class MyApp Extends App

    Field myMatrix : Matrix
    Field myPoint : Point
    Field myRect : Rectangle
    Field origin : Point
    Field debugOn : Bool = False
    Field canvas : Canvas

    Method OnCreate()
        canvas = New Canvas
         ' Vertical blanking
        SetSwapInterval 1 ; SetUpdateRate 0   
        ' Creates a new identity matrix
        myMatrix = New Matrix()
        ' Creates a new point
        myPoint = New Point()
        ' Creates a rectangle
        myRect = New Rectangle(-100,-100,200,200)
        ' Stores origin
        origin = New Point(DeviceWidth() / 2.0,DeviceHeight() / 2.0)
    End Method
   
    Method OnUpdate()
        canvas.Clear 0,0,0   
       
        ' Translate matrix
        If KeyDown(KEY_UP) Then myMatrix.translateBy(0,-1)
        If KeyDown(KEY_DOWN) THen myMatrix.translateBy(0,1)   
        If KeyDown(KEY_LEFT) Then myMatrix.translateBy(-1,0)
        If KeyDown(KEY_RIGHT) Then myMatrix.translateBy(1,0)

        ' Scale matrix
        If KeyDown(KEY_N) Then myMatrix.scaleBy(1.01,1.01)
        If KeyDown(KEY_M) Then myMatrix.scaleBy(0.99,0.99)
       
        ' Scale matrix (free dimensions)
        If KeyDown(KEY_W) Then myMatrix.scaleBy(1,1.1)
        If KeyDown(KEY_S) Then myMatrix.scaleBy(1,0.9)
        If KeyDown(KEY_A) Then myMatrix.scaleBy(1.1,1)
        If KeyDown(KEY_D) Then myMatrix.scaleBy(0.9,1)

        ' Rotate matrix
        If KeyDown(KEY_Z) Then myMatrix.rotateBy(-1)
        If KeyDown(KEY_X) Then myMatrix.rotateBy(1)   
       
        ' Shear matrix
        If KeyDown(KEY_1) Then myMatrix.shearBy(0.1,0)
        If KeyDown(KEY_2) Then myMatrix.shearBy(-0.1,0)
        If KeyDown(KEY_3) Then myMatrix.shearBy(0,0.1)
        If KeyDown(KEY_4) Then myMatrix.shearBy(0,-0.1)
       
    End Method
   
    Method OnRender()
        canvas.Clear 0,0,0
        canvas.PushMatrix
        canvas.Translate origin.mX,origin.mY
        canvas.Transform myMatrix.mA,myMatrix.mB,myMatrix.mC,myMatrix.mD,myMatrix.mTx,myMatrix.mTy
        canvas.Translate  -origin.mX,-origin.mY
        canvas.SetColor 1,1,1
        canvas.DrawRect origin.mX + myRect.mX,origin.mY + myRect.mY,myRect.mWidth,myRect.mHeight
        canvas.PopMatrix
        myPoint.mX = TouchX() - origin.mX
        myPoint.mY = TouchY() - origin.mY
       
        ' Inverse matrix
        Local invMatrix : Matrix = myMatrix.clone()
        invMatrix.invert()
               
        ' Transforms point
        Local testPoint : Point = invMatrix.transformPoint(myPoint)

        ' Draws debug stuff
        If debugOn = True

            ' Draws original outline rectangle shape
            canvas.SetColor 0,0,1
            canvas.DrawLine origin.mX + myRect.mX,origin.mY + myRect.mY,origin.mX + myRect.mWidth / 2.0,origin.mY + myRect.mY
            canvas.DrawLine origin.mX + myRect.mX,origin.mY + myRect.mHeight / 2.0,origin.mX + myRect.mWidth / 2.0,origin.mY + myRect.mHeight / 2.0
            canvas.DrawLine origin.mX + myRect.mX,origin.mY + myRect.mY,origin.mX + myRect.mX,origin.mY + myRect.mHeight / 2.0
            canvas.DrawLine origin.mX + myRect.mWidth / 2.0,origin.mY + myRect.mY,origin.mX + myRect.mWidth / 2.0,origin.mY + myRect.mHeight / 2.0
            canvas.SetColor 1,1,1
            canvas.DrawText testPoint.mX + "," + testPoint.mY,0,0

            ' Draws test marker
            canvas.SetColor 1,0,0
            canvas.DrawOval origin.mX + testPoint.mX - 4,origin.mY + testPoint.mY - 4,8,8
        Endif

        ' Checks if touching inside the shape
        If TouchDown(0)
            ' Simple rectangle check as the point has been transformed
            If myRect.containsPoint(testPoint) Then canvas.SetColor 1,0,0 ; canvas.DrawText "Point Inside Rect!",origin.mX,50,0.5,0.5
        Endif
        canvas.Flush
    End Method
   
End Class

' The Point Class describes a two dimensional point Or vector.

Class Point

    Field mX : Float
    Field mY : Float

    ' Initializes a point with its x And y components. _Designated Initializer_.
    Method New(x : Float,y : Float)
        Self.mX = x
        Self.mY = y
    End Method

    Function pointWithPolarLength : Point(length : Float,angle : Float)
        Return New Point(Cos(angle) * length,Sin(angle) * length)
    End Function

    Function pointWithXY : Point(x : Float,y : Float)
        Return New Point(x,y)
    End Function

    Function pointZero : Point()
        Return New Point(0,0)
    End Function
   
    ' Calculates the distance between two points.
    Function distanceFromPoint : Float(p1 : Point,p2 : Point)
        Local diff : Point = p1.subtractPoint(p2)
        Return Sqr((diff.mX * diff.mX) + (diff.mY * diff.mY))
    End Function
   
    ' Determines a point between two specified points. `ratio = 0 -> p1,ratio = 1 -> p2
    Function interpolateFromPoint : Point(p1 : Point,p2 : Point,ratio : Float)
        Local invRation = 1.0 - ratio
        Local x : Float = (invRatio * p1.mX) + (ratio * p2.mX)
        Local y : Float = (invRatio * p1.mY) + (ratio * p2.mY)
        Return New Point(x,y)
    End Function
   
    ' Clones point
    Method clone : Point()
        Return New Point(Self.mX,Self.mY)
    End Method

    ' Length of vector
    Method length : Float()
        Return Sqr((Self.mX * Self.mX) + (Self.mY * Self.mY))
    End Method

    ' Angle of vector
    Method angle : Float()
            Return ATan2(Self.mY,Self.mX)
    End Method
   
    ' Adds a point To the current point And returns the resulting point.
    Method addPoint : Point(point : Point)
        Return New Point(point.mX + Self.mY,point.mY + Self.mY)
    End Method

    ' Substracts a point from the current point And returns the resulting point.
    Method subtractPoint : Point(point : Point)
        Return New Point(point.mX - Self.mX,point.mY - Self.mY)
    End Method

    ' Scales the point by a certain factor And returns the resulting point.
    Method scaleBy : Point(scalar : Float)
        Return New Point(Self.mX * scalar,Self.mY * scalar)
    End Method
   
    ' Scales the line segment between the origin And the current point To one.
    Method normalize : Point()
        If Self.mX = 0 And Self.mY = 0
            Error("Cannot mormalize a point in the origin")
        Else
            Local inverseLength : Float = 1.0 / Self.length()
            Return New Point(Self.mX * inverseLength,Self.mY * inverseLength)
        Endif
    End Method
   
    ' Compares two points.
    Method isEqual : Bool(other : Point)
            If other = Self Then Return True
        Return (Self.mX = point.mX) And (Self.mY = point.mY)   
     End Method

    ' Calculates the distance between two points.
    Method distanceFromPoint : Float(point : Point)
        Local diff : Point = Self.subtractPoint(point)
        Return Sqr((diff.mX * diff.mX) + (diff.mY * diff.mY))
    End Method

    ' Determines a point between two specified points. `ratio = 0 -> self,ratio = 1 -> point
    Method interpolateFromPoint : Point(point : Point,ratio : Float)
        Local invRation = 1.0 - ratio
        Local x : Float = (invRatio * Self.mX) + (ratio * point.mX)
        Local y : Float = (invRatio * Self.mY) + (ratio * point.mY)
        Return New Point(x,y)
    End Method

    ' Returns a string containing the components of the point
    Method description : String()
        Return "(x: " + Self.mX + ",y: " + Self.mY + ")"
    End Method
   
End Class

' The Rectangle Class describes a rectangle by its top-left corner point (x,y) And by its width And height.

Class Rectangle

    Field mX : Float
    Field mY : Float
    Field mWidth : Float
    Field mHeight : Float

    ' Initializes a rectangle
    Method New()
        Self.mX = 0
        Self.mY = 0
        Self.mWidth = 0
        Self.mHeight = 0
    End Method
   
    ' Initializes a rectangle with the specified components
    Method New(x : Float,y : Float,width : Float,height : Float)
        Self.mX = x
        Self.mY = y
        Self.mWidth = width
        Self.mHeight = height
    End Method
   
    Function rectangleWithValues : Rectangle(x : Float,y : Float,width : Float,height : Float)
        Return New Rectangle(x,y,width,height)
    End Function
   
    ' Clone rectangle
    Method clone : Rectangle()
        Return New Rectangle(Self.mX,Self.mY,Self.mWidth,Self.mHeight)
    End Method

    ' Determines If a point is within the rectangle.
    Method containsXY : Bool(x : Float,y : Float)
        Return (x >= mX) And (y >= mY) And (x <= mX + mWidth) And (y <= mY + mHeight)
    End Method
   
    ' Determines If a point is within the rectangle.
    Method containsPoint : Bool(point : Point)
        Return Self.containsXY(point.mX,point.mY)
    End Method
   
    ' Determines If another rectangle is within the rectangle.
    Method containsRectangle : Bool(rectangle : Rectangle)
        Local rX : Float = rectangle.mX
        Local rY : Float = rectangle.mY
        Local rWidth : Float = rectangle.mWidth
        Local rHeight : Float = rectangle.mHeight
        Return (rX >= Self.mX) And (rX + rWidth <= mX + mWidth) And (rY >= mY) And (rY + rHeight <= mY + mHeight)
    End Method
   
    ' Determines If another rectangle contains Or intersects the rectangle.
    Method intersectsRectangle:Bool(rectangle : Rectangle)
        Local rX : Float = rectangle.mX
        Local rY : Float = rectangle.mY
        Local rWidth : Float = rectangle.mWidth
        Local rHeight : Float = rectangle.mHeight
        Local outside : Bool = (rX <= Self.mX And rX + rWidth <= Self.mX) Or (rX >= Self.mX + Self.mWidth And rX + rWidth >= Self.mX + Self.mWidth) Or (rY <= Self.mY And rY + rHeight <= Self.mY) Or (rY >= Self.mY + Self.mHeight And rY + rHeight >= Self.mY + Self.mHeight)
        Return Not outside
    End Method
   
    ' If the specified rectangle intersects with the rectangle,returns the area of intersection.
    Method intersectionWithRectangle : Rectangle(rectangle : Rectangle)
        Local left : Float = Max(Self.mX,rectangle.mX)
        Local right : Float = Min(Self.mX + Self.mWidth,rectangle.mX + rectangle.mWidth)
        Local top : Float = Max(Self.mY,rectangle.mY)
        Local bottom : Float = Min(Self.mY + Self.mHeight,rectangle.mY + rectangle.mHeight)
        If (left > right Or top > bottom)
            Return New Rectangle(0,0,0,0)
        Else
            Return New Rectangle(left,top,right - left,bottom - top)
        Endif
    End Method
   
    ' Adds two rectangles together To create a New Rectangle Object (by filling in the space between the two rectangles).
    Method uniteWithRectangle : Rectangle(rectangle : Rectangle)
        Local left : Float = Min(Self.mX,rectangle.mX)
        Local right : Float = Max(Self.mX + Self.mWidth,rectangle.mX + rectangle.mWidth)
        Local top : Float = Min(Self.mY,rectangle.mY)
        Local bottom : Float = Max(Self.mY + Self.mHeight,rectangle.mY + rectangle.mHeight)
        Return New Rectangle(left,top,right - left,bottom - top)
    End Method
   
    ' Sets x,y,width And height components To zero.
    Method setEmpty()
        Self.mX = 0
        Self.mY = 0
        Self.mWidth = 0
        Self.mHeight = 0
    End Method

    ' Checks if width or height components are zero.
    Method isEmpty : Bool()
        Return (Self.mWidth = 0) Or (Self.mHeight = 0)
    End Method

    ' Checks if two rectangles are equal
    Method isEqual : Bool(rectangle : Rectangle)
        If rectangle = Self Then Return True
        Return (Self.mX = rectangle.mX) And (Self.mY = rectangle.mY) And (Self.mWidth = rectangle.mWidth) And (Self.mHeight = rectangle.mHeight)          
    End Method
   
    ' Returns a string containing the components of the rectangle
    Method description : String()
        Return "(x: " + Self.mX + ",y: " + Self.mY + " width: " + Self.mWidth + " height: " + Self.mHeight + ")"
    End Method

End Class

' ------------------------------------------------------------------------------------------------
'  The Matrix Class describes an affine 2D transformation Matrix. It provides methods To
'  manipulate the matrix in convenient ways, and can be used To transform points.
' 
'  The matrix has the following form:
'      |a c tx|
'      |b d ty|
'      |0 0  1|
' -------------------------------------------------------------------------------------------------

Class Matrix

     Field mA : Float
    Field mB : Float
    Field mC : Float
    Field mD : Float
     Field mTx : Float
    Field mTy : Float

    ' Initializes an identity matrix.
    Method New()
        Self.mA = 1.0
        Self.mB = 0.0
        Self.mC = 0.0
        Self.mD = 1.0
        Self.mTx = 0.0
        Self.mTy = 0.0       
    End Method
   
    ' Initializes a matrix with the specified components.
    Method New(a : Float,b : Float,c : Float,d : Float,tx : Float,ty : Float)
        Self.mA = a
        Self.mB = b
        Self.mC = c
        Self.mD = d
        Self.mTx = tx
        Self.mTy = ty
    End Method

    Function matrixWithValues : Matrix(a : Float,b : Float,c : Float,d : Float,tx : Float,ty : Float)
        Return New Matrix(a,b,c,d,tx,ty)
    End Function
   
    Function matrixWithIdentity : Matrix()
        Return New Matrix()
    End Function

    ' Setter
    Function setValues(matrix : Matrix,a : Float,b : Float,c : Float,d : Float,tx : Float,ty : Float)
        matrix.mA = a
        matrix.mB = b
        matrix.mC = c
        matrix.mD = d
        matrix.mTx = tx
        matrix.mTy = ty
    End Function
   
    ' Compares two matrices.
    Method isEqual : Bool(other : Matrix)
        If other = Self Then Return True
        Return (other.mA = Self.mA) And (other.mB = Self.mB) And (other.mC = Self.mC) And (other.mD = Self.mD) And (other.mTx = Self.mTx) And (other.mTy = Self.mTy)
    End Method

    ' Clone matrix
    Method clone : Matrix()
        Return New Matrix(Self.mA,Self.mB,Self.mC,Self.mD,Self.mTx,Self.mTy)
    End Method
   
    ' Sets a matrix with the with the specified components
    Method setValues(a : Float,b : Float,c : Float,d : Float,tx : Float,ty : Float)
        Self.mA = a
        Self.mB = b
        Self.mC = c
        Self.mD = d
        Self.mTx = tx
        Self.mTy = ty
    End Method

    ' Concatenates a matrix with the current matrix,combining the geometric effects of the two.
    Method concatMatrix : Void(matrix : Matrix)
        Local a : Float = matrix.mA * Self.mA  + matrix.mC * Self.mB
        Local b : Float = matrix.mB * Self.mA  + matrix.mD * Self.mB
        Local c : Float = matrix.mA * Self.mC  + matrix.mC * Self.mD
        Local d : Float = matrix.mB * Self.mC  + matrix.mD * Self.mD
        Local tx : Float = matrix.mA * Self.mTx + matrix.mC * Self.mTy + matrix.mTx
        Local ty : Float = matrix.mB * Self.mTx + matrix.mD * Self.mTy + matrix.mTy
        Self.setValues(a,b,c,d,tx,ty)
    End Method
   
    ' Determinate of matrix
    Method determinant : Float()
            Return (Self.mA * Self.mD) - (Self.mC * Self.mB)
    End Method
   
    ' Translates the matrix along the x And y axes.
    Method translateBy : Void(dx : Float,dy : Float)
        Self.mTx = Self.mTx + dx
        Self.mTy = Self.mTy + dy
    End Method

    ' Applies a scaling transformation To the matrix.
    Method scaleBy : Void(sx : Float,sy : Float)
        Self.mA = Self.mA * sx
        Self.mB = Self.mB * sy
        Self.mC = Self.mC * sx
        Self.mD = Self.mD * sy
        Self.mTx = Self.mTx * sx
        Self.mTy = Self.mTy * sy
    End Method

    ' Applies a uniform scaling transformation To the matrix.
    Method scaleBy : Void(scale : Float)
        Self.scaleBy(scale,scale)
    End Method

    ' Applies a rotation on the matrix.
    Method rotateBy : Void(angle : Float)
        Local rotMatrix : Matrix = New Matrix(Cos(angle),Sin(angle),-Sin(angle),Cos(angle),0,0)
        Self.concatMatrix(rotMatrix)
    End Method
   
    ' Applies a shearing transformation To the matrix.
    Method shearBy : Void(sx : Float,sy : Float)   
        Self.mC = Self.mC + sx
        Self.mB = Self.mB + sy
    End Method

    ' Applies a uniform shearing transformation To the matrix.
    Method shearBy : Void(shear : Float)   
        Self.mC = Self.mC * shear
        Self.mB = Self.mB * shear
    End Method

    ' Sets each matrix Property To a value that causes a Null transformation.
    Method identity : Void()
        Self.setValues(1.0,0.0,0.0,1.0,0.0,0.0)
    End Method
   
    ' Performs the opposite transformation of the matrix.
    Method invert : Void()
        Local det : Float = Self.determinant()
        Local a : Float = Self.mD / det
        Local b : Float = -Self.mB / det
        Local c : Float = -Self.mC / det
        Local d : Float = Self.mA / det
        Local tx : Float = ((Self.mC * Self.mTy) - (Self.mD * Self.mTx)) / det
        Local ty : Float = ((Self.mB * Self.mTx) - (Self.mA * Self.mTy)) / det
        Self.setValues(a,b,c,d,tx,ty)
    End Method

    ' Applies the geometric transformation represented by the matrix To the specified point.
    Method transformPoint : Point(point : Point)
        Local x : Float = (Self.mA * point.mX) + (Self.mC * point.mY) + Self.mTx
        Local y : Float = (Self.mB * point.mX) + (Self.mD * point.mY) + Self.mTy
        Return New Point(x,y)
    End Method
   
    ' Returns a string containing the components of the matrix
    Method description : String()
        Return "(a = " + Self.mA + "," + "b = " + Self.mB + "," + "c = " + Self.mC + "," + "d = " + Self.mD + "," + "tx = " + Self.mTx + "," + "ty = " + Self.mTy + ")"
    End Method

End Class
 
Last edited:
Top Bottom