## Sunday, 25 September 2011

### DIY glass free 3D! (part 2)

You'll need to have Asymptote, ImageMagick and a C compiler to do the following.

For you poor Windows users out there, look up CygWin, which is sort of Linux on Windows, or look up Wubi to install Ubuntu along side Windows.

Trying to observe the result of a parallax barrier with a 3D picture/video is hit and miss without a hard reference - alternating vertical black and white lines, one pixel wide.

When you've got a barrier with the right separation, you should see white with one eye and black with the other eye.

You'll notice that the barrier needs to be positioned very carefully - we're trying to align it to the edge of a pixel!

You'll need a "C" compiler for this one

#include <stdio.h>
int main(int argc, char *argv[])
{
if(argc != 3) {
fprintf(stderr, "Usage: vertical-lines X Y\n");
fprintf(stderr, "For example vertical-lines 1400 900\n");
return 1;
}
int X, Y;
if(sscanf(argv, "%u", &X) != 1) {
fprintf(stderr, "Parameter X parse failed.\n");
return 1;
}
if(sscanf(argv, "%u", &Y) != 1) {
fprintf(stderr, "Parameter Y parse failed.\n");
return 1;
}
printf("P1\n");
printf("%u %u\n", X, Y);
int x, y;
for(y = 0; y < Y; ++y) {
printf("0");
for(x = 1; x < X; ++x) {
if(x & 1)
printf(" 1");
else
printf(" 0");
}
printf("\n");
}
return 0;
}

Once you paste the above code into a text file called "vertical-lines.c",
run the following commands
cc -o vertical-lines vertical-lines.c
./vertical-lines 1440 900 > vertical-lines.tiff
convert vertical-lines.tiff vertical-lines.png
Specify the resolution of your laptop screen above. Mine is 1440 x 900.

The "convert" program above is part of ImageMagick.

Open up vertical-lines.png with an image viewer capable of showing the image full screen.

Here's the source code for the swatch I made with asymptote.

Once you get this on your computer, you can create PDF documents with it.

If you know your metrics you'll recognize the 210x297 in the script - A4.
// I'm on a learning curve with Asymptote - my incomplete mental model results
// in a few different ways to add padding to the print area.
// Do let me know if you can clean it up!

import plain;

unitsize(1mm);
// Define the bounds of the page.

// Landscape
//int X = 297, Y = 210;

// Portrait
int X = 210, Y = 297;

// Define our page.
fill((0,0)--(X,0)--(X,Y)--(0,Y)--cycle, white);

// Add a 1cm white border, leaving our print area.
int X0 = 10, Y0 = 10, X1 = X - 10, Y1 = Y - 10;

real lenX = X1 - X0;
real lenY = Y1 - Y0;

// See text for the formula.
real GridWidth = 0.25793958;
real GridDelta = 0.001;

// The range of our calibration grill.
real W0 = GridWidth - 10 * GridDelta;
real W1 = GridWidth + 10 * GridDelta;

// We want the black lines to be slightly thicker, so we pad each side.
real pad = 0.005; // 5 microns!

real result = 95.5; // The most promising result I got.

if(true) { // for calibration.
//if(false) { // for the lenticular barrier.
// The calibration step.

// I'll put the calculated value in the middle of the page.
// Sizes get progressively larger above and progressively smaller below.
// Pick a value where the horizontal line is all the same color.

int n, c = 0;
real x0, y0, x1, y1;

////////////////////////////////////////////////////////////////////////////
// Draw a CONTINUOUS grid.
////////////////////////////////////////////////////////////////////////////
y0 = Y0;
y1 = Y1;
x0 = X0;
x1 = X0;
while((x0+W0) <= X1) {
fill((x0,y0)--(x0 + W0, y0)--(x1 + W1, y1)--(x1, y1)--cycle, black);
++c;
// It's 2 * c as we're effectively drawing black, then white, then black...
// Avoid accumulating errors.
x0 = X0 + 2 * c * W0;
x1 = X0 + 2 * c * W1;
}
clip((X0, Y0)--(X1,Y0)--(X1,Y1)--(X0,Y1)--cycle);

// Define our page - again?
// White out the 1cm border.
draw((0,0)--(10,0)--(10,Y)--(0,Y)--cycle, white);
draw((0,0)--(X,0)--(X,10)--(0,10)--cycle, white);
draw((X-10,0)--(X,0)--(X,Y)--(X-10,Y)--cycle, white);
draw((0,Y-10)--(X, Y-10)--(X,Y)--(0,Y)--cycle, white);

// Draw border around the print area.
draw((X0,Y0)--(X1,Y0)--(X1,Y1)--(X0,Y1)--cycle, black);

// Draw horizontal white lines at 1mm intervals to line up the transparency
// and to locate the precise match.
for(n = Y0 + 1; n < Y1; ++n) {
draw((X0 + 0.4, n)--(X1 - 0.4, n), white);
if((n % 10) == 0) {
label(format("%d", n - Y0), (X0, n), align=E, Fill(white));
label(format("%d", n - Y0), (X1, n), align=W, Fill(white));
}
}
}else{
// The lenticular barrier step.

int c = 0;
real t = result / lenY; // 0 <= t <= 1
real W = W0 * (1.0 - t) + W1 * t;
real X0 = X0 + W;
real x = X0;
while((x+W) <= X1) {
++c;
// It's 2 * c as we're effectively drawing black, then white, then black...
x = X0 + 2 * c * W; // Avoid accumulating errors.
}
}
Save the indented text above to a text file named "barrier.asy" then in the same directory run
asy -f pdf -o calibrate.pdf barrier.asy
and you'll get calibrate.pdf in the same directory in a second or two.

This is the calibration file - use it with the grill to find the line which is one color all the way across. Each eye will see a different color, so use just one eye to find it.

The numbers on the left are centered on centimeter boundaries and the white lines are on millimeter boundaries.

Once you have the result, plug it into the script and swap comments for the lines starting with if(true) and if(false) to enable the final print.
asy -f pdf -o barrier.pdf barrier.asy
The PDF format is good to 1/100 of a millimeter - plenty for this purpose.

I used VmWare player to install Windows XP Professional into an image on my Debian laptop (it belonged to an old laptop whose hard disk died).

This way, I could install all the software Canon supplies - Windows only, unfortunately.

One of the little gems included is a manual print head alignment utility.

After printing out three pages and filling in the on-screen forms based on the printouts, I got a lot better results.

Once again, the swatch entry that appeared to work best was slightly smaller than the calculated value, leading me to conclude that the borderless printing option enlarges the print slightly, whether I want it to or not.

My other disappointment was when I printed the same swatch twice.

It seems the printer can't produce identical results, so you end up choosing a different swatch entry each time - a moving target. The first print of the day seemed to be the best one.

Maybe I should get a printer with a "transparency" paper type.

The inkjet transparencies I used are only 100 microns thick.

I've achieved better results by sticking ordinary printer paper onto the back of the transparency with double-sided tape.

You can remove the paper + tape from the transparency after the print.

You'll need two strips along the length of the page, as close to the edge as you dare.

If you didn't align the paper exactly with the transparency, then trim the surplus paper off with a scissors.

The "paper type" printer setting that worked best for me with this setup was "matt photo paper".

This is far from an exact science as it appears that printer pixels aren't exactly square at the precision we're dealing with here, so you'll have to do a different calibration print for portrait and landscape.

Let me know how you get on!