Class: PerfectShape::AffineTransform

Inherits:
Object
  • Object
show all
Defined in:
lib/perfect_shape/affine_transform.rb

Overview

Represents an affine transform

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(xxp_element = nil, xyp_element = nil, yxp_element = nil, yyp_element = nil, xt_element = nil, yt_element = nil, xxp: nil, xyp: nil, yxp: nil, yyp: nil, xt: nil, yt: nil, m11: nil, m12: nil, m21: nil, m22: nil, m13: nil, m23: nil) ⇒ AffineTransform

Creates a new AffineTransform with the following Matrix:

xxp xyp xt
yxp yyp yt

The Matrix is used to transform (x,y) point coordinates as follows:

xxp xyp xt
  • x

    [ xxp * x + xyp * y + xt ]

yxp yyp yt
  • y

    [ yxp * x + yyp * y + yt ]

xxp is the x coordinate x product (m11) xyp is the x coordinate y product (m12) yxp is the y coordinate x product (m21) yyp is the y coordinate y product (m22) xt is the x coordinate translation (m13) yt is the y coordinate translation (m23)

The constructor accepts either the (x,y)-operation related argument/kwarg names or traditional Matrix element kwarg names

Example with (x,y)-operation kwarg names:

AffineTransform.new(xxp: 2, xyp: 3, yxp: 4, yyp: 5, xt: 6, yt: 7)

Example with traditional Matrix element kwarg names:

AffineTransform.new(m11: 2, m12: 3, m21: 4, m22: 5, m13: 6, m23: 7)

Example with standard arguments:

AffineTransform.new(2, 3, 4, 5, 6, 7)

If no arguments are supplied, it constructs an identity matrix (i.e. like calling ‘::new(xxp: 1, xyp: 0, yxp: 0, yyp: 1, xt: 0, yt: 0)`)



74
75
76
77
78
79
80
81
82
83
# File 'lib/perfect_shape/affine_transform.rb', line 74

def initialize(xxp_element = nil, xyp_element = nil, yxp_element = nil, yyp_element = nil, xt_element = nil, yt_element = nil,
               xxp: nil, xyp: nil, yxp: nil, yyp: nil, xt: nil, yt: nil,
               m11: nil, m12: nil, m21: nil, m22: nil, m13: nil, m23: nil)
  self.xxp = xxp_element || xxp || m11 || 1
  self.xyp = xyp_element || xyp || m12 || 0
  self.yxp = yxp_element || yxp || m21 || 0
  self.yyp = yyp_element || yyp || m22 || 1
  self.xt  = xt_element  || xt  || m13 || 0
  self.yt  = yt_element  || yt  || m23 || 0
end

Instance Attribute Details

#xtObject Also known as: m13

Returns the value of attribute xt.



32
33
34
# File 'lib/perfect_shape/affine_transform.rb', line 32

def xt
  @xt
end

#xxpObject Also known as: m11

Returns the value of attribute xxp.



32
33
34
# File 'lib/perfect_shape/affine_transform.rb', line 32

def xxp
  @xxp
end

#xypObject Also known as: m12

Returns the value of attribute xyp.



32
33
34
# File 'lib/perfect_shape/affine_transform.rb', line 32

def xyp
  @xyp
end

#ytObject Also known as: m23

Returns the value of attribute yt.



32
33
34
# File 'lib/perfect_shape/affine_transform.rb', line 32

def yt
  @yt
end

#yxpObject Also known as: m21

Returns the value of attribute yxp.



32
33
34
# File 'lib/perfect_shape/affine_transform.rb', line 32

def yxp
  @yxp
end

#yypObject Also known as: m22

Returns the value of attribute yyp.



32
33
34
# File 'lib/perfect_shape/affine_transform.rb', line 32

def yyp
  @yyp
end

Instance Method Details

#identity!Object Also known as: reset!

Resets to identity matrix Returns self to support fluent interface chaining



117
118
119
120
121
122
123
124
125
126
# File 'lib/perfect_shape/affine_transform.rb', line 117

def identity!
  self.xxp = 1
  self.xyp = 0
  self.yxp = 0
  self.yyp = 1
  self.xt  = 0
  self.yt  = 0
  
  self
end

#inverse_transform_point(x_or_point, y = nil) ⇒ Object



219
220
221
# File 'lib/perfect_shape/affine_transform.rb', line 219

def inverse_transform_point(x_or_point, y = nil)
  clone.invert!.transform_point(x_or_point, y)
end

#inverse_transform_points(*xy_coordinates_or_points) ⇒ Object



229
230
231
232
233
# File 'lib/perfect_shape/affine_transform.rb', line 229

def inverse_transform_points(*xy_coordinates_or_points)
  points = xy_coordinates_or_points.first.is_a?(Array) ? xy_coordinates_or_points.first : xy_coordinates_or_points
  points = MultiPoint.normalize_point_array(points)
  points.map { |point| inverse_transform_point(point) }
end

#invert!Object

Inverts AffineTransform matrix if invertible Raises an error if affine transform matrix is not invertible Returns self to support fluent interface chaining



132
133
134
135
136
137
138
# File 'lib/perfect_shape/affine_transform.rb', line 132

def invert!
  raise 'Cannot invert (matrix is not invertible)!' if !invertible?
  
  self.matrix_3d = matrix_3d.inverse
  
  self
end

#invertible?Boolean

Returns:

  • (Boolean)


140
141
142
# File 'lib/perfect_shape/affine_transform.rb', line 140

def invertible?
  (m11 * m22 - m12 * m21) != 0
end

#matrix_3dObject

Returns Ruby Matrix representing Affine Transform matrix elements in 3D



208
209
210
# File 'lib/perfect_shape/affine_transform.rb', line 208

def matrix_3d
  Matrix[[xxp, xyp, xt], [yxp, yyp, yt], [0, 0, 1]]
end

#matrix_3d=(the_matrix_3d) ⇒ Object

Sets elements from a Ruby Matrix representing Affine Transform matrix elements in 3D



198
199
200
201
202
203
204
205
# File 'lib/perfect_shape/affine_transform.rb', line 198

def matrix_3d=(the_matrix_3d)
  self.xxp = the_matrix_3d[0, 0]
  self.xyp = the_matrix_3d[0, 1]
  self.xt = the_matrix_3d[0, 2]
  self.yxp = the_matrix_3d[1, 0]
  self.yyp = the_matrix_3d[1, 1]
  self.yt = the_matrix_3d[1, 2]
end

#multiply!(other_affine_transform) ⇒ Object

Multiplies by other AffineTransform



145
146
147
148
149
# File 'lib/perfect_shape/affine_transform.rb', line 145

def multiply!(other_affine_transform)
  self.matrix_3d = matrix_3d*other_affine_transform.matrix_3d
  
  self
end

#rotate!(degrees) ⇒ Object

Rotates AffineTransform counter-clockwise for positive angle value in degrees or clockwise for negative angle value in degrees



175
176
177
178
179
180
181
182
183
# File 'lib/perfect_shape/affine_transform.rb', line 175

def rotate!(degrees)
  degrees = Math.normalize_degrees(degrees)
  radians = Math.degrees_to_radians(degrees)
  
  rotation_affine_transform = AffineTransform.new(xxp: Math.cos(radians), xyp: -Math.sin(radians), yxp: Math.sin(radians), yyp: Math.cos(radians), xt: 0, yt: 0)
  multiply!(rotation_affine_transform)
  
  self
end

#scale!(x_or_point, y = nil) ⇒ Object

Scales AffineTransform



163
164
165
166
167
168
169
170
171
# File 'lib/perfect_shape/affine_transform.rb', line 163

def scale!(x_or_point, y = nil)
  x, y = Point.normalize_point(x_or_point, y)
  return unless x && y
  
  scale_affine_transform = AffineTransform.new(xxp: x, xyp: 0, yxp: 0, yyp: y, xt: 0, yt: 0)
  multiply!(scale_affine_transform)
  
  self
end

#shear!(x_or_point, y = nil) ⇒ Object Also known as: skew!

Shears AffineTransform by (x,y) amount



186
187
188
189
190
191
192
193
194
# File 'lib/perfect_shape/affine_transform.rb', line 186

def shear!(x_or_point, y = nil)
  x, y = Point.normalize_point(x_or_point, y)
  return unless x && y
  
  shear_affine_transform = AffineTransform.new(xxp: 1 + x*y, xyp: x, yxp: y, yyp: 1, xt: 0, yt: 0)
  multiply!(shear_affine_transform)
  
  self
end

#transform_point(x_or_point, y = nil) ⇒ Object



212
213
214
215
216
217
# File 'lib/perfect_shape/affine_transform.rb', line 212

def transform_point(x_or_point, y = nil)
  x, y = Point.normalize_point(x_or_point, y)
  return unless x && y
  
  [xxp*x + xyp*y + xt, yxp*x + yyp*y + yt]
end

#transform_points(*xy_coordinates_or_points) ⇒ Object



223
224
225
226
227
# File 'lib/perfect_shape/affine_transform.rb', line 223

def transform_points(*xy_coordinates_or_points)
  points = xy_coordinates_or_points.first.is_a?(Array) ? xy_coordinates_or_points.first : xy_coordinates_or_points
  points = MultiPoint.normalize_point_array(points)
  points.map { |point| transform_point(point) }
end

#translate!(x_or_point, y = nil) ⇒ Object

Translates AffineTransform



152
153
154
155
156
157
158
159
160
# File 'lib/perfect_shape/affine_transform.rb', line 152

def translate!(x_or_point, y = nil)
  x, y = Point.normalize_point(x_or_point, y)
  return unless x && y
  
  translation_affine_transform = AffineTransform.new(xxp: 1, xyp: 0, yxp: 0, yyp: 1, xt: x, yt: y)
  multiply!(translation_affine_transform)
  
  self
end