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)

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)

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.

Trigonometry Problem without Trigonometry: Clarification/Correction

In the previous post I was exulting about the coolness of solving a trigonometry problem without trigonometry, using complex numbers. Well, that isn’t quite the full truth. You can certainly do the problem described easily and elegantly with a complex number capable calculator. However, there is some hidden trigonometry in the initial rotation operation:

The complex number ei has to be raised to a power. If you raise to an integer power, you could simply multiply out the rectangular values algebraically. But how do you raise a complex number to a fractional power, like 2.5? The standard approach, near as I can tell, is to convert the complex number to polar format, with a magnitude r and an angle ϕ. Assuming complex number a+bi, pulling out the magnitude requires only the sqrt function (pythagorean theorem) but pulling out the angle requires the arctangent of b/a. Then, use the formula Zn = rn (cos nϕ + i sin nϕ). I really don’t know if this is how all calculators do it, but here is the code behind the operations in the racket interpreter:

  Scheme_Complex *cb = (Scheme_Complex *)base;
  Scheme_Complex *ce = (Scheme_Complex *)exponent;
  double a, b, c, d, bm, ba, nm, na, r1, r2;
  int d_is_zero;

  if ((ce->i == zero) && !SCHEME_FLOATP(ce->r)) {
    if (SCHEME_INTP(ce->r) || SCHEME_BIGNUMP(ce->r))
      return scheme_generic_integer_power(base, ce->r);

  a = scheme_get_val_as_double(cb->r);
  b = scheme_get_val_as_double(cb->i);
  c = scheme_get_val_as_double(ce->r);
  d = scheme_get_val_as_double(ce->i);
  d_is_zero = (ce->i == zero);

  bm = sqrt(a * a + b * b);
  ba = atan2(b, a);

  /* New mag & angle */
  nm = scheme_double_expt(bm, c) * exp(-(ba * d));
  if (d_is_zero) /* precision here can avoid NaNs */
    na = ba * c;
    na = log(bm) * d + ba * c;

  r1 = nm * cos(na);
  r2 = nm * sin(na);

The code there is just slightly more complicated, as it has separate angle finding conditional branches for real number and complex number exponents.

Of course, you don’t need to deal with any of that if you are just manipulating complex numbers in the interpreter or calculator.

I feel this would be a great place to dive into the interesting subject of complex roots, but that I should hold off until I have a more thorough grasp of the subject.

Trigonometry Problem without Trigonometry

Here is a classic trigonometry problem:

With the satellite dish pointed up 13 degrees above the ground, how far up will the Line of Sight (LOS) be after passing over 100 meters of ground? (To save a few sentences in our post, we will ignore the fact that the satellite dish will usually start a few meters above the ground.)

This is simple using any calculator with the trigonometry function tangent:

tan(ϕ) = opp/adj, so height = 100 * tan 13° (after 13° is converted to radians).

If you didn’t want to use a trigonometric function, however, what could you do? Another approach is complex numbers, and it is pretty simple. You simply need to rotate ϕ degrees up the unit circle (a basic operation with complex numbers), get the real and imaginary parts of that complex number, and use that proportion to figure out the opposite side of your problem triangle.

So, after raising e^i by 13 degrees (converted to radians) multiply 100 x (imag Z / real Z). In programming code, it looks like this:

#lang racket

(define tau (* 2 pi))

(define rfact (/ tau 360))

(define (rise gnd deg)
  (let* ([rad (* deg rfact)]
         [Z (exp (* 0+1i rad))])
    (/ (* gnd (imag-part Z)) (real-part Z))))

> (rise 100 13)

It gives geometrically sane results also if you use negative degrees or negative distances:

> (rise 100 -13)
> (rise -100 -13)
> (rise -100 13)

What if you don’t have or want to use the exp function (to raise e to some value). You instead start with an approximation of e^i, which happens to be


and then just raise that to ϕ.

Now, why would you want to use the complex number approach rather than the trig function? Well, to be honest I couldn’t think of a really compelling reason off the top of my head. An easy one would be if you were already using complex numbers for other reasons. Another would be if you wanted to use purely algebraic operations in your programming, say, on a computer board that did not have trigonometric functions built in. I suspect that the complex number approach is ultimately simpler and more efficient on the computational level, but since trigonometric functions are usually approximated with tables and with an algebraic exponential function, I couldn’t really say for sure off the top of my head.

In any case, it seems cool that complex numbers allow you to do an angle and distance trigonometry problem without trigonometry.

Harmongrams on Arduino Via Serial Commands

Previously I was experimenting with trying to write advanced programs in uLisp, embedded on an Arduino with a TFT display/touchscreen. Ultimately this proves not the best approach due to limitations in the memory and also the uLisp interpreter. A different approach is to keep the uLisp interpreter on the Arduino, but limit the on-board programming to a set of core interface commands, e.g., (drawline) and (fillscr). Then have the desktop computer or server sending uLisp commands over the serial connection. Of course, the uLisp commands can be constructed in Racket Scheme, allowing the power of the Racket syntax and libraries, and the great CPU and memory access, in controlling the arduino. I.e., remote control by the Mother Brain.

The demo I’ve programmed here is drawing the harmonograms slow enough to watch, so as to simulate the harmonograph experience. I do not have a camera setup suitable to show this in live video, but here are some snapshots:

The Racket code for the demo is available here: (md5sum 7e2bc9bc2ea25d048577b28c95c4fca5)

The project to adapt uLisp with TFT control primitives is here:

Though control over serial cables is doable for some applications, the longer-term goal would be to have them transmitted over Wi-Fi.

Complex Numbers in Emacs Calc Tutorial Video (Part I)

After learning the basics of how to use complex numbers, I find myself suddenly more interested in buttons on scientific calculators, and the built-in functions of calculator and programming software. Something which I thought might be of interest to my small (but distinguished!) audience would be a tutorial on using complex numbers in the Gnu/Emacs Calc program.

Please forgive the quiet audio — you’ll need to turn up the volume a lot. I’m dealing with a not-ideal microphone set-up at the moment, but I hope to have access to a better microphone set-up next week.

I have been advised by well-meaning relatives that, by continuing to upload boring tutorials and technical articles, it is likely that my readership will never expand beyond two dozen people. There is hope, they say, if I were instead to upload cute bunny rabbit photos. Consequently, I am going to start the practice of randomly attaching bunny photos taken from my mother’s Web site about rabbit farming.

I am hoping that, since we are related, my Mom will overlook this blatant act of copyright violation. This image is reproduced for scholarship purposes only under the “fair use” doctrine.

Lossless Compression

A subject I’ve recently taken some interest in is data compression. As far as compression algorithms themselves, you’ll learn nothing in this post that you can’t find out in a few minutes on Wikipedia. Nevertheless, I think there are two things worth pointing out, which are perhaps not known to even many I.T. professionals:

  1. There is no lossless compression algorithm which is optimal for all types of data.
  2. With Free Software programs at least, you have considerable freedom in choosing your compression algorithm and related parameters.

For those not in the know, lossless compression is compression that allows you to compress without losing any information. In contrast, the lossy compression algorithm used in JPEG basically discards some of the image data, sacrificing some fine resolution in such a way that hopefully you won’t notice the difference. PNG compression, on the other hand, is lossless, allowing you to view the image exactly the way you got it originally.

Point (1) above is true because of the mathematical Pidgeon Hole principle. Applied to compression algorithms, this basically means that any algorithm that is good at compressing one type of data, will be bad at compressing at least one other type of data.

Regarding point (2): On a Gnu/Linux system, it is likely that without even having to install anything, you will have at least three compression tools installed: gzip, bzip2, and 7z.

My version of gzip uses LZ77 (Lempel-Ziv) coding.

LZ77 algorithms achieve compression by replacing repeated occurrences of data with references to a single copy of that data existing earlier in the uncompressed data stream.
christopher@nightshade:~/Scratch$ gzip --version | head -n1
gzip 1.6

This is a simple idea that works decently applied to some text data:

christopher@nightshade:~/Scratch$ wget
--2019-03-07 16:34:50--
Resolving (… 2001:470:142:3::a,
Connecting to (|2001:470:142:3::a|:443… connected.
HTTP request sent, awaiting response… 200 OK
Length: 35149 (34K) [text/plain]
Saving to: ‘gpl-3.0.txt’
gpl-3.0.txt 100%[================================================================>] 34.33K --.-KB/s in 0.1s 
2019-03-07 16:34:51 (280 KB/s) - ‘gpl-3.0.txt’ saved [35149/35149]
christopher@nightshade:~/Scratch$ gzip -vc gpl-3.0.txt > gpl-3.0.txt.default.gz
gpl-3.0.txt: 65.5%

Now you can (in principle) increase your compression amount by looking over a larger data window, which is to say, spending more cpu cycles looking for repeats. With gzip you can specify this by specifying a compression level. The default is 6, and you can go up to 9. In this particular case though, it doesn’t make a difference:

christopher@nightshade:~/Scratch$ gzip -v9c gpl-3.0.txt > gpl-3.0.txt.9.gz
gpl-3.0.txt: 65.6%

I haven’t had a chance to spend any quality time with bzip2 yet, but 7z (which technically does both archiving and compressing) by default uses LZMA2 algorithm. In this case (text data) we get only slightly better results, even with the compress level set high:

christopher@nightshade:~/Scratch$ 7z a gpl-3.0.txt.default.7z gpl-3.0.txt

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.utf8,Utf16=on,HugeFiles=on,64 bits,3 CPUs AMD Athlon(tm) II X3 455 Processor (100F53),ASM)

Scanning the drive:
1 file, 35149 bytes (35 KiB)

Creating archive: gpl-3.0.txt.default.7z

Items to compress: 1

Files read from disk: 1
Archive size: 11487 bytes (12 KiB)
Everything is Ok
christopher@nightshade:~/Scratch$ 7z a gpl-3.0.txt.9.7z gpl-3.0.txt -mx=9

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.utf8,Utf16=on,HugeFiles=on,64 bits,3 CPUs AMD Athlon(tm) II X3 455 Processor (100F53),ASM)

Scanning the drive:
1 file, 35149 bytes (35 KiB)

Creating archive: gpl-3.0.txt.9.7z

Items to compress: 1

Files read from disk: 1
Archive size: 11495 bytes (12 KiB)
Everything is Ok

However, 7z allows us to choose from several different compression algorithms. One is PPMd:

Predictions are usually reduced to symbol rankings[clarification needed]. Each symbol (a letter, bit or any other amount of data) is ranked before it is compressed and, the ranking system determines the corresponding codeword (and therefore the compression rate). In many compression algorithms, the ranking is equivalent to probability mass function estimation. Given the previous letters (or given a context), each symbol is assigned with a probability. For instance, in arithmetic coding the symbols are ranked by their probabilities to appear after previous symbols and the whole sequence is compressed into a single fraction that is computed according to these probabilities.

This algorithm is especially good with text data, and gives us much compression in this case:

christopher@nightshade:~/Scratch$ 7z a gpl-3.0.txt.ppm.7z gpl-3.0.txt -m0=PPMd

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.utf8,Utf16=on,HugeFiles=on,64 bits,3 CPUs AMD Athlon(tm) II X3 455 Processor (100F53),ASM)

Scanning the drive:
1 file, 35149 bytes (35 KiB)

Creating archive: gpl-3.0.txt.ppm.7z

Items to compress: 1

Files read from disk: 1
Archive size: 9617 bytes (10 KiB)
Everything is Ok

On a Debian Gnu/Linux system, you can see the algorithms supported by 7z at file:///usr/share/doc/p7zip/DOC/MANUAL/cmdline/switches/method.htm using your Web browser. Our results so far:

christopher@nightshade:~/Scratch$ ls -l
total 96
-rw-r--r-- 1 christopher parents 35149 Sep 29  2017 gpl-3.0.txt
-rw-r--r-- 1 christopher parents 11495 Mar  7 16:41 gpl-3.0.txt.9.7z
-rw-r--r-- 1 christopher parents 12136 Mar  7 16:36 gpl-3.0.txt.9.gz
-rw-r--r-- 1 christopher parents 11487 Mar  7 16:37 gpl-3.0.txt.default.7z
-rw-r--r-- 1 christopher parents 12142 Mar  7 16:35 gpl-3.0.txt.default.gz
-rw-r--r-- 1 christopher parents  9617 Mar  7 16:37 gpl-3.0.txt.ppm.7z

Compression of images and audio is a different animal, for one because the storage format itself usually includes the compression. Perhaps that will be the subject of another post.