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: from sage.plot.plot3d.transform import rotate_arbitrary

Try rotating about the axes:

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]

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]

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
(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
[-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: vx,vy,vz,theta = var('x y z theta')

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

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)

Now we rotate about the $x$-axis so $v$ is in the $xy$-plane:

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

And rotate about the $z$ axis so $v$ lies on the $x$ axis:

sage: s = arctan(vx/new_y) + pi/2
sage: m = rotZ(s) * m

Rotating about $v$ in our old system is the same as rotating about the $x$-axis in the new:

sage: m = rotX(theta) * m

Do some simplifying here to avoid blow-up:

sage: m = m.simplify_rational()

Now go back to the original coordinate system:

sage: m = rotZ(-s) * m
sage: m = rotX(-t) * m

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()])  # long time; random
[                                       -(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.