Skew transformation inverse Issue when constructed with axes Method

I am seeing the Geom::Transformation class’s inverse method not work correctly for a specific skew/shear situation.

The Ruby code I am working with italicizes some text in the X-Y plane with the following transformation:

shear_array = [ 1,0,0,0,  1,1,0,0, 0,0,1,0, 0,0,0,1 ]
h = Geom::Transformation.new( shear_array )

Now I can apply transformation “h” to my component instance with “transform!” to shear it. That is working correctly.
I can obtain a string object that provides the transformation matrix values in column major order with the following method:

    def tmatrix_bycolumn( transformation )
      ma = transformation.to_a.collect{|value| "%8.3f" % value }
      result =  "   #{ma[ 0]}, #{ma[ 4]}, #{ma[ 8]}, #{ma[12]}\n"
      result << "   #{ma[ 1]}, #{ma[ 5]}, #{ma[ 9]}, #{ma[13]}\n"
      result << "   #{ma[ 2]}, #{ma[ 6]}, #{ma[10]}, #{ma[14]}\n"
      result << "   #{ma[ 3]}, #{ma[ 7]}, #{ma[11]}, #{ma[15]}\n"
      return result
    end

I can output with a statement like:

puts tmatrix_bycolumn( h * h.inverse)

The results are:

  1.000,    0.000,    0.000,    0.000
  0.000,    1.000,    0.000,    0.000
  0.000,    0.000,    1.000,    0.000
  0.000,    0.000,    0.000,    1.000

The output is the identity matrix, indicating that the inverse was calculated correctly.

But, constructing this transformation with normalized “h” matrix axes and taking the inverse results in an incorrect inverse. Initialization and output statement below:

skew = Geom::Transformation.axes( ORIGIN, h.xaxis, h.yaxis, h.zaxis )
puts tmatrix_bycolumn( skew * skew.inverse )

The output is:

  1.500,    0.500,    0.000,    0.000
  0.500,    0.500,    0.000,    0.000
  0.000,    0.000,    1.000,    0.000
  0.000,    0.000,    0.000,    1.000

The result is not identity, even though this type of skew is a valid transformation matrix.
Sketchup’s Geom module’s transformation class’s inverse for the skew is:

Puts tmatrix_bycolumn( skew.inverse )

  1.000,    0.000,    0.000,   -0.000
  0.707,    0.707,    0.000,   -0.000
  0.000,    0.000,    1.000,   -0.000
  0.000,    0.000,    0.000,    1.000

The correct result from Octave (an open source type Matlab console) is:

   1.00000  -1.00000  -0.00000  -0.00000
   0.00000   1.41443  -0.00000  -0.00000
   0.00000   0.00000   1.00000  -0.00000
   0.00000   0.00000   0.00000   1.00000

You can also try an online matrix calculator like: https://matrixcalc.org/en/

The only difference between the one that works and the one that does not is that the one that works is scaled.
I realize that skews/shears are uncommon, but it looks like inverse isn’t working for all types of matrices.

Note: Matrix inverse was discussed at:

But that topic was about non-invertible transformaitons.

Did you consider that SketchUp stores in the last matrix element (m[15]) a uniform scale factor (scales in all directions)? To normalize the matrix I believe one has to do:

[ 0, 1, 2].each{ |i| m[i] /= m[15] }
[ 4, 5, 6].each{ |i| m[i] /= m[15] }
[ 8, 9,10].each{ |i| m[i] /= m[15] }
# [12,13,14] store the translation which is unaffected.
m[15] = 1.0

I observed another inconsistency. Does the problem already occur in the Transformation.axes factory method or only in inverse? I recreated the skew transformation with the normal constructor and a matrix array:

skew2 = Geom::Transformation.new([ 1,0,0,0,  Math.sqrt(0.5),Math.sqrt(0.5),0,0, 0,0,1,0, 0,0,0,1 ])

which prints just like skew

puts tmatrix_bycolumn(skew2)
      1.000,    0.707,    0.000,    0.000
      0.000,    0.707,    0.000,    0.000
      0.000,    0.000,    1.000,    0.000
      0.000,    0.000,    0.000,    1.000

but has a different inverse:

puts tmatrix_bycolumn(skew2.inverse)
      1.000,   -1.000,    0.000,   -0.000
     -0.000,    1.414,   -0.000,   -0.000
      0.000,   -0.000,    1.000,   -0.000
      0.000,    0.000,    0.000,    1.000

which leads to the correct identity:

puts tmatrix_bycolumn(skew2 * skew2.inverse)
      1.000,    0.000,    0.000,    0.000
      0.000,    1.000,    0.000,    0.000
      0.000,    0.000,    1.000,    0.000
      0.000,    0.000,    0.000,    1.000

So a transformation created from axes must have a certain private attribute since it behaves different from a transformation with identical elements.

1 Like

No, I initialized m[15] to 1. A uniform scale factor of 1 shouldn’t affect anything.

After examining your output, I think it occurs with the transformation initialized with the “axes” constructor. Instead of using my output routine, consider my “skew” and your “skew2” with just “to_a” (Sketchup 2017 Make):

skew.to_a
[1.0, 0.0, 0.0, 0.0, 0.7071067811865476, 0.7071067811865476, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]
skew2.to_a
[1.0, 0.0, 0.0, 0.0, 0.7071067811865476, 0.7071067811865476, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, 0.0, 0.0, 0.0, 1.0]

With no floating point formatting, they are a match. But when I use the inverse method with “to_a”.

skew.inverse.to_a
[1.0, 0.7071067811865476, 0.0, 0.0, 0.0, 0.7071067811865476, 0.0, 0.0, 0.0, 0.0, 1.0, 0.0, -0.0, -0.0, -0.0, 1.0]
skew2.inverse.to_a
[1.0, -0.0, 0.0, 0.0, -1.0, 1.414213562373095, -0.0, 0.0, 0.0, -0.0, 1.0, 0.0, -0.0, -0.0, -0.0, 1.0]

I’m seeing the same results on Sketchup 2016 32bit and Sketchup 2015 64 bit, so it does not appear to be related to the Ruby version change.
You said:

I concur.
I clicked the solution check box because @Aerilius has a work around solution. I can:

  • Obtain normalized axis vectors
  • Create a 16 element array containing transformation matrix elements that include the normalized values; and
  • Initialize a transformation matrix with the array elements;

A coding example that follows the steps is below:

u = h.xaxis;    v = h.yaxis;   w = h.zaxis  # These are normalized
arr = [u.x,u.y,u.z,0,   v.x,v.y,v.z,0, w.x,w.y,w.z,0, 0,0,0,1]
skew3 = Geom::Transformation.new( arr )

The “skew3” works like @Aerilius 's “skew2”, but for all vector values. I still consider there to be a bug in either the “inverse” method or the axes constructor.

1 Like

You are spot on. The internal transformation class Ruby wraps uses some internal flags for optimizations. Depending on how the matrix is constructed these flags are set. Though it’s not recomputed - so seemingly similar transformation objects might vary in how they are set.

However, this should really affect some small floating point precision. Results shouldn’t very other than that. That we’re getting different inverse here puzzles me.

This topic was automatically closed 91 days after the last reply. New replies are no longer allowed.