Click here to Skip to main content
65,938 articles
CodeProject is changing. Read more.
Articles
(untagged)

Illusionary XAML: Reinventing Benham's Top

0.00/5 (No votes)
1 Apr 2018 1  
The mystery of Benham’s top invented in 1895, as well as Fechner color effect, remains not fully uncovered so far. WPF and XAML help to accelerate the research greatly.

Warning: Running and observing the provided applications on a computer screen may potentially trigger seizures for people with photosensitive epilepsy. Viewer discretion is advised.

Epigraph:

If you see a "buffalo" sign on an elephant's cage, do not trust your eyes.

Kozma Prutkov

Contents

Introduction

Many years ago, I was greatly intrigued with one visual oddity produced by a toy I read about in one of the books popularizing psychology. That was a pretty well-known invention called Benham’s top, or Benham’s disk, named after its creator, an amateur scientist and toymaker.

The black-and-white pattern of this disk, when it is spun, produces illusionary effect of variety of pale but distinct colors, called Fechner colors. Unlike many other illusions of human visual perception, this effect is not entirely understood, not even at present days.

No wonder, I immediately reproduced this object using wooden sticks, pieces of cardboard, Whatman paper, India ink and a technical drawing toolset, trying different patterns. To my amazement, I worked immediately, but… only if my pattern was pretty close to the original Benham’s disk. Sometimes, not very noticeable difference totally killed otherwise well-pronounced effect. Probably, most of the time I simply went in wrong direction. Later on, I saw several publications of different authors who offered very different designs and claimed that they produce similar effects; moreover, some even called their patterns “Benham’s top”. Despite all such claims… none of those designs actually worked. Not at all. The mere fact of such publications and claims was another mystery.

So, what’s the big deal? Why some patterns work and other don’t? This is a question which is hard to answer.

Recently, I decided to spend some time to create a fairly convenient tool for experimenting. It is pretty obvious that the time required for creation and testing a pattern is one of the most limiting factors. To make experimenting productive, we need good number of trials and errors. It is also pretty obvious that, at the beginning of the experiments, it would be hard to predict the required features of the pattern editor.

These considerations lead to the idea that the application for experimenting should provide some combination of features. At the very beginning, it should better provide the facility for rendering and rotation of the patterns created by some available external editors. Getting some experience would make further plans clear. When some techniques used to create new patterns are developed, the specialized pattern editor would further accelerate the research, as patterns can be created on the fly and tested immediately, step by step.

This is how the application called “ColorTop” was created. In the present article, I’ll explain how it works and report some basic experimental results.

XAML as Vector Graphics

It seems to be pretty obvious that for our purposes our best bet would be exclusively vector graphics. With vector graphics, we can seamlessly scale our image properly and render it with no quality loss, with minimal pixelation, thanks to the anti-aliasing techniques.

Let’s start with something simple. First of all, I would like to illustrate the power of XAML used as vector graphics.
This article comes with the source code showing two applications: main “ColorTop” and simple introductory application “XamlDemo”. Let’s first look at “XamlDemo”.

This simple application shows my vectorized rendition of the CodeProject mascot. The image is scalable, hence showing little to no pixelation when scaled:

Additional element, the golden star, is placed in the background, and animated using standard WPF animation.

If you look at the provided source code (the project named “XamlDemo”), you will see next to nothing. Even the image seems to be missing. Actually, a single XAML element Canvas, the only element of this type, has it all in its content. It can be found in the only XAML file of this project, “MainWindows.xaml”. Actually, more consistent technique would require vector-graphic part to be placed in a separate file, defining a Resource Dictionary, or a set of such dictionaries, but I wanted to demonstrate a simplest solution.

Note that the Canvas element is placed in a Viewbox element, which defines the scaling behavior of the view. Correct settings for scaling preserving original aspect ratio are achieved be setting a single property: Stretch="Uniform".

Now, where the Canvas content itself came from?

First of all, I need to apologize for less than perfect vectorization of the image. First problem was the limited pixel size and quality of the bitmap source taken from the CodeProject Web site. Ideally, vector image should be created from scratch. How I obtain the vectorized image? Oh, this is done thanks to my “secret weapon”, one of my favorite applications, cross-platform open-source Inkscape. This application would be my best recommendation to anyone who needs 2D vector graphics, and many of those who need any kind of graphics.

In the case of CodeProject logo, I’ve used simple and efficient Inkscape feature Path->TraceBitmap, to get basic Bézier curves out of the bitmap, and then tweaked the curves manually, using the usual vector-graphics controls. After that, I saved the image in both SVG and XAML formats. From the vector-graphics standpoint, SVG format is more feature-reach than XAML and is compact enough, so it should better be preserved in all cases.

This is how the animation is done:

public partial class MainWindow : Window {

    public MainWindow() {
        InitializeComponent();
    } //MainWindow

    void Animate(Canvas svg, Path star) {
        DoubleAnimation da = new DoubleAnimation();
        da.From = 0;
        da.To = 360;
        da.Duration = new Duration(TimeSpan.FromSeconds(5));
        da.RepeatBehavior = RepeatBehavior.Forever;
        RotateTransform rt = new RotateTransform();
        rt.CenterX = svg.Width / 2;
        rt.CenterY = svg.Height / 2;
        star.RenderTransform = rt;
        rt.BeginAnimation(RotateTransform.AngleProperty, da);
    } //Animate

    protected override void OnContentRendered(System.EventArgs e) {
        base.OnContentRendered(e);
        svg.Background = Brushes.Transparent;
        Animate(this.svg, this.star);
    } //OnContentRendered

} //class MainWindow

Actually, this comprises entire application-specific part of code.

The key here is understanding of the access to the object this.star. It is originated from XAML as early as it was created with Inkscape. When we want to manipulate a vector-graphic object originated from SVG, we need to generate a value for corresponding Name property of a corresponding XAML object property. InkSkape generates XAML code with Name values copied from XML id attribute values of SVG. As the attribute id values are unique, generated Name values are also unique, which is one of the requirements.

Therefore, the recipe includes the following step: with Inkscape, the SVG object of interest should be edited with “Object Properties”; its id value should be given the value which is unique in the scope of the document and is also a valid .NET variable/property identificator and is easily recognizable, convenient for the code developer.

Loading XAML Vector Graphics Dynamically

Now, let’s get to next step. Instead of embedding vector graphics, the application “ColorTop” offers loading and saving of XAML files. The graphics can be edited in a specialized way, helping to experiment with features contributing to the Fechner color effect.

Loading itself is pretty much trivial:

using XamlReader = System.Windows.Markup.XamlReader;
using Canvas = System.Windows.Controls.Canvas;
using Viewbox = System.Windows.Controls.Viewbox;

// ...

public MainWindow() {
    // ...
    Action reload = () => {
        if (openDialog.ShowDialog() != true) return;
        using (XmlReader reader = XmlReader.Create(openDialog.FileName)) {
            DependencyObject top = XamlReader.Load(reader) as DependencyObject;
            Canvas canvas = findCanvas(top);
            if (canvas == null) throw new Main.NoCanvasException();
            Viewbox vb = top as Viewbox;
            if (vb == null) {
                vb = new Viewbox();
                vb.Child = canvas;
            } //if
            this.borderAnimationBackground.Child = vb;
            animationState.Animate(animatedElement, animation, canvas);
            string title = getTitle(vb);
            if (title == null)
                title = getTitle(canvas);
            if (title == null)
                title = System.IO.Path.GetFileName(openDialog.FileName);
            animationState.setTitle(this, title);
        } //using
    }; //reload
    // ...
}

The animation is similar to the code shown above. The only notable part of this loading code is related to finding certain XAML elements, first of all, Canvas:

using Canvas = System.Windows.Controls.Canvas;
using LogicalTreeHelper = System.Windows.LogicalTreeHelper;

// ...

static Canvas findCanvas(System.Windows.DependencyObject parent) {
    if (parent == null) return null;
    Canvas result = parent as Canvas;
    if (result != null) return result;
    DependencyObject current = parent;
    var children = LogicalTreeHelper.GetChildren(parent);
    foreach (var child in children) {
        result = child as Canvas;
        if (result != null)
            return result;
        else
            return findCanvas(child as System.Windows.DependencyObject);
    } //loop
    return result;
} //findCanvas

This is done to give some freedom to the author of the XAML file. XAML code created by Inkscape uses Viewbox element as the root document element, but this is not a rule. The top element could be something else, first of all, Canvas. Therefore, top-level Canvas instance should be found. If its parent is Viewbox, it can be used, if not, an instance of Viewbox should be created, as it is shown in the previous code sample.

Saving XAML Vector Graphics

Saving vector-graphic XAML would be even more trivial, if not the design of the application ColorTop where the main window functionality is abstracted out, in order to isolate the graphics editor. This is my technique based in implementation of some interface by some window class. The interface IGeneratorClient is defined in some file which is used for definition of the partial class declaration; and this partial class implements this interface. Thanks to this design, open and save commands are performed in different windows: main window menu has “Open” command, and another window, “Pattern Editor” has the menu with “Save Vector Graphics” item. Despite of that, both operations are conveniently implemented by the main window class.

This form of partial class declaration is especially convenient for windows, as inheritance list of the windows contains only the interface to me implemented, and not the base class. This way, different independent aspects of the window class become segregated into different files, which greatly improve maintainability of code. This is how it looks:

using XamlWriter = System.Windows.Markup.XamlWriter;
using StreamWriter = System.IO.StreamWriter;
using Canvas = System.Windows.Controls.Canvas;
using Viewbox = System.Windows.Controls.Viewbox;

// ...

interface IGeneratorClient {
    void NewPattern(string title);
    void SavePattern(string title, string fileName);
    void Add(string title, Main.Sector sector);
    bool CanUndo();
    bool CanRedo();
    bool Undo();
    bool Redo();
}

public partial class MainWindow : IGeneratorClient {

    void IGeneratorClient.SavePattern(string title, string fileName) {
        Viewbox viewBox = (Viewbox)borderAnimationBackground.Child;
        viewBox.Tag = title;
        using (StreamWriter writer = new StreamWriter(fileName, false))
            XamlWriter.Save(viewBox, writer);
    } //IGeneratorClient.SavePattern

    // ...
}

Playing with the Illusion

The research of the illusion cannot pretend to be comprehensive. However, preliminary experiments with different disk-shaped patterns revealed the following:

  • Some people never observe any color effects. Some of them will try to make fun of you, pretending your demonstration is a hoax. Actually, I don’t even know if the lack of observation of the colors in such people was true or not; one my hypotheses is that those people simply tried to troll me.

  • Bright screen helps to observe the illusion; at the same time, the brightness of the screen should not be painfully high — the observation should be comfortable.

  • The effect is apparent at ≃200-900 rpm; closer to 1000 rpm, it may gradually decay.

  • Direction of rotation makes no apparent difference.

  • Any radial oscillation during rotation of the pattern kills the effect.

  • Any stroboscopic effects also kill the effect.

  • The effect can be observed if at least ≃1/4 of the angular size of the pattern is solid black.

  • Small solid-white round shape in the middle, outlined with black thin circular band, produces additional color effect on its white background.

Some fairly successful samples of the patterns based on these observations are named “Experimental1.xaml”, “Experimental2.xaml” and “Experimental3.xaml”. All of them have been created using the pattern editor described below.

What if You don’t Believe the Demo?

Indeed, even pure monochrome demonstration on a computer screen is not truly monochrome. After all, most modern monitors show color pixels. What you perceive as “white” or “black” is a matter of color balance between color sub-pixels and individual characteristics of color vision. Moreover, there are delicate effects related to those sub-pixels; in particular, it’s easy to see colors on the edges of specially positioned “black-and-white” shapes.

So, if computer demonstration seems to be not convincing enough, you can always build a real top, using a round piece of cardboard and a sharpened stick. You can always print out an SVG or XAML file on a piece of paper, in required scale, and glue it on top of your disk.

If you don’t trust the blackness of your printer, you can even buy a bottle of India (Chinese) ink and paint the pattern manually on top of your printed image. This ink (actually a pigment based on soot) is known for good quality of neutral-black color rendition. Accurate drawing with ink will require a special tool: a ruling pen combined with a drawing compass; this pen is specially designed for the use with this kind of ink, in particular, it cannot be clogged and can easily be cleaned off the dry ink.

My prediction is: if you can see the Fechner color effect on this computer experiments, you will also see it on your paper top, if you use bright lighting and vary rotation speeds.

Pattern Editor

The observation of the effect based on different patterns described above leads to a simple practical rule: a major class of potentially successful patterns can be designed as a combination of only one kind of element: an arc fragment of certain thickness. This shape can be also considered as an analog of a solid-black rectangular shape in polar coordinates. Obviously, this class of shapes covers solid round shapes and rings.

One conclusion from the observations mentioned above is: some randomness of the patterns can be very useful. This conclusion was well justified by the experiments.

All such patterns can be easily created on the fly with the use of the Pattern Editor shown on the top picture.

There is no need to show programming aspects of this editor. It’s enough to note that all parameters can come in either one number, or two blankspace-separated numbers. If there is just on number, the parameter is deterministic. In case of two numbers, the shape is generated with the random value of the parameter, with uniform probability distribution in the interval of values between first and second number. Obviously, another way of entering a deterministic value is a pair of equal numbers.

Conclusions

If we can produce wide range of colors with the help of simple illusion, fully explained or not, who needs monitors or digital TV sets with “real” color pixels?

If someone wanted to argue against visionary illusions, I can provide one sobering argument: all imaging and video techniques are totally based on illusions anyway. No photo or video technology reproduces the full spectrum of the original scenery; instead, each image element (pixel or a grain of photosensitive material) is a composition of three fixed colors, simply because human vision is based on three sorts of cone cells, with three distinctly different responsivity spectra. I don’t even want to elaborate the topic of so called “3D” imaging, which is totally illusionary, even from the standpoint of the retina operation.

Instead of wasting resources on rendering “realistic” screen colors, we should make one more radical practical step: get back to monochrome imaging, compensating lack of colors through the use of effects similar to the Fechner color effect. With elimination of nearly-useless colored sub-pixels, we can focus on getting much more valuable screen characteristics: better contrast and screen resolution which can be achieved with purely gray-scale pixels.

In the field of television and Internet video streaming, we can focus on pure vector imaging. Even though real-time vectorization is a difficult technical problem, we can get enormous gain in the use of available bandwidth and the quality of delivered images and video.

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here