## Axis To Axis Transformations With EKL

Transforming geometric features from an AxisSystem to another is a common task in CATIA; especially when the goal is to decouple driving geometry from the construction of a UDF or a PowerCopy. For example, providing 2D-curves as input to a UDF that transforms the curves to build an extruded surface along a line–think window-frame tubes/channels. There are many such use cases. While EKL does not provide a convenient Axis To Axis transformation function, it does make available all the APIs we need to build our own function. Take a look:

### Pseudo

Transforming a geometric features will depend on a source `AxisSystem`

and a target `AxisSystem`

. There are four types of transformations that we can apply to geometric objects: translation, rotation, scale, and skew. Our use case will require handling translation and rotation.

The idea is to extract the 3 rotation angles (around `x`

, `y`

, and `z`

) and the origin from both axes. Then compute the difference in rotations between the corresponding 3 axes. Then identify the translation direction and distance. Finally, we\’ll rotation the geometric object around the source axis system\’s `x`

direction, then take the resulting object and rotate it around the source\’s `y`

direction, then take the resulting object and rotate it around the source\’s `z`

direction. Then finally translate the result of the last rotation (around z) using the direction and distance– from source origin to target origin.

`AxisSystems`

have two attributes: `Origin`

and `Axes`

. The first is a horizontal vector (1 row of 3 columns) with real numbers indicating x, y, and z coordinates. Notes that EKL uses `mm`

for lengths. So the `Real`

values are the magnitudes without the `mm`

unit. Origin\’s vector is organized as follows:

`x, y, z`

The second is a matrix, 3 by 3, holding the x, y, and z horizontal vectors that define the `AxisSystem`

orientation. Rows are organized as follows:

```
xx, xy, xz
yx, yy, yz
zx, zy, zz
```

The first row describes the X Axis, the second describes Y, and the third describes Z. Calculating the rotations around each axis can be done using the following formulas:

`x`

rotation is equal to the`atan2(xy, xx)`

`y`

rotation is equal to the`atan2(yz, zz)`

`z`

rotation is equal to the`asin(-xz)`

Note that if you\’re using a version of CATIA that is older than 18X, then `atan2`

function might not be available. So instead, you\’ll need to use `atan(number1/number2)`

.

To see the content of a matrix, use the `Dump()`

method. For example

```
let myMatrix(Matrix)
myMatrix =...
Notify( myMatrix.Dump())
```

Now let\’s take a look at the full code!

### Code

```
/* Action created by melkhaldi 8/20/2022 */
//inputs: SourceAxis: AxisSystem, TargetAxis: AxisSystem, and GeoObject: Wireframe
//get the source axis system vectors
let souceMatrix(Matrix)
souceMatrix = SourceAxis.Axes
//get the source axis system origin vector
let sourceOrigin(Vector)
sourceOrigin =SourceAxis.Origin
//build a point object, EKL uses millimeters by default --regardless of what your settings are in session
let sourcePoint(Point)
sourcePoint = point( sourceOrigin.Get(1,1)*1mm, sourceOrigin.Get(1,2)*1mm, sourceOrigin.Get(1,3)*1mm)
//get source axis matrix components
let sourceXX, sourceXY, sourceXZ, sourceYX, sourceYY, sourceYZ, sourceZX, sourceZY, sourceZZ(Real)
sourceXX = souceMatrix.Get(1,1)
sourceXY = souceMatrix.Get(1,2)
sourceXZ = souceMatrix.Get(1,3)
sourceYX = souceMatrix.Get(2,1)
sourceYY = souceMatrix.Get(2,2)
sourceYZ = souceMatrix.Get(2,3)
sourceZX = souceMatrix.Get(3,1)
sourceZY = souceMatrix.Get(3,2)
sourceZZ = souceMatrix.Get(3,3)
//get the source rotation angles around each axis using the axis system matrix components
let sourceXRotation, sourceYRotation, sourceZRotation(Angle)
sourceZRotation = atan2(sourceXY, sourceXX)
sourceYRotation = atan2(sourceYZ, sourceZZ)
sourceXRotation = asin(-sourceXZ)
//lets build Directions from the source matrix
let sourceXDir, sourceYDir, sourceZDir (Direction)
sourceXDir= direction(sourceXX*1mm, sourceXY*1mm, sourceXZ*1mm)
sourceYDir = direction(sourceYX*1mm, sourceYY*1mm, sourceYZ*1mm)
sourceZDir = direction(sourceZX*1mm, sourceZY*1mm, sourceZZ*1mm)
//let's create source axis system lines. we'll need those for the rotations later on.
let sourceXLine, sourceYLine, sourceZLine(Line)
//lets get some directions using a line.
//the line is form axis origin to a point that is a result of a translate of the origin point to some distance along a direction
sourceXLine = line( sourcePoint, translate( sourcePoint, sourceXDir, 1mm):Point)
sourceYLine = line( sourcePoint, translate( sourcePoint, sourceYDir, 1mm):Point)
sourceZLine = line( sourcePoint, translate( sourcePoint, sourceZDir, 1mm):Point)
//get the target axis system vectors
let targetMatrix(Matrix)
targetMatrix = TargetAxis.Axes
//get target origin
let targetOrigin(Vector)
targetOrigin =TargetAxis.Origin
//make a target axis system point at axis origin
let targetPoint(Point)
targetPoint = point( targetOrigin.Get(1,1)*1mm, targetOrigin.Get(1,2)*1mm, targetOrigin.Get(1,3)*1mm)
//get target axis system components
let targetXX, targetXY, targetXZ, targetYX, targetYY, targetYZ, targetZX, targetZY, targetZZ(Real)
targetXX = targetMatrix.Get(1,1)
targetXY = targetMatrix.Get(1,2)
targetXZ = targetMatrix.Get(1,3)
targetYX = targetMatrix.Get(2,1)
targetYY = targetMatrix.Get(2,2)
targetYZ = targetMatrix.Get(2,3)
targetZX = targetMatrix.Get(3,1)
targetZY = targetMatrix.Get(3,2)
targetZZ = targetMatrix.Get(3,3)
//get target axis system rotations
let targetXRotation, targetYRotation, targetZRotation(Angle)
targetZRotation = atan2(targetXY, targetXX)
targetYRotation = atan2(targetYZ, targetZZ)
targetXRotation = asin(- targetXZ)
//apply rotation angles to the geometric object we need to transform.
//the result of each rotation will be the input for the next one
//note that we will do the rotations around the directions of the source.
//rotation is the target angle - source angle
let rX, rY, rZ(Wireframe)
set rX = rotate( GeoObject, sourceXLine, targetXRotation - sourceXRotation)
set rY = rotate( rX, sourceYLine, targetYRotation- sourceYRotation)
set rZ = rotate( rY, sourceZLine, targetZRotation- sourceZRotation)
//now lets translate
//get the direction from source origin point to target origin point
let direct(Direction)
direct = direction( line( sourcePoint, targetPoint))
//get the travel distance
let dist(Length)
dist = distance( sourcePoint, targetPoint)
//translate the last rotated shape
//let's try to give it a unique name
let ResultName(String)
ResultName = TextFormat("AxisToAxis #--#", GeoObject.Name,GeoObject.PrimaryType.Name())
//lets recreate the wireframe
let xFormed(Wireframe)
//note that I am declaring the variable using the super class, more generic type, Wireframe
//however, I am using the type name, which is a string, as the creation type for the new function
xFormed = new( GeoObject.PrimaryType.Name(), ResultName , GeoObject.Owner)
xFormed = translate( rZ,direct, dist)
```

Tags: Algorithms, EKL, Geometry & Topology, Linear Algebra