Circle from two points and projected point

Hi Everyone, I’ve been learning GA using enkimute’s Ganja coffeeshop. I have a 2D CGA problem where the wedge of three points does not line up with the points and I wonder what is different.

With three explicit points, this works as expected. However, when one of the points is the projection of a point on a circle, then the resulting circle does not coincide with the projected point.

I wonder if this is because the projection is a point pair? I have used split() to get the correct point.

Any advice is much appreciated.

Instructions: Drag p1 and observe that circle edge does not line up with s1.

// Create a Clifford Algebra with 3,1 metric for 2D CGA. 
Algebra(3,1,()=>{ 

// The conformal model adds in more element types. (circles, point-pairs)
// We no longer work in a dual space. (so ^ = join and & = meet)
// Vectors are points, Bivectors are point pairs, Trivectors are lines/circles

// We don't work directly in the e3/e4 basis, but instead rotate it so we have
// two null vectors to work with (called origin and infinite)
  var ni = 1e4+1e3,           // n-infinite
      no = .5e4-.5e3;         // n-origin
  
  // Define points, lines, circles using the null basis.  
  var point  = (x,y)=>no + x*1e1 + y*1e2 + 0.5*(x*x+y*y)*ni,
      line   = (a,b,c)=>!(a*1e1 + b*1e2 + c*ni),
      circle = (x,y,r)=>!(point(x,y) - r**2/2*ni);
  
  // Distances and Angles. 
  var dist=(x,y)=>(2*(x<<y).Length)**0.5,
      angle=(x,y)=>Math.acos(!x.Normalized<<!y.Normalized);
      
  // Rotator, Translator
  var translate = (v)=>(1-.5*v^ni),
      rotate    = (P,a)=>Math.cos(a/2) - Math.sin(a/2)*(1e12-P<<1e12^ni);
  
  // Some identities
  var line_through_P_parallel_with_X = (P,X)=>(P^ni)<<X*(P^ni),
      project_point_on_circle        = (P,C)=>(P^ni)<<C*C,
      project_point_on_line          = (P,L)=>P<<L.Normalized*L.Normalized,
      position                       = (X)=>{ X=X/(X^ni); return X/(X<<-ni); },
      radius                         = (X)=>Math.abs(((X<<X).s/((X^ni)**2).s))**.5,
      attitude                       = (X)=>(ni^no<<(X^ni)).Normalized,
      split                          = (pp)=>position(pp)-radius(pp)*attitude(pp);
  
  // FIXME?
  var split_point_pair = (pp) => {
    return split(pp)
  }
  
  var p1 = point(0, 1.2, 0)
  var p2 = () => rotate(0, 2*Math.PI/3)>>>p1
  var p3 = () => rotate(0, 2*Math.PI/3)>>>p2
  
  var p4 = () => rotate(0, 2*Math.PI/6)>>>p1
  var p5 = () => rotate(0, 2*Math.PI/3)>>>p4
  var p6 = () => rotate(0, 2*Math.PI/3)>>>p5
  
  var R = () => p1^p2^p3
  
  var petal = (p) => !(p - 0.4**2/2*ni);
  
  var P1 = () => petal(p1)
  var P2 = () => petal(p2)
  var P3 = () => petal(p3)
  var P4 = () => petal(p4)
  var P5 = () => petal(p5)
  var P6 = () => petal(p6)
  
  var origin = point(0, 0, 0)
  
  var X14 = () => split_point_pair(P1 & P4);
  var s1 = () => split_point_pair(project_point_on_circle(origin, P4)) // X14 + (X14 ^ ni).Normalized * 0.1
  
  console.log("p2", p2())
  console.log("s1", s1())
  
  // FIXME: circle doesn't like s1
  var e1 = () => p1 ^ p2 ^ (position(s1));
  
  // Graph these items.
  document.body.appendChild(this.graph([
    "2D CGA - drag p1","",                  // title                   // circles
    0x44AA44,                // lines
    0x4444FF,  p2, "p2", p3, "p3",    // points
    0x6633FF, p4, "p4", p5, "p5", p6, "p6",
    
    0xFF8888, R, "R", P1, "P1", P2, "P2", P3, "P3",
    0xBB88BB, P4, "P4", P5, "P5", P6, "P6",
    
    0x44AA44, // X14, "X14",
    0x4444FF, s1, "s1", 
    e1, "edge", origin, "O",
    
    0x33FF55, p1, "p1",
  ],{conformal:true,grid:true}));                 // conformal flag!  
});

Following Extracting Euclidean Points from Conformal Points - #2 by hadfield.hugo

the new split function is:

  var split = (P) => {
    const denominator = Math.sqrt(-(P * ~P)[0])
    const A = (-1 + P/denominator) * (P | ni)
    return A
  }