Introduction

Welcome to the GAOnline tutorial!

In this document we will run through how to use GAOnline to visualise objects from Conformal Geometric Algebra (CGA)

We will be using the clifford package for generating and manipulating conformal objects. GAOnline was originally designed to be used this package and clifford contains tools that make it very easy to use GAOnline for visualising many objects at once!

Visualising our objects

The scripting language used for GAOnline is very simple, the default script that appears in the bottom left of the page shows the full extent of the operations available to us:

DrawSphere(-(3.0^e1234) - (2.0^e1235) + (2.0^e1245) - (1.0^e1345) + (1.0^e2345),rgb(255,0,0)); DrawPlane((0.57735^e1234) + (0.57735^e1235) - (0.57735^e1245) + (0.57735^e1345) - (0.57735^e2345),rgb(255,255,0)); DrawLine(-(0.70711^e124) - (0.70711^e125) - (0.70711^e145) - (0.70711^e245),rgb(255,0,255)); DrawCircle(-(0.07071^e124) + (0.07071^e125) - (0.07071^e134) + (0.07071^e135) + (0.70711^e145) + (0.07071^e234) - (0.07071^e235) - (0.70711^e245),rgb(0,0,255)); DrawPointPair((12.0^e12) - (16.0^e23) + (56.0^e24) + (60.0^e25),rgb(255,0,255)); DrawEucPoint(0.5^e1 + 0.5^e2 - 0.5^e3,rgb(255,0,0));

Each seperate command is on a new line and ends in a semi-colon. This is very important, your script will not work if you do not do this. Together all of the commands specify a the scene we will draw.

The format of the commands is simple. Consider the first line our above demo script:

DrawSphere(-(3.0^e1234) - (2.0^e1235) + (2.0^e1245) - (1.0^e1345) + (1.0^e2345),rgb(255,0,0));

DrawSphere is the command. Unsurprisingly this means we would like to draw a sphere!

The first argument to the command is the multivector that we would like to work with:

-(3.0^e1234) - (2.0^e1235) + (2.0^e1245) - (1.0^e1345) + (1.0^e2345)

We would like GAOnline to try to interpret this multivector as a sphere. Note the form of the multivector, it uses the wedge product ^ and not a geometric product * to separate the coefficients from the canonical blades. Additionally even for blades with a coefficient of 1 we still need to put the coefficient in front with a wedge eg. ... + 1.0^e2345 rather than just ... + e2345; this is due to the way in which GAOnline parses the input script and if you don't do this then GAOnline will not work.

The second argument is the color that our sphere will be drawn in:

rgb(255,0,0)

This is an RGB code where values are in the range 0 to 255. Thus rgb(255,0,0) means maximum red channel, and no green and no blue. Thus our sphere appears red.

The final thing to remember is the semi-colon at the end of the line and a new line for the next command...

Creating objects in CGA

Now that we know the format of the commands lets try actually generating some objects for visualisation!

We first import the clifford library and associated tools:

In [1]:
from clifford.g3c import *
from clifford.tools.g3 import *
from clifford.tools.g3c import *

We can now create objects such as point pairs, lines, circles, spheres, and planes; either by manually wedging conformal points together or using the utility functions from the clifford package.

Point pairs:

In [2]:
# The up() function performs the mapping of a 3d point to conformal space
point_pair_a = ( up(0)^up(e2) ).normal()
# The clifford package also has utility functions for creating random objects
point_pair_b = random_point_pair()
print('point_pair_a ' , point_pair_a)
print('point_pair_b ' , point_pair_b)
point_pair_a  (1.0^e24) - (1.0^e25) - (1.0^e45)
point_pair_b  -(1.27689^e12) - (0.69033^e13) - (4.32895^e14) - (4.61037^e15) + (0.86841^e23) - (0.17096^e24) - (0.20691^e25) - (3.03655^e34) - (3.24737^e35) - (0.0842^e45)

Lines:

In [3]:
line_a = ( up(0)^up(e1)^einf ).normal()
line_b = random_line()

print('line_a ' , line_a)
print('line_b ' , line_b)
line_a  (1.0^e145)
line_b  (1.28817^e124) + (1.28817^e125) + (0.64178^e134) + (0.64178^e135) - (0.89338^e145) - (0.60891^e234) - (0.60891^e235) + (0.38596^e245) - (0.23001^e345)

Circles:

In [4]:
circle_a = ( up(0)^up(e1)^up(e3) ).normal()
circle_b = random_circle()
print('circle_a ' , circle_a)
print('circle_b ' , circle_b)
circle_a  -(0.70711^e134) + (0.70711^e135) + (0.70711^e145) - (0.70711^e345)
circle_b  (0.35652^e123) - (2.33275^e124) - (2.28623^e125) - (0.15416^e134) - (0.09628^e135) - (0.35866^e145) - (5.01848^e234) - (5.02537^e235) + (0.69989^e245) + (0.81784^e345)

Spheres:

In [5]:
sphere_a = ( up(0)^up(e1)^up(e2)^up(e3) ).normal()
sphere_b = random_sphere()
print('sphere_a ' , sphere_a)
print('sphere_b ' , sphere_b)
sphere_a  (0.57735^e1234) - (0.57735^e1235) - (0.57735^e1245) + (0.57735^e1345) - (0.57735^e2345)
sphere_b  -(0.50343^e1234) - (0.5198^e1235) - (0.28523^e1245) + (0.55135^e1345) - (0.77324^e2345)

Planes:

In [6]:
plane_a = ( up(0)^up(e1)^up(e2+e3)^einf ).normal()
plane_b = random_plane()
print('plane_a ' , plane_a)
print('plane_b ' , plane_b)
plane_a  -(0.70711^e1245) - (0.70711^e1345)
plane_b  (2.6121^e1234) + (2.6121^e1235) + (0.14363^e1245) + (0.58984^e1345) - (0.79465^e2345)

We can also generate and visualise 3D points

In [7]:
point_a = up(e1+2*e2)
point_b = random_euc_mv()
print('point_a ' , point_a)
print('point_b ' , point_b)
point_a  (1.0^e1) + (2.0^e2) + (2.0^e4) + (3.0^e5)
point_b  -(2.88743^e1) + (2.0341^e2) - (5.30764^e3)

Visualising our new objects

Now that we know the format of our commands and can generate conformal objects we can modify our demo scene to see our new objects!

Try changing the script to show some of the objects that we generated above... eg. for plane_a:

DrawPlane(-(0.70711^e1245) - (0.70711^e1345),rgb(255,255,0));

While useful for basic experimenting and messing around with CGA objects this manual creation of commands can become very tedius very fast. We would instead like a way of generating GAOnline commands programmatically directly from the multivectors.

The clifford package contains tools to let us do exactly this!

We first define an empty GAScene and then add objects to it. Once we have added all of our objects added we can print the scene and then copy and paste the whole script into GAOnline at once!

In [8]:
from clifford.tools.g3c.GAOnline import *

sc = GAScene()
sc.add_point_pair(point_pair_b,'rgb(255,0,0)')
sc.add_line(line_b,'rgb(0,0,0)')
sc.add_circle(circle_b,'rgb(0,255,0)')
sc.add_sphere(sphere_b,'rgb(0,255,255)')
sc.add_plane(plane_b,'rgb(0,0,255)')
sc.add_euc_point(point_b,'rgb(255,0,255)')
print(sc)
DrawPointPair(-(1.27689^e12) - (0.69033^e13) - (4.32895^e14) - (4.61037^e15) + (0.86841^e23) - (0.17096^e24) - (0.20691^e25) - (3.03655^e34) - (3.24737^e35) - (0.0842^e45),rgb(255,0,0));
DrawLine((1.28817^e124) + (1.28817^e125) + (0.64178^e134) + (0.64178^e135) - (0.89338^e145) - (0.60891^e234) - (0.60891^e235) + (0.38596^e245) - (0.23001^e345),rgb(0,0,0));
DrawCircle((0.35652^e123) - (2.33275^e124) - (2.28623^e125) - (0.15416^e134) - (0.09628^e135) - (0.35866^e145) - (5.01848^e234) - (5.02537^e235) + (0.69989^e245) + (0.81784^e345),rgb(0,255,0));
DrawSphere(-(0.50343^e1234) - (0.5198^e1235) - (0.28523^e1245) + (0.55135^e1345) - (0.77324^e2345),rgb(0,255,255));
DrawPlane((2.6121^e1234) + (2.6121^e1235) + (0.14363^e1245) + (0.58984^e1345) - (0.79465^e2345),rgb(0,0,255));
DrawEucPoint(-(2.88743^e1) + (2.0341^e2) - (5.30764^e3),rgb(255,0,255));

The real power of this scripting interface comes when you want to display large number of conformal objects at once.

For example imagine you would like to visualise the direct interpolation of one CGA object into another (as per Direct Linear Interpolation of Conformal Geometric Objects). We can acheive this very easily with the following few lines of python and a bit of copy and paste.

In [11]:
# Define our 
def interpolate_circles_linearly(L1,L2,n_steps=50,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 = GAScene()
    for alpha in alpha_list:
        intermediate_color = (alpha*color_1 + (1-alpha)*color_2)
        intermediate_object = interp_objects_root(L1,L2,alpha)
        intermediary_list.append(intermediate_object)
        sc.add_circle(intermediate_object, 'rgb'+str(tuple([int(c) for c in intermediate_color])))
    return intermediary_list, sc

intermediary_list, finished_scene = interpolate_circles_linearly(circle_a, circle_b)
print(finished_scene)
DrawCircle((0.35652^e123) - (2.33275^e124) - (2.28623^e125) - (0.15416^e134) - (0.09628^e135) - (0.35866^e145) - (5.01848^e234) - (5.02537^e235) + (0.69989^e245) + (0.81784^e345),rgb(0, 255, 0));
DrawCircle((0.49663^e123) - (2.26814^e124) - (2.21184^e125) + (0.74584^e134) + (0.81888^e135) - (0.41811^e145) - (4.93936^e234) - (4.96672^e235) + (0.68492^e245) + (0.68529^e345),rgb(5, 249, 0));
DrawCircle((0.5746^e123) - (2.04695^e124) - (1.99092^e125) + (1.4626^e134) + (1.54543^e135) - (0.43771^e145) - (4.5081^e234) - (4.55881^e235) + (0.62021^e245) + (0.52083^e345),rgb(10, 244, 0));
DrawCircle((0.58843^e123) - (1.77914^e124) - (1.73071^e125) + (1.85892^e134) + (1.94833^e135) - (0.42336^e145) - (3.95383^e234) - (4.02445^e235) + (0.53896^e245) + (0.37772^e345),rgb(15, 239, 0));
DrawCircle((0.56617^e123) - (1.53934^e124) - (1.50134^e125) + (2.01457^e134) + (2.10959^e135) - (0.39357^e145) - (3.44368^e234) - (3.52961^e235) + (0.46476^e245) + (0.27222^e345),rgb(20, 234, 0));
DrawCircle((0.52986^e123) - (1.3453^e124) - (1.31806^e125) + (2.03815^e134) + (2.13876^e135) - (0.36023^e145) - (3.02359^e234) - (3.12141^e235) + (0.40379^e245) + (0.19787^e345),rgb(26, 228, 0));
DrawCircle((0.49022^e123) - (1.19175^e124) - (1.17469^e125) + (1.99805^e134) + (2.10447^e135) - (0.3282^e145) - (2.68679^e234) - (2.79433^e235) + (0.35488^e245) + (0.14494^e345),rgb(31, 223, 0));
DrawCircle((0.45157^e123) - (1.06934^e124) - (1.06171^e125) + (1.92957^e134) + (2.04202^e135) - (0.29892^e145) - (2.4155^e234) - (2.53143^e235) + (0.31537^e245) + (0.10616^e345),rgb(36, 218, 0));
DrawCircle((0.41534^e123) - (0.97012^e124) - (0.97117^e125) + (1.84995^e134) + (1.96863^e135) - (0.27254^e145) - (2.19364^e234) - (2.31714^e235) + (0.28293^e245) + (0.07675^e345),rgb(41, 213, 0));
DrawCircle((0.38185^e123) - (0.88818^e124) - (0.89729^e125) + (1.76748^e134) + (1.89255^e135) - (0.24878^e145) - (2.009^e234) - (2.13955^e235) + (0.25578^e245) + (0.05372^e345),rgb(46, 208, 0));
DrawCircle((0.35097^e123) - (0.81929^e124) - (0.83593^e125) + (1.68608^e134) + (1.81768^e135) - (0.22726^e145) - (1.85266^e234) - (1.98995^e235) + (0.23265^e245) + (0.03512^e345),rgb(52, 202, 0));
DrawCircle((0.32243^e123) - (0.76042^e124) - (0.78417^e125) + (1.60754^e134) + (1.7458^e135) - (0.20763^e145) - (1.7182^e234) - (1.86203^e235) + (0.21261^e245) + (0.01969^e345),rgb(57, 197, 0));
DrawCircle((0.29593^e123) - (0.70937^e124) - (0.73991^e125) + (1.53255^e134) + (1.6776^e135) - (0.18958^e145) - (1.60093^e234) - (1.75118^e235) + (0.195^e245) + (0.00656^e345),rgb(62, 192, 0));
DrawCircle((0.27119^e123) - (0.66455^e124) - (0.70159^e125) + (1.46128^e134) + (1.61326^e135) - (0.17284^e145) - (1.49736^e234) - (1.65399^e235) + (0.17931^e245) - (0.00484^e345),rgb(67, 187, 0));
DrawCircle((0.24796^e123) - (0.62474^e124) - (0.66808^e125) + (1.39363^e134) + (1.55269^e135) - (0.1572^e145) - (1.40487^e234) - (1.56788^e235) + (0.16517^e245) - (0.01495^e345),rgb(72, 182, 0));
DrawCircle((0.22602^e123) - (0.58903^e124) - (0.6385^e125) + (1.32938^e134) + (1.49569^e135) - (0.14247^e145) - (1.32147^e234) - (1.49088^e235) + (0.15229^e245) - (0.02408^e345),rgb(78, 176, 0));
DrawCircle((0.20518^e123) - (0.55671^e124) - (0.61218^e125) + (1.26826^e134) + (1.442^e135) - (0.12851^e145) - (1.24559^e234) - (1.42147^e235) + (0.14046^e245) - (0.03245^e345),rgb(83, 171, 0));
DrawCircle((0.18528^e123) - (0.52722^e124) - (0.58861^e125) + (1.20998^e134) + (1.39135^e135) - (0.11519^e145) - (1.17601^e234) - (1.35845^e235) + (0.12948^e245) - (0.04023^e345),rgb(88, 166, 0));
DrawCircle((0.16617^e123) - (0.50013^e124) - (0.56737^e125) + (1.15426^e134) + (1.34348^e135) - (0.10239^e145) - (1.11175^e234) - (1.30085^e235) + (0.11923^e245) - (0.04758^e345),rgb(93, 161, 0));
DrawCircle((0.14775^e123) - (0.47506^e124) - (0.54814^e125) + (1.10082^e134) + (1.29815^e135) - (0.09002^e145) - (1.052^e234) - (1.24791^e235) + (0.10958^e245) - (0.05459^e345),rgb(98, 156, 0));
DrawCircle((0.12988^e123) - (0.45174^e124) - (0.53064^e125) + (1.04942^e134) + (1.25515^e135) - (0.07799^e145) - (0.99612^e234) - (1.19899^e235) + (0.10045^e245) - (0.06137^e345),rgb(104, 150, 0));
DrawCircle((0.11249^e123) - (0.42992^e124) - (0.51467^e125) + (0.99983^e134) + (1.21426^e135) - (0.06624^e145) - (0.94356^e234) - (1.15357^e235) + (0.09174^e245) - (0.06797^e345),rgb(109, 145, 0));
DrawCircle((0.09548^e123) - (0.40939^e124) - (0.50003^e125) + (0.95181^e134) + (1.1753^e135) - (0.05469^e145) - (0.89387^e234) - (1.11122^e235) + (0.08339^e245) - (0.07448^e345),rgb(114, 140, 0));
DrawCircle((0.07876^e123) - (0.39^e124) - (0.48659^e125) + (0.90518^e134) + (1.13812^e135) - (0.04327^e145) - (0.84665^e234) - (1.07158^e235) + (0.07535^e245) - (0.08095^e345),rgb(119, 135, 0));
DrawCircle((0.06228^e123) - (0.37158^e124) - (0.47423^e125) + (0.85973^e134) + (1.10257^e135) - (0.03193^e145) - (0.80158^e234) - (1.03433^e235) + (0.06756^e245) - (0.08744^e345),rgb(124, 130, 0));
DrawCircle((0.04595^e123) - (0.35403^e124) - (0.46283^e125) + (0.81529^e134) + (1.06852^e135) - (0.02059^e145) - (0.75837^e234) - (0.99922^e235) + (0.05997^e245) - (0.094^e345),rgb(130, 124, 0));
DrawCircle((0.0297^e123) - (0.33722^e124) - (0.45232^e125) + (0.77167^e134) + (1.03587^e135) - (0.00921^e145) - (0.71676^e234) - (0.96603^e235) + (0.05255^e245) - (0.10068^e345),rgb(135, 119, 0));
DrawCircle((0.01348^e123) - (0.32107^e124) - (0.44263^e125) + (0.7287^e134) + (1.0045^e135) + (0.00229^e145) - (0.67652^e234) - (0.93456^e235) + (0.04526^e245) - (0.10754^e345),rgb(140, 114, 0));
DrawCircle(-(0.00279^e123) - (0.30549^e124) - (0.4337^e125) + (0.68622^e134) + (0.97434^e135) + (0.01397^e145) - (0.63746^e234) - (0.90464^e235) + (0.03805^e245) - (0.11463^e345),rgb(145, 109, 0));
DrawCircle(-(0.01917^e123) - (0.2904^e124) - (0.42547^e125) + (0.64404^e134) + (0.94531^e135) + (0.02591^e145) - (0.59938^e234) - (0.87612^e235) + (0.03091^e245) - (0.12202^e345),rgb(150, 104, 0));
DrawCircle(-(0.03573^e123) - (0.27573^e124) - (0.41792^e125) + (0.60199^e134) + (0.91736^e135) + (0.03819^e145) - (0.56211^e234) - (0.84888^e235) + (0.02378^e245) - (0.12977^e345),rgb(156, 98, 0));
DrawCircle(-(0.05253^e123) - (0.26142^e124) - (0.411^e125) + (0.55987^e134) + (0.89043^e135) + (0.0509^e145) - (0.52549^e234) - (0.8228^e235) + (0.01665^e245) - (0.13797^e345),rgb(161, 93, 0));
DrawCircle(-(0.06966^e123) - (0.2474^e124) - (0.40469^e125) + (0.51746^e134) + (0.86449^e135) + (0.06415^e145) - (0.48935^e234) - (0.79778^e235) + (0.00948^e245) - (0.14671^e345),rgb(166, 88, 0));
DrawCircle(-(0.08719^e123) - (0.23362^e124) - (0.39897^e125) + (0.47454^e134) + (0.83953^e135) + (0.07807^e145) - (0.45355^e234) - (0.77371^e235) + (0.00224^e245) - (0.15611^e345),rgb(171, 83, 0));
DrawCircle(-(0.1052^e123) - (0.22002^e124) - (0.39383^e125) + (0.43083^e134) + (0.81553^e135) + (0.09281^e145) - (0.41793^e234) - (0.75052^e235) - (0.00511^e245) - (0.16629^e345),rgb(176, 78, 0));
DrawCircle(-(0.12378^e123) - (0.20654^e124) - (0.38925^e125) + (0.386^e134) + (0.79252^e135) + (0.10857^e145) - (0.38234^e234) - (0.7281^e235) - (0.0126^e245) - (0.17743^e345),rgb(182, 72, 0));
DrawCircle(-(0.14302^e123) - (0.19313^e124) - (0.38521^e125) + (0.33969^e134) + (0.77056^e135) + (0.12558^e145) - (0.34661^e234) - (0.70636^e235) - (0.02026^e245) - (0.18974^e345),rgb(187, 67, 0));
DrawCircle(-(0.16301^e123) - (0.17971^e124) - (0.3817^e125) + (0.29142^e134) + (0.74971^e135) + (0.14414^e145) - (0.31059^e234) - (0.68521^e235) - (0.02813^e245) - (0.2035^e345),rgb(192, 62, 0));
DrawCircle(-(0.18382^e123) - (0.16623^e124) - (0.37867^e125) + (0.24058^e134) + (0.73012^e135) + (0.16466^e145) - (0.27411^e234) - (0.66449^e235) - (0.03624^e245) - (0.21906^e345),rgb(197, 57, 0));
DrawCircle(-(0.2055^e123) - (0.15262^e124) - (0.37605^e125) + (0.18641^e134) + (0.71199^e135) + (0.18764^e145) - (0.237^e234) - (0.64403^e235) - (0.0446^e245) - (0.23691^e345),rgb(202, 52, 0));
DrawCircle(-(0.22803^e123) - (0.1388^e124) - (0.3737^e125) + (0.1279^e134) + (0.69561^e135) + (0.21381^e145) - (0.19912^e234) - (0.62354^e235) - (0.05321^e245) - (0.2577^e345),rgb(208, 46, 0));
DrawCircle(-(0.25121^e123) - (0.12469^e124) - (0.37132^e125) + (0.06367^e134) + (0.68142^e135) + (0.24412^e145) - (0.16038^e234) - (0.60255^e235) - (0.06202^e245) - (0.28232^e345),rgb(213, 41, 0));
DrawCircle(-(0.27454^e123) - (0.11021^e124) - (0.36838^e125) - (0.00814^e134) + (0.67008^e135) + (0.27992^e145) - (0.1208^e234) - (0.58027^e235) - (0.07085^e245) - (0.31205^e345),rgb(218, 36, 0));
DrawCircle(-(0.29681^e123) - (0.09528^e124) - (0.36377^e125) - (0.09007^e134) + (0.66252^e135) + (0.32308^e145) - (0.0807^e234) - (0.55525^e235) - (0.07933^e245) - (0.34865^e345),rgb(223, 31, 0));
DrawCircle(-(0.31532^e123) - (0.07986^e124) - (0.35529^e125) - (0.18542^e134) + (0.66007^e135) + (0.37609^e145) - (0.04108^e234) - (0.52473^e235) - (0.08661^e245) - (0.39454^e345),rgb(228, 26, 0));
DrawCircle(-(0.32431^e123) - (0.06399^e124) - (0.33847^e125) - (0.29777^e134) + (0.66436^e135) + (0.44186^e145) - (0.00432^e234) - (0.48336^e235) - (0.09086^e245) - (0.45264^e345),rgb(234, 20, 0));
DrawCircle(-(0.3121^e123) - (0.04789^e124) - (0.30467^e125) - (0.42831^e134) + (0.6766^e135) + (0.52193^e145) + (0.02447^e234) - (0.42114^e235) - (0.0885^e245) - (0.5249^e345),rgb(239, 15, 0));
DrawCircle(-(0.25917^e123) - (0.03196^e124) - (0.23993^e125) - (0.56688^e134) + (0.69488^e135) + (0.61049^e145) + (0.03664^e234) - (0.3224^e235) - (0.07367^e245) - (0.60695^e345),rgb(244, 10, 0));
DrawCircle(-(0.14875^e123) - (0.01632^e124) - (0.13315^e125) - (0.67696^e134) + (0.70956^e135) + (0.68379^e145) + (0.02526^e234) - (0.1755^e235) - (0.04186^e245) - (0.67822^e345),rgb(249, 5, 0));
DrawCircle(-(0.70711^e134) + (0.70711^e135) + (0.70711^e145) - (0.70711^e345),rgb(255, 0, 0));