Transformations#

class sage.plot.plot3d.transform.Transformation[source]#

Bases: object

avg_scale()[source]#
get_matrix()[source]#
is_skew(eps=1e-05)[source]#
is_uniform(eps=1e-05)[source]#
is_uniform_on(basis, eps=1e-05)[source]#
max_scale()[source]#
transform_bounding_box(box)[source]#
transform_point(x)[source]#
transform_vector(v)[source]#
sage.plot.plot3d.transform.rotate_arbitrary(v, theta)[source]#

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

INPUT:

• theta – real number, the angle

EXAMPLES:

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

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


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]

>>> 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: 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]

>>> 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: 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]

>>> 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:

ALGORITHM:

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

Setup some variables:

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

>>> 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: 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])

>>> 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: vy = sqrt(1-vx^2-vz^2)                                                # needs sage.symbolic

>>> 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: 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

>>> 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: s = arctan(vx/new_y) + pi/2                                           # needs sage.symbolic
sage: m = rotZ(s) * m                                                       # needs sage.symbolic

>>> 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: m = rotX(theta) * m                                                   # needs sage.symbolic

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


Do some simplifying here to avoid blow-up:

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

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


Now go back to the original coordinate system:

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

>>> 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: 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)]

>>> 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.