github.com/richardwilkes/toolbox@v1.121.0/xmath/geom/matrix.go (about)

     1  // Copyright (c) 2016-2024 by Richard A. Wilkes. All rights reserved.
     2  //
     3  // This Source Code Form is subject to the terms of the Mozilla Public
     4  // License, version 2.0. If a copy of the MPL was not distributed with
     5  // this file, You can obtain one at http://mozilla.org/MPL/2.0/.
     6  //
     7  // This Source Code Form is "Incompatible With Secondary Licenses", as
     8  // defined by the Mozilla Public License, version 2.0.
     9  
    10  package geom
    11  
    12  import (
    13  	"fmt"
    14  
    15  	"github.com/richardwilkes/toolbox/xmath"
    16  	"golang.org/x/exp/constraints"
    17  )
    18  
    19  // Matrix provides a 2D matrix.
    20  type Matrix[T constraints.Float] struct {
    21  	ScaleX T `json:"scale_x"`
    22  	SkewX  T `json:"skew_x"`
    23  	TransX T `json:"trans_x"`
    24  	SkewY  T `json:"skew_y"`
    25  	ScaleY T `json:"scale_y"`
    26  	TransY T `json:"trans_y"`
    27  }
    28  
    29  // NewIdentityMatrix creates a new identity transformation Matrix.
    30  func NewIdentityMatrix[T constraints.Float]() Matrix[T] {
    31  	return Matrix[T]{ScaleX: 1, ScaleY: 1}
    32  }
    33  
    34  // NewTranslationMatrix creates a new Matrix that translates by 'tx' and 'ty'.
    35  func NewTranslationMatrix[T constraints.Float](tx, ty T) Matrix[T] {
    36  	return Matrix[T]{ScaleX: 1, ScaleY: 1, TransX: tx, TransY: ty}
    37  }
    38  
    39  // NewScaleMatrix creates a new Matrix that scales by 'sx' and 'sy'.
    40  func NewScaleMatrix[T constraints.Float](sx, sy T) Matrix[T] {
    41  	return Matrix[T]{ScaleX: sx, ScaleY: sy}
    42  }
    43  
    44  // NewRotationMatrix creates a new Matrix that rotates by 'radians'. Positive values are clockwise.
    45  func NewRotationMatrix[T constraints.Float](radians T) Matrix[T] {
    46  	s := xmath.Sin(radians)
    47  	c := xmath.Cos(radians)
    48  	return Matrix[T]{ScaleX: c, SkewX: -s, SkewY: s, ScaleY: c}
    49  }
    50  
    51  // NewRotationByDegreesMatrix creates a new Matrix that rotates by 'degrees'. Positive values are clockwise.
    52  func NewRotationByDegreesMatrix[T constraints.Float](degrees T) Matrix[T] {
    53  	return NewRotationMatrix(degrees * xmath.DegreesToRadians)
    54  }
    55  
    56  // Translate returns a new Matrix which is a copy of this Matrix translated by 'tx' and 'ty'.
    57  func (m Matrix[T]) Translate(tx, ty T) Matrix[T] {
    58  	return Matrix[T]{
    59  		ScaleX: m.ScaleX,
    60  		SkewX:  m.SkewX,
    61  		TransX: m.TransX + tx,
    62  		SkewY:  m.SkewY,
    63  		ScaleY: m.ScaleY,
    64  		TransY: m.TransY + ty,
    65  	}
    66  }
    67  
    68  // Scale returns a new Matrix which is a copy of this Matrix scaled by 'sx' and 'sy'.
    69  func (m Matrix[T]) Scale(sx, sy T) Matrix[T] {
    70  	return Matrix[T]{
    71  		ScaleX: m.ScaleX * sx,
    72  		SkewX:  m.SkewX * sx,
    73  		TransX: m.TransX * sx,
    74  		SkewY:  m.SkewY * sy,
    75  		ScaleY: m.ScaleY * sy,
    76  		TransY: m.TransY * sy,
    77  	}
    78  }
    79  
    80  // Rotate returns a new Matrix which is a copy of this Matrix rotated by 'radians'. Positive values are clockwise.
    81  func (m Matrix[T]) Rotate(radians T) Matrix[T] {
    82  	s := xmath.Sin(radians)
    83  	c := xmath.Cos(radians)
    84  	return Matrix[T]{
    85  		ScaleX: m.ScaleX*c - s*m.SkewY,
    86  		SkewX:  m.SkewX*c - s*m.ScaleY,
    87  		TransX: m.TransX*c - s*m.TransY,
    88  		SkewY:  m.ScaleX*s + m.SkewY*c,
    89  		ScaleY: m.SkewX*s + m.ScaleY*c,
    90  		TransY: m.TransX*s + m.TransY*c,
    91  	}
    92  }
    93  
    94  // RotateByDegrees returns a new Matrix which is a copy of this Matrix rotated by 'degrees'. Positive values are clockwise.
    95  func (m Matrix[T]) RotateByDegrees(degrees T) Matrix[T] {
    96  	return m.Rotate(degrees * xmath.DegreesToRadians)
    97  }
    98  
    99  // Multiply returns this Matrix multiplied by the other Matrix.
   100  func (m Matrix[T]) Multiply(other Matrix[T]) Matrix[T] {
   101  	return Matrix[T]{
   102  		ScaleX: m.ScaleX*other.ScaleX + m.SkewY*other.SkewX,
   103  		SkewX:  m.SkewX*other.ScaleX + m.ScaleY*other.SkewX,
   104  		TransX: m.TransX*other.ScaleX + m.TransY*other.SkewX + other.TransX,
   105  		SkewY:  m.ScaleX*other.SkewY + m.SkewY*other.ScaleY,
   106  		ScaleY: m.SkewX*other.SkewY + m.ScaleY*other.ScaleY,
   107  		TransY: m.TransX*other.ScaleX + m.TransY*other.SkewX + other.TransX,
   108  	}
   109  }
   110  
   111  // TransformPoint returns the result of transforming the given Point by this Matrix.
   112  func (m Matrix[T]) TransformPoint(p Point[T]) Point[T] {
   113  	return Point[T]{X: m.ScaleX*p.X + m.SkewX*p.Y + m.TransX, Y: m.SkewY*p.X + m.ScaleY*p.Y + m.TransY}
   114  }
   115  
   116  // String implements fmt.Stringer.
   117  func (m Matrix[T]) String() string {
   118  	return fmt.Sprintf("%#v,%#v,%#v,%#v,%#v,%#v", m.ScaleX, m.SkewX, m.TransX, m.SkewY, m.ScaleY, m.TransY)
   119  }