Example 1 Interpolating Conformal Objects

In this example we will look at a few of the tools provided by the clifford package for (4,1) conformal geometric algebra (CGA) and see how we can use them in a practical setting to interpolate geometric primitives.

The first step in using the package for CGA is to generate and import the algebra:

from clifford.g3c import *
{'': 1,
 'e1': (1^e1),
 'e2': (1^e2),
 'e3': (1^e3),
 'e4': (1^e4),
 'e5': (1^e5),
 'e12': (1^e12),
 'e13': (1^e13),
 'e14': (1^e14),
 'e15': (1^e15),
 'e23': (1^e23),
 'e24': (1^e24),
 'e25': (1^e25),
 'e34': (1^e34),
 'e35': (1^e35),
 'e45': (1^e45),
 'e123': (1^e123),
 'e124': (1^e124),
 'e125': (1^e125),
 'e134': (1^e134),
 'e135': (1^e135),
 'e145': (1^e145),
 'e234': (1^e234),
 'e235': (1^e235),
 'e245': (1^e245),
 'e345': (1^e345),
 'e1234': (1^e1234),
 'e1235': (1^e1235),
 'e1245': (1^e1245),
 'e1345': (1^e1345),
 'e2345': (1^e2345),
 'e12345': (1^e12345)}

This creates an algebra with the required signature and imports the basis blades into the current workspace. We can check our metric by squaring our grade 1 multivectors.

print('e1*e1 ', e1*e1)
print('e2*e2 ', e2*e2)
print('e3*e3 ', e3*e3)
print('e4*e4 ', e4*e4)
print('e5*e5 ', e5*e5)
e1*e1  1
e2*e2  1
e3*e3  1
e4*e4  1
e5*e5  -1

As expected this gives us 4 basis vectors that square to 1.0 and one that squares to -1.0, therefore confirming our metric is (4,1).

The up() function implements the mapping of vectors from standard 3D space to conformal space. We can use this to construct conformal objects to play around with.

For example a line at the origin:

line_a = ( up(0)^up(e1+e2)^einf ).normal()
(0.70711^e145) + (0.70711^e245)

The tools submodule of the clifford package contains a wide array of algorithms and tools that can be useful for manipulating objects in CGA. We will use these tools to generate rotors that rotate and translate objects:

from clifford.tools.g3 import *
from clifford.tools.g3c import *
from numpy import pi

rotation_radians = pi/4
euc_vector_in_plane_m = e1
euc_vector_in_plane_n = e3

euc_translation = -5.2*e1 + 3*e2 - pi*e3

rotor_rotation = generate_rotation_rotor(rotation_radians, euc_vector_in_plane_m, euc_vector_in_plane_n)
rotor_translation = generate_translation_rotor(euc_translation)

combined_rotor = (rotor_translation*rotor_rotation).normal()

line_b = (combined_rotor*line_a*~combined_rotor).normal()
/home/docs/checkouts/readthedocs.org/user_builds/clifford/conda/v1.2.0/lib/python3.7/site-packages/pyganja/__init__.py:2: UserWarning: Failed to import cef_gui, cef functions will be unavailable
  from .script_api import *
0.92388 - (0.38268^e13)
1.0 + (2.6^e14) + (2.6^e15) - (1.5^e24) - (1.5^e25) + (1.5708^e34) + (1.5708^e35)
-(5.17696^e124) - (5.17696^e125) - (1.0292^e134) - (1.0292^e135) + (0.5^e145) + (3.72144^e234) + (3.72144^e235) + (0.70711^e245) + (0.5^e345)

In the above snippet of code we have generated rotors for translation and rotation, then combined these, then applied the combined rotor to the original line that we made.


The clifford package can be used alongside pyganja to render CGA objects which can be rotated interactively in a jupyter notebook:

from pyganja import GanjaScene, draw
sc = GanjaScene()
sc.add_object(line_a,color=0xFF0000, label='a')
sc.add_object(line_b,color=0x00FF00, label='b')
draw(sc, scale=0.1)

We can also interpolate the objects using the tools in clifford and can visualise the result

def interpolate_objects_linearly(L1, L2, n_steps=10, color_1=np.array([255,0,0]), color_2=np.array([0,255,0])):
    alpha_list = np.linspace(0, 1, num=n_steps)
    intermediary_list = []
    sc = GanjaScene()
    for alpha in alpha_list:
        intermediate_color = (alpha*color_1 + (1-alpha)*color_2).astype(np.uint8)
        intermediate_object = interp_objects_root(L1, L2, alpha)
        color_string = int(
            (intermediate_color[0] << 16) | (intermediate_color[1] << 8) | intermediate_color[2]
        sc.add_object(intermediate_object, color_string)
    return intermediary_list, sc
intermediary_list, finished_scene = interpolate_objects_linearly(line_a, line_b)
draw(finished_scene, scale=0.1)

We can do the same for all the other geometric primitives as well


circle_a = (up(0)^up(e1)^up(e2)).normal()
circle_b = (combined_rotor*circle_a*~combined_rotor).normal()
intermediary_list, finished_scene = interpolate_objects_linearly(circle_a, circle_b)
draw(finished_scene, scale=0.1)

Point pairs

point_pair_a = (up(e3)^up(e1+e2)).normal()
point_pair_b = (combined_rotor*point_pair_a*~combined_rotor).normal()
intermediary_list, finished_scene = interpolate_objects_linearly(point_pair_a, point_pair_b)
draw(finished_scene, scale=0.1)


plane_a = (up(0)^up(e1)^up(e2)^einf).normal()
plane_b = (combined_rotor*plane_a*~combined_rotor).normal()
intermediary_list, finished_scene = interpolate_objects_linearly(plane_a, plane_b)


sphere_a = (up(0)^up(e1)^up(e2)^up(e3)).normal()
sphere_b = (combined_rotor*sphere_a*~combined_rotor).normal()
intermediary_list, finished_scene = interpolate_objects_linearly(sphere_a, sphere_b)
draw(finished_scene, scale=0.1)