CadQuery / CadQuery/cadquery

Add a "rotateByPlanes" method

Open
#425 8 comments 0 reactions 0 assignees View on GitHub
Dominant language
Python
Stars
5.8k
Forks
541
Avg merge
3d 2h
Merged PRs (30d)
5

Description

I believe that a 3D rotation method defined by means of 2 coordinate systems would be very useful. After some investigation I found that Cadquery already has all needed information and tools to easily implement such a method (basically is just one line). Since the Plane object underlies a coordinate system, it's easy to define a 3D rotation in terms of starting and ending planes. I report below the code (working in cq-editor, Cadquery master branch) that progammatically adds such method to Cadquery:

```
import cadquery as cq
from cadquery.occ_impl.geom import Plane
from cadquery.occ_impl.shapes import Shape

def shape_rotateByPlanes(self, toPlane :Plane, fromPlane :Plane = Plane((0,0,0),(1,0,0),(0,0,1))):
"""
Rotates shapes by the same 3D rotation that leads coordinate system underneath Plane `fromPlane` to coincide with coordinate system underneath Plane `toPlane`. If plane origins are different, a translation component is added to the rotation.
Under several situations it's easier to define 3D rotations by a starting and an ending coordinate system. Note that a Plane object is actually a coordinate system. The coordinate system of a Plane is defined through 2 vectors describing the X axis and the Z axis (normal) directions of the Plane. If X axis direction is not orthogonal to Z axis, the Z-X plane is used to define the position of resulting X axis.

:param self: the shape to be rotated in 3D.
:type self: a Shape object
:param toPlane: the plane underlying the coordinate system that defines the ending position of 3D rotation.
:type toPlane: a Plane object. For example cq.Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)) underlies the global coordinate system.
:param fromPlane: the Plane underlying the coordinate system that defines the starting position of 3D rotation. This parameter is optional and the default value is the global coordinate system.
:type fromPlane: a Plane object. For example cq.Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)) underlies the global coordinate system.
:returns: a 3D rotated copy of original Shape.
"""
return self.transformShape(toPlane.rG.multiply(fromPlane.fG)) # rG is the inverse transformation matrix of a coordinate system, fG is the forward transformation matrix
cq.Shape.rotateByPlanes = shape_rotateByPlanes # adds method to cadquery

def workplane_rotateByPlanes(self, toPlane :Plane, fromPlane :Plane = Plane((0,0,0),(1,0,0),(0,0,1))) -> "Workplane":
"""
Returns a copy of all of the items on the stack 3D rotated using Shape.rotateByPlanes method.

:param toPlane: the Plane underlying the coordinate system that defines the ending position of 3D rotation.
:type toPlane: a Plane object. For example cq.Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)) underlies the global coordinate system.
:param fromPlane: the Plane underlying the coordinate system that defines the starting position of 3D rotation. This parameter is optional and the default value is the global coordinate system.
:type fromPlane: a Plane object. For example cq.Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,0,1)) underlies the global coordinate system.
:returns: a cq.Workplane object
"""
return self.newObject(
[
o.rotateByPlanes(toPlane, fromPlane)
if isinstance(o, Shape)
else o
for o in self.objects
]
)
cq.Workplane.rotateByPlanes = workplane_rotateByPlanes # adds method to cadquery

pl = Plane(origin=(0,0,0), xDir=(1,0,0), normal=(0,-1,1))

show_object(cq.Workplane("XY").box(10,5,1).rotateByPlanes(pl))
```

Despite the name `rotateByPlanes`, the method adds a translation component if the origins of `fromPlane` and `toPlane` are not the same. However, since the 3D rotation method in terms of planes is the most important (and missing in Cadquery) functionality of the method, I believe that referring only to rotation in the name of the method is appropriate. In fact, defining a transformation between two planes with different origins that omits the translation would be counterintuitive (and more complex to code).

I apologize for any oddity in my post or in the code, I'm a non-professional programmer.

Contributor guide

No contributing guide indexed for this repository

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.