Transformations - 3D Graphics

Skip to content
Back to top
View this page
Edit this page

# Transformations

class sage.plot.plot3d.transform.Transformation

  Bases: `object`

  avg_scale()
  get_matrix()
  is_skew(eps=1e-05)
  is_uniform(eps=1e-05)
  is_uniform_on(basis, eps=1e-05)
  max_scale()
  transform_bounding_box(box)
  transform_point(x)
  transform_vector(v)

sage.plot.plot3d.transform.rotate_arbitrary(v, theta)

  Return a matrix that rotates the coordinate space about the axis v by the angle `theta`.

  INPUT:

  - `theta` – real number, the angle

  EXAMPLES:

  Sage

      sage: from sage.plot.plot3d.transform import rotate_arbitrary

  Python

      >>> from sage.all import *
      >>> from sage.plot.plot3d.transform import rotate_arbitrary

  Try rotating about the axes:

  Sage

      sage: rotate_arbitrary((1,0,0), 1)
      [                1.0                 0.0                 0.0]
      [                0.0  0.5403023058681398  0.8414709848078965]
      [                0.0 -0.8414709848078965  0.5403023058681398]
      sage: rotate_arbitrary((0,1,0), 1)
      [ 0.5403023058681398                 0.0 -0.8414709848078965]
      [                0.0                 1.0                 0.0]
      [ 0.8414709848078965                 0.0  0.5403023058681398]
      sage: rotate_arbitrary((0,0,1), 1)
      [ 0.5403023058681398  0.8414709848078965                 0.0]
      [-0.8414709848078965  0.5403023058681398                 0.0]
      [                0.0                 0.0                 1.0]

  [Python]

      >>> from sage.all import *
      >>> rotate_arbitrary((Integer(1),Integer(0),Integer(0)), Integer(1))
      [                1.0                 0.0                 0.0]
      [                0.0  0.5403023058681398  0.8414709848078965]
      [                0.0 -0.8414709848078965  0.5403023058681398]
      >>> rotate_arbitrary((Integer(0),Integer(1),Integer(0)), Integer(1))
      [ 0.5403023058681398                 0.0 -0.8414709848078965]
      [                0.0                 1.0                 0.0]
      [ 0.8414709848078965                 0.0  0.5403023058681398]
      >>> rotate_arbitrary((Integer(0),Integer(0),Integer(1)), Integer(1))
      [ 0.5403023058681398  0.8414709848078965                 0.0]
      [-0.8414709848078965  0.5403023058681398                 0.0]
      [                0.0                 0.0                 1.0]

  These next two should be the same (up to floating-point errors):

  Sage

      sage: rotate_arbitrary((1,1,1), 1)  # rel tol 1e-15
      [  0.6935348705787598   0.6390560643047186 -0.33259093488347846]
      [-0.33259093488347846   0.6935348705787598   0.6390560643047186]
      [  0.6390560643047186  -0.3325909348834784   0.6935348705787598]
      sage: rotate_arbitrary((1,1,1), -1)^(-1)  # rel tol 1e-15
      [  0.6935348705787598   0.6390560643047186 -0.33259093488347846]
      [-0.33259093488347846   0.6935348705787598   0.6390560643047186]
      [  0.6390560643047185 -0.33259093488347835   0.6935348705787598]

  Python

      >>> from sage.all import *
      >>> rotate_arbitrary((Integer(1),Integer(1),Integer(1)), Integer(1))  # rel tol 1e-15
      [  0.6935348705787598   0.6390560643047186 -0.33259093488347846]
      [-0.33259093488347846   0.6935348705787598   0.6390560643047186]
      [  0.6390560643047186  -0.3325909348834784   0.6935348705787598]
      >>> rotate_arbitrary((Integer(1),Integer(1),Integer(1)), -Integer(1))**(-Integer(1))  # rel tol 1e-15
      [  0.6935348705787598   0.6390560643047186 -0.33259093488347846]
      [-0.33259093488347846   0.6935348705787598   0.6390560643047186]
      [  0.6390560643047185 -0.33259093488347835   0.6935348705787598]

  Make sure it does the right thing…:

  Sage

      sage: rotate_arbitrary((1,2,3), -1).det()
      1.0000000000000002
      sage: rotate_arbitrary((1,1,1), 2*pi/3) * vector(RDF, (1,2,3))  # rel tol 2e-15             # needs sage.symbolic
      (1.9999999999999996, 2.9999999999999996, 0.9999999999999999)
      sage: rotate_arbitrary((1,2,3), 5) * vector(RDF, (1,2,3))  # rel tol 2e-15
      (1.0000000000000002, 2.0, 3.000000000000001)
      sage: rotate_arbitrary((1,1,1), pi/7)^7  # rel tol 2e-15                        # needs sage.symbolic
      [-0.33333333333333337   0.6666666666666671   0.6666666666666665]
      [  0.6666666666666665 -0.33333333333333337   0.6666666666666671]
      [  0.6666666666666671   0.6666666666666667 -0.33333333333333326]

  [Python]

      >>> from sage.all import *
      >>> rotate_arbitrary((Integer(1),Integer(2),Integer(3)), -Integer(1)).det()
      1.0000000000000002
      >>> rotate_arbitrary((Integer(1),Integer(1),Integer(1)), Integer(2)*pi/Integer(3)) * vector(RDF, (Integer(1),Integer(2),Integer(3)))  # rel tol 2e-15             # needs sage.symbolic
      (1.9999999999999996, 2.9999999999999996, 0.9999999999999999)
      >>> rotate_arbitrary((Integer(1),Integer(2),Integer(3)), Integer(5)) * vector(RDF, (Integer(1),Integer(2),Integer(3)))  # rel tol 2e-15
      (1.0000000000000002, 2.0, 3.000000000000001)
      >>> rotate_arbitrary((Integer(1),Integer(1),Integer(1)), pi/Integer(7))**Integer(7)  # rel tol 2e-15                        # needs sage.symbolic
      [-0.33333333333333337   0.6666666666666671   0.6666666666666665]
      [  0.6666666666666665 -0.33333333333333337   0.6666666666666671]
      [  0.6666666666666671   0.6666666666666667 -0.33333333333333326]

  AUTHORS:

  > - Robert Bradshaw

  ALGORITHM:

  > There is a formula. Where did it come from? Lets take a quick jaunt into Sage’s calculus package…

  > Setup some variables:

  > Sage

  >     sage: vx,vy,vz,theta = var('x y z theta')                                   # needs sage.symbolic

  > Python

  >     >>> from sage.all import *
  >     >>> vx,vy,vz,theta = var('x y z theta')                                   # needs sage.symbolic

  > Symbolic rotation matrices about X and Y axis:

  > Sage

  >     sage: def rotX(theta): return matrix(SR, 3, 3, [1, 0, 0,  0, cos(theta), -sin(theta), 0, sin(theta), cos(theta)])
  >     sage: def rotZ(theta): return matrix(SR, 3, 3, [cos(theta), -sin(theta), 0,  sin(theta), cos(theta), 0, 0, 0, 1])

  > [Python]

  >     >>> from sage.all import *
  >     >>> def rotX(theta): return matrix(SR, Integer(3), Integer(3), [Integer(1), Integer(0), Integer(0),  Integer(0), cos(theta), -sin(theta), Integer(0), sin(theta), cos(theta)])
  >     >>> def rotZ(theta): return matrix(SR, Integer(3), Integer(3), [cos(theta), -sin(theta), Integer(0),  sin(theta), cos(theta), Integer(0), Integer(0), Integer(0), Integer(1)])

  > Normalizing \(y\) so that \(|v|=1\). Perhaps there is a better way to tell Maxima that \(x^2+y^2+z^2=1\) which would make for a much cleaner calculation:

  > Sage

  >     sage: vy = sqrt(1-vx^2-vz^2)                                                # needs sage.symbolic

  > Python

  >     >>> from sage.all import *
  >     >>> vy = sqrt(Integer(1)-vx**Integer(2)-vz**Integer(2))                                                # needs sage.symbolic

  > Now we rotate about the \(x\)-axis so \(v\) is in the \(xy\)-plane:

  > Sage

  >     sage: t = arctan(vy/vz)+pi/2                                                # needs sage.symbolic
  >     sage: m = rotX(t)                                                           # needs sage.symbolic
  >     sage: new_y = vy*cos(t) - vz*sin(t)                                         # needs sage.symbolic

  > [Python]

  >     >>> from sage.all import *
  >     >>> t = arctan(vy/vz)+pi/Integer(2)                                                # needs sage.symbolic
  >     >>> m = rotX(t)                                                           # needs sage.symbolic
  >     >>> new_y = vy*cos(t) - vz*sin(t)                                         # needs sage.symbolic

  > And rotate about the \(z\) axis so \(v\) lies on the \(x\) axis:

  > Sage

  >     sage: s = arctan(vx/new_y) + pi/2                                           # needs sage.symbolic
  >     sage: m = rotZ(s) * m                                                       # needs sage.symbolic

  > Python

  >     >>> from sage.all import *
  >     >>> s = arctan(vx/new_y) + pi/Integer(2)                                           # needs sage.symbolic
  >     >>> m = rotZ(s) * m                                                       # needs sage.symbolic

  > Rotating about \(v\) in our old system is the same as rotating about the \(x\)-axis in the new:

  > Sage

  >     sage: m = rotX(theta) * m                                                   # needs sage.symbolic

  > [Python]

  >     >>> from sage.all import *
  >     >>> m = rotX(theta) * m                                                   # needs sage.symbolic

  > Do some simplifying here to avoid blow-up:

  > Sage

  >     sage: m = m.simplify_rational()                                             # needs sage.symbolic

  > Python

  >     >>> from sage.all import *
  >     >>> m = m.simplify_rational()                                             # needs sage.symbolic

  > Now go back to the original coordinate system:

  > Sage

  >     sage: m = rotZ(-s) * m                                                      # needs sage.symbolic
  >     sage: m = rotX(-t) * m                                                      # needs sage.symbolic

  > [Python]

  >     >>> from sage.all import *
  >     >>> m = rotZ(-s) * m                                                      # needs sage.symbolic
  >     >>> m = rotX(-t) * m                                                      # needs sage.symbolic

  > And simplify every single entry (which is more effective that simplify the whole matrix like above):

  > Sage

  >     sage: m.parent()([x.simplify_full() for x in m._list()])  # random  # long time, needs sage.symbolic
  >     [                                       -(cos(theta) - 1)*x^2 + cos(theta)              -(cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*x + sin(theta)*abs(z)      -((cos(theta) - 1)*x*z^2 + sqrt(-x^2 - z^2 + 1)*sin(theta)*abs(z))/z]
  >     [             -(cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*x - sin(theta)*abs(z)                           (cos(theta) - 1)*x^2 + (cos(theta) - 1)*z^2 + 1 -((cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*z*abs(z) - x*z*sin(theta))/abs(z)]
  >     [     -((cos(theta) - 1)*x*z^2 - sqrt(-x^2 - z^2 + 1)*sin(theta)*abs(z))/z -((cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*z*abs(z) + x*z*sin(theta))/abs(z)                                        -(cos(theta) - 1)*z^2 + cos(theta)]

  > Python

  >     >>> from sage.all import *
  >     >>> m.parent()([x.simplify_full() for x in m._list()])  # random  # long time, needs sage.symbolic
  >     [                                       -(cos(theta) - 1)*x^2 + cos(theta)              -(cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*x + sin(theta)*abs(z)      -((cos(theta) - 1)*x*z^2 + sqrt(-x^2 - z^2 + 1)*sin(theta)*abs(z))/z]
  >     [             -(cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*x - sin(theta)*abs(z)                           (cos(theta) - 1)*x^2 + (cos(theta) - 1)*z^2 + 1 -((cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*z*abs(z) - x*z*sin(theta))/abs(z)]
  >     [     -((cos(theta) - 1)*x*z^2 - sqrt(-x^2 - z^2 + 1)*sin(theta)*abs(z))/z -((cos(theta) - 1)*sqrt(-x^2 - z^2 + 1)*z*abs(z) + x*z*sin(theta))/abs(z)                                        -(cos(theta) - 1)*z^2 + cos(theta)]

  > Re-expressing some entries in terms of y and resolving the absolute values introduced by eliminating y, we get the desired result.