Elliptic Pencil

I wanted to play around with generating an “elliptic pencil of circles” starting with the Hermatian matrices. An elliptic pencil of circles are all the circles (or some of them) which all intersect at two specific points. Here is a simple one, where the points of intersection are at (0,1) and (0,-1), or ±i in the complex plane.

You see there is the unit circle, plus the other circles branching off to each side:

The Hermatian matrix template for this particular pencil is elegant:

\begin{bmatrix} 1 & -n \\ -n & -1 \end{bmatrix}

where n is the x coordinate of the center of each circle (or the complex number center, not having an imaginary component).

Here is Racket code used to generate the plot:

(require math/array)
(require math/matrix)
(require plot)

(define (matrix-circle-radius M)
  (let ([A (array-ref M #[0 0])]
        [d (matrix-determinant M)])
    (sqrt (/ d (* -1 (* A A))))))

(define (matrix-circle-center M)
  (let ([C (array-ref M #[1 0])]
        [A (array-ref M #[0 0])])
    (/ C (* -1 A))))

(define (circle-isoline x y r c)
  (isoline
   (lambda (x_ y_) (sqrt (+ (sqr (- x_ x)) (sqr (- y_ y))))) r
   #:color c
   #:width 2))

(define (elipt-plot-demo)
  (plot
   (map (lambda (C)
          (circle-isoline (real-part (first C))
                          (imag-part (first C))
                          (second C)
                          (list 30 160 210)
                          ))
        (map (lambda (M)
               (list (matrix-circle-center M)
                     (matrix-circle-radius M)))
             (map (lambda (n)
                    (matrix+
                     (matrix-scale (array #[#[0 1] #[1 0]]) n)
                     (array #[#[1 0] #[0 -1]])))
                  (range 2.5 -3 -0.5))))
                    
   #:x-min -5
   #:x-max 5
   #:y-min -5
   #:y-max 5
   #:width 400
   #:height 400))

Circles Common Invariant

Schwerdteger describes an interesting “invariant” relationship between any two circles:

\frac{\Delta_{12}}{\sqrt{\Delta_1} \sqrt{\Delta_2}}

where

\Delta_1 = | \mathfrak{C}_1 |, \Delta_2 = | \mathfrak{C}_2 |, and 2 \Delta_{12} = A_1 D_2 + A_2 D_1 - B_1 C_2 - B_2 C_1

with the circles represented as Hermitian matrices \begin{bmatrix} A & B \\ C & D \end{bmatrix}.

The point is, in the end you come up with this single number which represents whether the smaller circle is inside the first, overlapping the first, just touching the first, or outside the first. Here are the cases:

common invariant > 1 : smaller circle contained withing the greater circle

common invariant = 1 : touching from the inside

-1 > common invariant > 1 : overlapping at two points

common invariant = -1 : touching from the outside

common invariant < -1 : completely outside

Here is Racket code used for calculations and plotting:

(require math/array)
(require math/matrix)
(require plot)

(define (circle-to-matrix zC r)
  (let ([B (* -1 (conjugate zC))]
        [mzC (magnitude zC)])
    (matrix [[ 1             B                       ]
             [ (conjugate B) (- (* mzC mzC) (* r r)) ]])))

;; gothic C - 212d
;; delta - 394
(define (invariant ℭ1 ℭ2)
  (let* ([Δ1 (matrix-determinant ℭ1)]
         [Δ2 (matrix-determinant ℭ2)]
         [A1 (array-ref ℭ1 #[0 0])]
         [B1 (array-ref ℭ1 #[0 1])]
         [C1 (array-ref ℭ1 #[1 0])]
         [D1 (array-ref ℭ1 #[1 1])]
         [A2 (array-ref ℭ2 #[0 0])]
         [B2 (array-ref ℭ2 #[0 1])]
         [C2 (array-ref ℭ2 #[1 0])]
         [D2 (array-ref ℭ2 #[1 1])]
         [Δ12 (* 0.5 (+ (* A1 D2)
                        (* A2 D1)
                        (* -1 B1 C2)
                        (* -1 B2 C1)))])
    (/ Δ12 (* (sqrt Δ1) (sqrt Δ2)))))

(define (circle-isoline x y r)
  (isoline
   (lambda (x_ y_) (sqrt (+ (sqr (- x_ x)) (sqr (- y_ y))))) r))

(define (circles-invariant-plot x1 y1 r1 x2 y2 r2 min max)
  (let ([C1 (circle-to-matrix (make-rectangular x1 y1) r1)]
        [C2 (circle-to-matrix (make-rectangular x2 y2) r2)])
    (plot-file
     (list
      (circle-isoline x1 y1 r1)
      (circle-isoline x2 y2 r2)
      (point-label (vector (+ min (* (- max min) 0.1))
                           (+ min (* (- max min) 0.9)))
                   (number->string (invariant C1 C2)) #:point-size 0))
     "out.png"
     #:x-min min
     #:x-max max
     #:y-min min
     #:y-max max
     #:width 400
     #:height 400)))

Representation of Circles by Hermitian Matrices

I borrowed the book Geometry of Complex Numbers by Schwerdtfeger. The material has been fascinating so far, though admittedly it took me about 3 hours to comprehend the material in page 1 of chapter 1. The first subject is this intriguing idea that you can represent a circle as a matrix. More specifically, you you can represent a circle with (complex) center 𝛾 and radius 𝜌 as a matrix

\begin{bmatrix} A & B \\ C & D \end{bmatrix}

Where B = – A𝛾̅, C = -A𝛾, and D = A(𝛾𝛾̅-𝜌²), so that A and D are real numbers, and B and C are complex numbers. For normal circles, you will have A=1, though you can scale the matrix to have other (non-zero) values of A and still have the same circle. (If A is zero, you end up with a straight line, or some other mysterious thing that is not a circle.)

The simple case is the unit circle:

Which is \begin{bmatrix} 1 & 0 \\ 0 & -1 \end{bmatrix}

More exotic is the circle away from the origin, centered at 10+10i, i.e., (10, 10), with radius 3:

\begin{bmatrix} 1 & -10+10i \\ -10-10i & 191 \end{bmatrix}

Here is some code to generate the matrix in Racket, and to put it back:

lang racket

(require math/array)
(require math/matrix)

(define (circle-to-matrix zC r)
  (let ([B (* -1 (conjugate zC))]
        [mzC (magnitude zC)])
    (matrix [[ 1             B                       ]
             [ (conjugate B) (- (* mzC mzC) (* r r)) ]])))

(define (matrix-circle-radius M)
  (let ([A (array-ref M #[0 0])]
        [d (matrix-determinant M)])
    (sqrt (/ d (* -1 (* A A))))))

(define (matrix-circle-center M)
  (let ([C (array-ref M #[1 0])]
        [A (array-ref M #[0 0])])
    (/ C (* -1 A))))
racket@circle.rkt> (circle-to-matrix 0 1)
(array #[#[1 0] #[0 -1]])
racket@circle.rkt> (matrix-circle-radius (circle-to-matrix 0 1))
1
racket@circle.rkt> (matrix-circle-center (circle-to-matrix 0 1))
0

Why is this significant? I think Schwerdtfeger is going to cover that on page 2. :)

Fractal with Barycentric Coordinates

This fractal idea did not originate with me, but I wrote some racket code to do the midpoint calculation using barycentric coordinates. This fractal draws a circle at the midpoint of a triangle, then subdivides the triangle and repeats:

Here is the same fractal to four iterations:

To get the midpoints, I could simple pass in the coordinates of the last triangle ABC, and then use “0.5” barycentric coordinates:

        [P1 (barycentric->complex 0.5 0.5 0 A B C)]
        [P2 (barycentric->complex 0.5 0.0 0.5 A B C)]
        [P3 (barycentric->complex 0 0.5 0.5 A B C)]

Here is the full code:

ftp://lavender.qlfiles.net/Racket/bc-fractal.7z

Barycentric Coordinates

An interesting concept I came across recently in an introductory geometry book is barycentric coordinates. The fundamental idea is defining the location of a point by its relationship to the points of a triangle. (Well, technically you can use any polygon, but we will keep things simple.)

So, each of the vertices of the triangle (A, B, and C) are given a sort of weight or pull factor. These three numbers uniquely represent the position of P. The theorem goes

If A, B, C are three non-collinear points, any vector P may be expressed in terms of the vectors A, B, C thus:

P = xA + yB +zC, where x + y + z = 1

Geometry: A Comprehensive Course, Dan Pedoe

If P is pulled all the way over to vertex A, then the coordinates become (1, 0, 0). Likewise at B it is (0, 1, 0) and for C it is (0, 0, 1). Another coordinate inside the triangle would be (⅓, ⅓, ⅓). If the point moves outside the triangle, some of those coordinates become negative.

Of course, instead of boring old vectors, you can use… complex numbers!

From the formula, it is obvious how to start with some barycentric coordinates, add a triangle, and find the point. Here is some Racket code:

(define (barycentric->complex x y z A B C)
   (+ (* x A) (* y B) (* z C)))

Note, that you can use any triangle you want with the same barycentric coordinates. This could allow you to stretch the graph of some set of coordinates in various ways.

But, how do you convert in the other direction, having a point and a triangle, and wanting the barycentric coordinates? This is a little more complicated. Fortunately, it happens that the factor for each of the vertices is equal to the proportional area of the triangle opposite that vertex. So, looking at this diagram again

x = [BCP] / [ABC], y = [CAP] / [ABC], z = [ABP] / [ABC]
where [BCP] is the area of triangle BCP, etc.

Now, a tricky part here is that the areas must be signed areas, or the formula doesn’t necessarily work correctly. Signed area means that you have to be consistent in assigning a negative or positive value to the area of the triangle, depending on the orientation of the vertices. So, if you happen to plug in the vertices in a counter-clockwise direction, you get a negative area, and in a clockwise direction, you get a positive area. Thankfully, there is an elegant formula for determining this, which I will represent by this Racket function

(define (orientation Z1 Z2 Z3)
  (let* ([rise1 (- (imag-part Z2) (imag-part Z1))]
         [rise2 (- (imag-part Z3) (imag-part Z2))]
         [run1 (- (real-part Z2) (real-part Z1))]
         [run2 (- (real-part Z3) (real-part Z2))]
         [diff (- (* rise1 run2) (* rise2 run1))])
    (if (< diff 0) 'ccw
        (if (> diff 0) 'cw 'col))))

The function returns ‘ccw for counter-clockwise, ‘cw for clockwise, and ‘col for collinear (i.e., the points are on a straight line, which is not relevant in this application).

This allows us to have a signed-area function, using the common formula for determining the area of a triangle from just the length of the sides. We get the length of the sides by subtracting the complex numbers from each other and pulling the magnitudes of the resulting complex numbers.

(define (signed-area A B C)
  (let* ([a (magnitude (- C B))]
         [b (magnitude (- A C))]
         [c (magnitude (- A B))]
         [s (* (+ a b c) 0.5)]
         [area (sqrt (* s (- s a) (- s b) (- s c)))]
         [orient (orientation A B C)])
    (if (eq? orient 'ccw)
        (* -1 area)
        area)))

And finally we determine x, y, and z:

(define (complex->barycentric A B C P)
  (let ([ABC (signed-area A B C)])
    (list (/ (signed-area B C P) ABC)
          (/ (signed-area C A P) ABC)
          (/ (signed-area A B P) ABC))))

See, simple as Pi! Heh, heh.

racket@barycentric.rkt> (complex->barycentric 0 6 2+2i 2+i)
'(0.33333333333333376 0.16666666666666657 0.5000000000000009)
racket@barycentric.rkt> (barycentric->complex 0.33333333333333376 0.16666666666666657 0.5000000000000009 0 6 2+2i)
2.0000000000000013+1.0000000000000018i

So, you can see it works, though there is some very small inaccuracy introduced by the approximations that occur during the conversion operations.

I hope to explore, in future posts, some interesting applications of barycentric coordinates.