Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
24 changes: 17 additions & 7 deletions cadquery/occ_impl/geom.py
Original file line number Diff line number Diff line change
Expand Up @@ -634,19 +634,29 @@ def rotated(self, rotate=(0, 0, 0)):
:param rotate: Vector [xDegrees, yDegrees, zDegrees]
:return: a copy of this plane rotated as requested.
"""
rotate = Vector(self.toWorldCoords(rotate))
# NB: this is not a geometric Vector
rotate = Vector(rotate)
# Convert to radians.
rotate = rotate.multiply(math.pi / 180.0)

# Compute rotation matrix.
m = Matrix()
m.rotateX(rotate.x)
m.rotateY(rotate.y)
m.rotateZ(rotate.z)
T1 = gp_Trsf()
T1.SetRotation(
gp_Ax1(gp_Pnt(*(0, 0, 0)), gp_Dir(*self.xDir.toTuple())), rotate.x
)
T2 = gp_Trsf()
T2.SetRotation(
gp_Ax1(gp_Pnt(*(0, 0, 0)), gp_Dir(*self.yDir.toTuple())), rotate.y
)
T3 = gp_Trsf()
T3.SetRotation(
gp_Ax1(gp_Pnt(*(0, 0, 0)), gp_Dir(*self.zDir.toTuple())), rotate.z
)
T = Matrix(gp_GTrsf(T1 * T2 * T3))

# Compute the new plane.
newXdir = self.xDir.transform(m)
newZdir = self.zDir.transform(m)
newXdir = self.xDir.transform(T)
newZdir = self.zDir.transform(T)

return Plane(self.origin, newXdir, newZdir)

Expand Down
85 changes: 67 additions & 18 deletions tests/test_cadquery.py
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
from random import random
from random import randrange

from pytest import approx

# my modules
from cadquery import *
from cadquery import exporters
Expand Down Expand Up @@ -283,27 +285,74 @@ def testPlaneRotateZNormal(self):
"""
Rotation of a plane in the Z direction should never alter its normal.

This test creates random planes, with the normal in a random direction
among positive and negative X, Y and Z. The plane is defined with this
normal and another random perpendicular vector (the X-direction of the
plane). The plane is finally rotated a random angle in the Z-direction
to verify that the resulting plane maintains the same normal.
This test creates random planes. The plane is rotated a random angle in
the Z-direction to verify that the resulting plane maintains the same
normal.

The test also checks that the random origin is unaltered after
rotation.
"""
for _ in range(100):
normal_sign = choice((-1, 1))
normal_dir = randrange(3)
angle = (random() - 0.5) * 720

normal = [0, 0, 0]
normal[normal_dir] = normal_sign
xdir = [random(), random(), random()]
xdir[normal_dir] = 0

plane = Plane(origin=(0, 0, 0), xDir=xdir, normal=normal)
rotated = plane.rotated((0, 0, angle)).zDir.toTuple()
self.assertAlmostEqual(rotated[0], normal[0])
self.assertAlmostEqual(rotated[1], normal[1])
self.assertAlmostEqual(rotated[2], normal[2])
xdir = Vector(random(), random(), random()).normalized()
rdir = Vector(random(), random(), random()).normalized()
zdir = xdir.cross(rdir).normalized()
origin = (random(), random(), random())
plane = Plane(origin=origin, xDir=xdir, normal=zdir)
rotated = plane.rotated((0, 0, angle))
assert rotated.zDir.toTuple() == approx(zdir.toTuple())
assert rotated.origin.toTuple() == approx(origin)

def testPlaneRotateConcat(self):
"""
Test the result of a well-known concatenated rotation example.
"""
xdir = (1, 0, 0)
normal = (0, 0, 1)
k = 2.0 ** 0.5 / 2.0
origin = (2, -1, 1)
plane = Plane(origin=origin, xDir=xdir, normal=normal)
plane = plane.rotated((0, 0, 45))
assert plane.xDir.toTuple() == approx((k, k, 0))
assert plane.yDir.toTuple() == approx((-k, k, 0))
assert plane.zDir.toTuple() == approx((0, 0, 1))
plane = plane.rotated((0, 45, 0))
assert plane.xDir.toTuple() == approx((0.5, 0.5, -k))
assert plane.yDir.toTuple() == approx((-k, k, 0))
assert plane.zDir.toTuple() == approx((0.5, 0.5, k))
assert plane.origin.toTuple() == origin

def testPlaneRotateConcatRandom(self):
"""
Rotation of a plane in a given direction should never alter that
direction.

This test creates a plane and rotates it a random angle in a given
direction. After the rotation, the direction of the resulting plane
in the rotation-direction should be constant.

The test also checks that the origin is unaltered after all rotations.
"""
origin = (2, -1, 1)
plane = Plane(origin=origin, xDir=(1, 0, 0), normal=(0, 0, 1))
for _ in range(100):
before = {
0: plane.xDir.toTuple(),
1: plane.yDir.toTuple(),
2: plane.zDir.toTuple(),
}
angle = (random() - 0.5) * 720
direction = randrange(3)
rotation = [0, 0, 0]
rotation[direction] = angle
plane = plane.rotated(rotation)
after = {
0: plane.xDir.toTuple(),
1: plane.yDir.toTuple(),
2: plane.zDir.toTuple(),
}
assert before[direction] == approx(after[direction])
assert plane.origin.toTuple() == origin

def testLoft(self):
"""
Expand Down