MikeRandrup

pet projects in code, graphics, and more.

By

Find Point along Bezier Curve in JavaScript

I adapted this from an excellent C++ example yesterday and wanted to share. This is for an HTML5 game (like) project I’m working on where interpolating along vector art created in Adobe Illustrator is needed (more on that in a future post hopefully!)

 Javascript |  copy code |? 
01
02
///////////////////////////////////////////////////////////////
03
// JavaScript Implementation of the DeCasteljau Algorithm
04
// Based on C++ Implementation from 
05
//       http://cubic.org/docs/bezier.htm
06
// which credits Nils Pipenbrinck aka Submissive/Cubic & $eeN
07
// this particular implementation written by Mike Randrup Aug 30 2012
08
///////////////////////////////////////////////////////////////
09
 
10
/* Usage:
11
 
12
	// A & D are the start and end points of the line
13
	// B & C are the handle (influence) points
14
 
15
	myPointA = bezier.point(40,100);
16
	myPointB = bezier.point(80,20);
17
	myPointC = bezier.point(150,180);
18
	myPointD = bezier.point(260,100);
19
 
20
	time = 0.5; // halfway through curve
21
 
22
	resultPoint = bezier.calc(
23
		myPointA, myPointB,
24
		myPointC, myPointD,
25
		time
26
	);
27
 
28
*/   
29
 
30
 
31
var bezier = (function(){
32
 
33
	var Point = function (x, y) {
34
		this.x = x || 0;
35
		this.y = y || 0;
36
		return this;
37
	}
38
 
39
	var interpolateLinear = function (pointA, pointB, time) {
40
		return {
41
			x: pointA.x + (pointB.x - pointA.x) * time,
42
			y: pointA.y + (pointB.y - pointA.y) * time,
43
		}
44
	}
45
 
46
	var bezObj = {
47
 
48
		point: function(x, y) {
49
			return new Point(x, y);
50
		},
51
 
52
		calc: function(p1, p2, p3, p4, time) {
53
			var ab = interpolateLinear(p1, p2, time),
54
				bc = interpolateLinear(p2, p3, time),
55
				cd = interpolateLinear(p3, p4, time),
56
				abbc = interpolateLinear(ab, bc, time),
57
				bccd = interpolateLinear(bc, cd, time);
58
 
59
			return interpolateLinear(abbc, bccd, time);
60
		},
61
 
62
	}
63
 
64
	return bezObj;
65
 
66
}());
67
 
68
console.log("bezier loaded", bezier);   
69
 
70
///////////////////////////////////////////////////////////////
71
// Also check out http://13thparallel.com/archive/bezier-curves/
72
// for an alternate bezier option in JavaScript
73
///////////////////////////////////////////////////////////////
74

Plotting and animating the resulting curve in a canvas is a good way to see it working. Here is what that might look like.

 HTML5 |  copy code |? 
01
02
<html lang="us-en">
03
 
04
<head>
05
<title>Bezier curve test by Mike Randrup</title>
06
</head>
07
 
08
<body>
09
 
10
<canvas id="output" width="600" height="400"></canvas>
11
 
12
<script type="text/javascript" src="bezier.js"></script>
13
<script type="text/javascript">
14
   // the below javascript snippet goes inline here
15
</script></body></html>

This snippet goes into the HTML5 markup above. (I needed to separate to fix a problem with my new WordPress code formatting plugin)

 Javascript |  copy code |? 
01
02
	window.onload = function() {
03
 
04
		console.log("main running");
05
 
06
		var i,
07
			t,
08
			steps = 200,
09
			resultPoint,
10
 
11
			myPointA = bezier.point(40,100),
12
			myPointB = bezier.point(80,20),
13
			myPointC = bezier.point(700,180),
14
			myPointD = bezier.point(550,100),
15
 
16
			canvasEl = document.getElementById("output"),
17
			canvasContext = canvasEl.getContext("2d");
18
 
19
		var nextFrame = function() {
20
			moveControlPoints();
21
			drawCurve();
22
		};
23
 
24
		var moveControlPoints = function() {
25
			if (myPointA.x>0 && myPointA.x&lt;600) myPointA.x++;
26
			if (myPointB.x>0 && myPointB.x&lt;600) myPointB.x++;
27
 
28
			if (myPointC.y>0 && myPointC.y&lt;400) myPointC.y--;
29
			if (myPointD.x>0 && myPointD.x&lt;600) myPointD.x--;
30
 
31
		};
32
 
33
		var drawCurve = function () {
34
			canvasContext.fillStyle = "#FFF";
35
			canvasContext.fillRect(0,0,600,400);
36
 
37
			canvasContext.fillStyle = "#F00";
38
 
39
			for (i=0; i<steps ; i++) {
40
				t = i / (steps-1);
41
 
42
				resultPoint = bezier.calc(
43
					myPointA, myPointB,
44
					myPointC, myPointD,
45
					t
46
				);
47
 
48
				canvasContext.fillRect(resultPoint.x, resultPoint.y, 2, 2);
49
			}
50
		};
51
 
52
		setInterval(nextFrame, 50); // I don't like this here.
53
 
54
 
55
	}
56

By

Realtime 3D version of Water Flow Simulation written for Microsoft XNA Framework

Though this is admittedly from earlier this year, hopefully it’s still interesting.  This is the realtime 3D experiment written on the Microsoft XNA (Xbox, Windows, Windows Phone) Gaming Framework in C#. This is some of my earliest work in the C# language, so much refactoring could occur.

Here is the slide deck from a talk I gave on this (contains video clips of results):

The diff analysis on the code between tutorial stage and current stage illustrates my contributions and technique for the water calculation:

Starting point is tutorial from Riemer Grootjans here: http://www.riemers.net/eng/Tutorials/XNA/Csharp/series4.php Each additional commit shows my changes to achieve water effects. THANK YOU to Riemer!

The core technique is worth discussing, as is the experience of working in XNA, as well as observations on how realtime 3D rendering is handled in this framework.  Hopefully a future blog post will follow.

By

3D Land & Water Coding Experiment

Have you ever played the video game called From Dust? It contains intriguing land, water, and lava simulation. Since first playing it, I have been fascinated with the results of Ubisoft’s very interesting simulation system. I developed an inspired guess as to how the basic algorithm and technique might work. During some holiday downtime, I put code to the concept and came up with the following:

The algorithm steps through a pair of 2 Dimensional arrays containing the height of land and water at each coordinate in the grid. During each step, it determines if and where the water would run downhill, then adjusts the old and new values accordingly. The resolution of the grid was limited by the performance of Flash, which I could no doubt have improved dramatically by using more advanced stage drawing techniques. After running and adjusting the simulations in Flash, I switched the movie to export a JPG image of each frame. That sequence was then run through After Effects (frame blend) to smooth the adjustments. The final sequences were then applied as separate Y-axis displacement maps on “land” and “water” objects in Lightwave 3D.

In the first of the two animations (mountain springs running downhill into a valley), a trio of simple “water emitters” put out a finite amount of water at the tops of the hills, and the algorithm runs it downhill to pool at the bottom. The land was generated with high values at the edges, and low values at the center, with a touch of randomization of the terrain (a “valley”).

In the second (sea mountain rising), a flat land mass with water sitting on top (an “ocean”) is affected by a strong “land emitter” which pushes a mountain out of the sea floor. The water is reacting by being displaced downhill from the land. The circular wave of water was the natural product of the algorithm I created, which seems to indicate being on the right track for how the video game works internally.

Why generate the frames in Flash AS3? Frankly, that choice was due to my own limitations as a programmer. Also, my algorithm fails to ever resolve the water to a natural state of a flat surface.

I now have an even greater sense of awe for what the Ubisoft team achieved with their amazing video game. For example, the game contains dynamic water foam textures which give realistic cues to how the water runs downhill and around obstacles.

The Flash AS3 source code is below. I realize my coding techniques are horrible, and not even object oriented. Had this been for anything other than a proof-of-concept fun project, many loose ends would be wrapped up. There was nothing else in the Flash movie, as it creates all needed movieclips dynamically. If you want to try it, just create a new Flash (ActionScript v3) document and paste the below into the frame 1 actions. It reflects the coding for the “mountain rising out of the sea” effect.

 ActionScript 3 |  copy code |? 
001
002
import flash.display.MovieClip;
003
import flash.events.MouseEvent;
004
import flash.events.KeyboardEvent;
005
import flash.geom.Matrix;
006
//import com.adobe.images.JPGEncoder; // external library used for saving frames
007
 
008
stop();
009
var frameNum:int = 99;
010
 
011
var resX:int = 40;
012
var resY:int = resX;
013
 
014
var paintStrength:Number = 1;
015
 
016
var tileSpacing:int = 0;
017
var tileWidth:int = stage.stageWidth/(resX+(resX*tileSpacing));
018
var tileHeight:int = stage.stageHeight/(resY+(resY*tileSpacing));
019
 
020
var alreadyEmitted:Number = 0;
021
var maxEmitted:Number = 0;
022
 
023
var edgeValue:Number =  0;
024
var maxBoundary:int = resX-1;
025
var drawValue:Number = paintStrength;
026
var editMode:String = "water";
027
var hideLand:Boolean = false;
028
var hideWater:Boolean = false;
029
 
030
var waterInit:Number = 0.1;
031
var landInit:Number = 0.05;
032
 
033
var updateTimer:Timer = new Timer(250);
034
updateTimer.addEventListener(TimerEvent.TIMER, advanceFrame);
035
updateTimer.start();
036
 
037
// used to capture frames in non-realtime
038
//stage.addEventListener(KeyboardEvent.KEY_DOWN, advanceFrame);
039
 
040
function advanceFrame(e:TimerEvent):void{
041
	updateMap(null)//commands
042
}
043
 
044
var parent_mc:MovieClip = new MovieClip();
045
stage.addChild(parent_mc);
046
 
047
var landMap:Array = new Array();
048
var landHeight:Number = 0;
049
landMap.push(new Array());
050
for (var yInit:int=0; yInit<resy ; yInit++) {
051
	var newRow:Array = new Array();
052
	//newRow.splice(0,newRow.length);
053
	newRow[0] = new Array();
054
	for (var xInit:int=0; xInit<resX; xInit++) {
055
		landHeight = landInit;//+(Math.random()*.1);
056
		newRow[0].push(landHeight);
057
	}
058
	landMap[0].push(newRow);
059
}
060
 
061
var waterMap:Array = new Array();
062
waterMap.push(new Array());
063
for (yInit=0; yInit<resY; yInit++) {
064
	var waterRow:Array = new Array();
065
	//newRow.splice(0,newRow.length);
066
	waterRow[0] = new Array();
067
	for (xInit=0; xInit<resX; xInit++) {
068
		waterRow[0].push(waterInit);
069
	}
070
	waterMap[0].push(waterRow);
071
}
072
 
073
function calculate_new(x:int, y:int):void {
074
	var newVal:Number = 0;
075
	var newDelta:Number = 0;
076
	var proposedX:int = 0;
077
	var proposedY:int = 0;
078
 
079
	var minLevel:Number = 100;
080
	var minX:int = 0;
081
	var minY:int = 0;
082
 
083
	var curLand:Number;
084
	var curWater:Number;
085
	var checkLand:Number;
086
	var checkWater:Number;
087
 
088
	curLand = landMap[0][y][0][x];
089
	curWater = waterMap[0][y][0][x];
090
	for ( var neighborX:int=-1; neighborX<=1; neighborX++) {
091
		for ( var neighborY:int=-1; neighborY<=1; neighborY++) {
092
 
093
			proposedX = x+neighborX;
094
			proposedY = y+neighborY;
095
			if (
096
				(proposedX&lt;0) || (proposedY&lt;0) ||
097
				(proposedX>maxBoundary) || (proposedY>maxBoundary) 
098
			) {
099
				//trace('skipping edge');
100
			}
101
			else {
102
				checkLand = landMap[0][proposedY][0][proposedX];
103
				checkWater = waterMap[0][proposedY][0][proposedX];
104
				if (neighborX==0 && neighborY==0) {
105
					//trace('skipping self');
106
				}
107
				else {
108
					if ((checkLand+checkWater)<minlevel ) {
109
						minLevel = checkLand+checkWater;
110
						minX = proposedX;
111
						minY = proposedY;
112
						//trace('new tallness found: ' + minLevel);
113
					}
114
				}
115
			}
116
		}
117
	}
118
	compare_cells(x, y, minX, minY);
119
}
120
 
121
function compare_cells(curX:int, curY:int, checkX:int, checkY:int):void {
122
	var delta:Number;
123
	var curLand:Number = landMap[0][curY][0][curX];
124
	var curWater:Number = waterMap[0][curY][0][curX];
125
	var checkLand:Number = landMap[0][checkY][0][checkX];
126
	var checkWater:Number = waterMap[0][checkY][0][checkX];
127
	var idealLevel:Number = 0;
128
 
129
	// if there is water here
130
	if (curWater > 0) {
131
		//if this water should fall
132
		if ((curLand+curLand) > (checkLand+checkWater) ) { 
133
 
134
			// get the goal average height between the two including water
135
			idealLevel = (curLand + curWater + checkLand + checkWater)/2;
136
			if (idealLevel > curLand) { idealLevel = curLand};
137
 
138
			delta = curLand + curWater - idealLevel;
139
			delta *= 0.3;
140
			waterMap[0][curY][0][curX] -= delta;
141
			waterMap[0][checkY][0][checkX] += delta;
142
		}
143
	}
144
}
145
 
146
function updateMap(eventObject:MouseEvent):void {
147
	frameNum++;
148
 
149
	// clean up stage from last update
150
    if(parent_mc.numChildren!=0){
151
        var cond:int = parent_mc.numChildren;
152
        while( cond -- ) {
153
            parent_mc.removeChildAt( cond );
154
        }
155
    }
156
 
157
	var shading:Number = 0;
158
	for (var k in waterMap[0]) {
159
		for (var i in waterMap[0][k][0]) {
160
			var square:Shape = new Shape();
161
			var squareMC:MovieClip = new MovieClip();
162
			calculate_new(i,k);
163
 
164
			if (waterMap[0][k][0][i]>0.001 ) { // water is on this spot
165
				shading = get_water_color(k,i);
166
			}
167
			else { // land is showing on this spot
168
				shading = get_land_color(k,i);
169
			}
170
 
171
			square.graphics.beginFill(shading);
172
			square.graphics.drawRect(0, 0, tileWidth, tileHeight);
173
			square.graphics.endFill();
174
 
175
			squareMC.addChild(square);
176
			squareMC.x = (i * (tileWidth + tileSpacing + tileSpacing)) + tileSpacing;
177
			squareMC.y = (k * (tileHeight + tileSpacing + tileSpacing)) + tileSpacing;
178
 
179
			parent_mc.addChild(squareMC);
180
		}
181
	}
182
 
183
	//save_screenshot();  // used to capture frames in non-realtime
184
 
185
	// water emmitters
186
	emit_water();
187
	progress_land();
188
	trace(frameNum);
189
}
190
 
191
function get_land_color(y:int, x:int):Number {
192
	var color:Number = (int)(landMap[0][y][0][x] * 0xFF);
193
	if (color < 0) { color = 0 }
194
	if (color > 255) { color = 255 }
195
	color = (color < < 16) | (color << 8) | color; // maps to grey of RGB
196
	return(color);
197
}
198
 
199
function get_water_color(y:int, x:int):Number {
200
	var color:Number = 0;
201
	if (waterMap[0][y][0][x] >0)
202
		color = (waterMap[0][y][0][x] + landMap[0][y][0][x]) * 0xFF;
203
 
204
	if (color < 0) { color = 0 }
205
	if (color > 255) { color = 255 }
206
 
207
	// the below line draws the water in greyscale, otherwise shades of blue
208
	// as in an RGB value, the blue is in the least significant digits
209
	//color = (color < < 16) | (color << 8) | color; // maps to grey of RGB
210
	return(color);
211
}
212
 
213
/* 
214
function save_screenshot():void {
215
	var jpgSource:BitmapData = new BitmapData (stage.stageWidth, stage.stageHeight);
216
	jpgSource.draw(stage);
217
 
218
	var jpgEncoder:JPGEncoder = new JPGEncoder(100);
219
	var jpgStream:ByteArray = jpgEncoder.encode(jpgSource);
220
	var file:FileReference = new FileReference();
221
 
222
	file.save(jpgStream, 'riseLand-'+frameNum+'.jpg');
223
}
224
*/
225
 
226
function emit_water():void {
227
	if (alreadyEmitted < maxEmitted) {
228
		if (waterMap[0][int(resY*.30)][0][int(resX*0.95)]<.3) {
229
			waterMap[0][int(resY*.30)][0][int(resX*0.95)] += .2;
230
			alreadyEmitted += .2;
231
		}
232
		if (waterMap[0][int(resY*.10)][0][int(resX*0.45)]<.3) {
233
			waterMap[0][int(resY*.10)][0][int(resX*0.45)] += .1;
234
			alreadyEmitted += .1;
235
		}
236
 
237
		if (waterMap[0][int(resY*.85)][0][int(resX*0.15)]<.3) {
238
			waterMap[0][int(resY*.85)][0][int(resX*0.15)]+= .05;
239
			alreadyEmitted += .05;
240
		}
241
	}
242
}
243
 
244
var landAdded:Number = 0;
245
var landMax:Number = 200;
246
var cur_landshift:Number = 0;
247
var max_landshift:Number = 100;
248
 
249
function progress_land():void {
250
	var proposedX:int;
251
	var proposedY:int;
252
 
253
	// these are the land emitters in the center.
254
	if (landAdded < landMax) {
255
		landMap[0][int(resY*.5)][0][int(resX*0.5)]+= 2;
256
		landAdded += 2;
257
	}
258
 
259
	cur_landshift++;
260
	if (cur_landshift > max_landshift) { return; }
261
 
262
 
263
	for (var k in landMap[0]) {
264
		for (var i in landMap[0][k][0]) {
265
			for ( var neighborX:int=-1; neighborX< =1; neighborX++) {
266
				for ( var neighborY:int=-1; neighborY<=1; neighborY++) {
267
					proposedX = i+neighborX;
268
					proposedY = k+neighborY;
269
					if (
270
						(proposedX&lt;0) || (proposedY&lt;0) ||
271
						(proposedX>resX-1) || (proposedY>resY-1) 
272
					) {
273
						//trace('skipping land edge');
274
					}
275
					else {
276
						shift_land(i, k, proposedX, proposedY);
277
					}
278
				}
279
			}
280
		}
281
	}
282
}
283
 
284
function shift_land(curX:int, curY:int, checkX:int, checkY:int):void {
285
	var delta:Number = landMap[0][curY][0][curX] - landMap[0][checkY][0][checkX];
286
	delta *= .5;
287
	landMap[0][curY][0][curX] -= delta;
288
	landMap[0][checkY][0][checkX] += delta;
289
}
290
 
291
</minlevel></resy>

By

Work Stuff: Motion Graphics for Splash Coupons

These two videos were created to help the Splash Coupons sales force. The videos were available online, and also on iPads the sales team took to appointments. After Effects was my tool of choice. It was my first project getting to use After Effects after completing training. I’m looking forward to creating better and better results in future projects.

One feature in these videos is the “traveling line effect” in the background of both videos (most visible in 2nd video). The lines were created in Illustrator using an “expanded” “blend”. This was then brought into After Effects, where a 3D camera was attached to a variation of its own path. After Effects did an amazing job of rendering the vector artwork transformed in 3D space. The voiceover was edited in Adobe Soundbooth CS5. (I’ve now upgraded to Adobe Audition as part of Adobe CS5.5).

Credits: Shana Rose (voice performance), Various iStockPhoto.com Photographers, Mike Randrup (all other roles)

This was the full product introduction video. It contains a call-to-action, directing users to an online calendar that was on the original page (but is not on this blog page).

http://www.youtube.com/watch?v=Lsa3y5g-9eU

By

Work Stuff: Big Brands Poster Design

This print project was for www.BargainBanners.com. Over the years, they have printed banners promoting quite a few national brands. The idea for a poster to show the diverse group of companies seemed natural.
The first task was gathering logo files. Most of them were possible to take from the original banner artwork, but a few needed to be picked up from www.BrandsOfTheWorld.com as vector files. Each logo is superimposed on a small “virtual banner”, which I created in Photoshop back in 2008 as part of the BargainBanners.com logo. The background contains a lovely US Map and “light steaks” courtesy of VectorStock.com. Everything was assembled in InDesign, and output to a PDF for the poster printing.
The finished piece is 4 feet wide by about 3 feet tall, and is now hanging in their banner workshop.

By

Work Stuff: Loading Animation for native iPhone App


Earlier this year, I worked on the graphics used in a Native iPhone App (now live on the App Store) for www.SplashCoupons.com. The app lists offers for home improvement savings for people in the North Dallas, Texas area. When the App first launches, it downloads the latest set of coupons from a data server via XML. During this process, which can take a couple of seconds, I wanted to display a visually interesting animation. So out came the storyboard, and a new After Effects project was born.

Conceptually, the animation was designed to show a user that “coupons” are going “into their iPhone”. The After Effects project was relatively simple. The coupon image assets I created were animated along paths from off screen into the pulsing iPhone in the middle. The screen of the iPhone has an AJAX-style busy cursor superimposed on it. Two layers of After Effects “Particle Playground” effects were blending with a glow effect. Since the particle filter naturally emits from the center outward, I time reversed the particle composition so the particles would flow into the iPhone. There is a colored glow in the center, and concentric circles pulsing inward. The last element, “droplet” pieces of the Splash Coupons logo, fly outward from the phone. The color decisions were based on the style guide for the Splash Coupons logo and brand.

The end deliverable for the animation was an 18 frame looping sequence. It didn’t take up much memory or room in the IPA file, and loops to run as long as needed. I also provided several other button graphics for the user interface.

Hopefully it makes for a nicer wait as the XML downloads in the background. At any rate, it was fun to create.

By

Lightwave 3D v10: Yay! I’ve got new graphic software to create work in…

I have such a back log of finished work this year to post on this blog, and yet haven’t been making it happen. Just added to the category of work will be 3D rendering using Lightwave. They just came out with version 10, and I did a lot of work with it way back in the day starting at version 3.5. So naturally I am blown away by the many new features.

I figure over the next year I can learn and develop some basic 3D skills again. I have a big monster project in mind. On a more practical level, this stuff has already come in handy for my work. We just received back from the printers a brochure with a big 3D illustration of a product name on it.

After installing, I rendered a couple of the example scenes:

That's a wolf, with the kind of artificial fur that Pixar made famous with the giant blue monster in Monsters, Inc. Fun stuff!

This example scene that came with the 3D software was very shiny.

 

By

Work Stuff: Sales Folder (Print Project)

Fun Factoid: Everything in the photo is at half scale, even the tiny 1 inch tall business card.

This was a recent project I completed for our sales team at work. It will be a nicely printed folder filled with sales materials (brochures, price sheets, things like that).

Tiny Mockup of Dark Version:

Sales Folder Mockup - Black Version - Inside

Sales Folder Mockup - Black Version - Inside

Sales Folder Mockup - Black Version - Front

Sales Folder Mockup - Black Version - Front


Tiny Mockup of Light Version:

Sales Folder Mockup - White Version - Front

Sales Folder Mockup - White Version - Front

Sales Folder Mockup - White Version - Inside

Sales Folder Mockup - White Version - Inside


In the end, we went with the version over white. This better matches the other series of collateral items I produced earlier this year (envelope, sales team business cards, a promotional coupon, and notesheet sized letterhead). Can’t wait to get them back from the printer next week!

By

We are glad to have great suppliers

Our raw materials supplier, which also has a service and technical support division, happened to request and offer to come by today.

The meeting was a great conversation, and we very much enjoyed their insight. We figured out pretty quickly that our sales rep was a great one. And our tech (when we need him for advanced service needs) is also very, very good.

What a great day. We definitely realized that we chose the right major supplier.

Can’t post our supplier name here unfortunately, but please private message me if you are in the banner printing business and need a suggestion for who to align with. They provide sign industry inputs (inks, materials, and other such items).

By

Maintaining the manufacturing equipment

This is all done on the Mutoh ValueJet 1204AS we use in one of our businesses. The print feed motor failed, and for good measure, we intended to replace the print feed motor’s encoder (which provides feedback to the machine about the movement the motor generates).

So I did all this earlier today, pausing only to shoot pictures for the blog (and just in case I had to show a real technician what I had done to it later). Naturally this is pretty scary because the machine way expensive to repair or replace. But it went really well.

Next I plan to try some home surgery. There’s a kit for that called “Suture Self”. (thanks to my dad for telling me that joke when I was a kid)

By

It was the extra “space” that killed it.

Just finished troubleshooting a bug in a customer system. It’s a piece of custom software, run from a website, and written in PHP. This software collects orders from customers online, and transfers them into QuickBooks via a custom XML service and the QuickBooks Web Connector. And the other day, after a simple update, it stopped working.

Guess what the problem was? Heinous code changes? Chinese hackers? Faulty QuickBooks update? Nope. It was a single ” ” (space) character.

Several functions of the system require that absolutely no output be sent to the browser screen for various redirect functions, especially surrounding SSL and initiating a SOAP session with QuickBooks. Well, the extra character had been accidentally typed into the visible part of a PHP script file filled with common functions.

I finally found what the problem was after pulling the backup version of the recently changed file out of our Amazon S3 cloud backups (performed via JungleDisk automatically on our affected server at The Planet). Restoring the old version of the file fixed the problem, so I used Komodo code editor to compare the files (results attached).

It turned out that there was an extra ” ” (space) at the end of the file that got sent to the screen too early. And the browser got really confused about the content of the page as a result.

Just more evidence of the complex nature of these systems that make the web go round. Good thing we have solid processes for tracking and fixing! Yay for automatic backups into the cloud.

By

A Difficult Day in Non-Profit Land

The local environmental non-profit group I’m involved with had a significant event happen in its history today. Due to a variety of factors (primarily budget shortfall issues), the board today voted to become an all-volunteer organization effective at the end of June 2009. This is a huge change: the original founder had been a full-time, paid contractor prior to this point. No longer will this be the case, and the process of creating this change is a major one that has big consequences for the founder. No one likes to change jobs, even if their paycheck was sporadic at best. No one likes to release the reins of their own creation.

Our board has bitten off a huge responsibility to ensure that the organization continues to fulfill its mission, and to properly honor the contributions, vision, and work of our founder.

Let’s just say that it all made for a pretty heavy board meeting.

By

Turning Desktop Wallpaper into Real Wallpaper

For this project, I picked an area up my upstairs office that was nestled between some built in shelves and cabinets on one wall. It measured 48 inches wide by 33 inchess tall.

Next I went to my favorite free high resolution desktop wallpaper sites, and looked for something that I wanted to see on my wall. I wanted to find something that included obvious perspective cues to give the illusion of depth, and seemed to have a light source in the middle of the picture where my lamp was going to be shining from. I picked this one without over-thinking it too much for the purposes of the experiment. Many others would have worked, and been much more interesting. I downloaded it at the highest resolution offered (2,560 x 1,600 pixels).

In order to know how my photo would look, I divided the number of pixels I had wide by inches wide (2,560 pixels/48 inches) = 53 DPI (dots per inch). For a section of wall that people will be at least 1-3 feet away from, 50 DPI is fine. 100 DPI is good if people will get less than a foot away at eye level to your wall. This requires less DPI than other printing applications in order to look good from the distance a viewer sees it.

Next I went to an online printing place to have it printed large. I went to BargainBanners.com, (my employer), here. My wallpaper printout arrived well within a week.

When it arrived, I thumb-tacked it to the wall under where the trim was attached.

Since there is a lamp that goes on that part of the shelf, I set it up without its shade to preview how the room’s real lighting would work with the image. Looks like the bulb is practically part of the sky. Exactly the effect I was hoping for.

Wood trim, lampshade, and various office junk put back into place. I’m pretty happy with how it turned out for a first try. What does everyone else think? I have more complicated wood paneling elsewhere in the house. I was thinking of trying something more complex, with a more meaningful image, in the future.

Regarding the copyright of the image, that part is sticky. I believe it would be a better practice to surf on over to iStockPhoto.com to buy the proper rights to an image they have for $5-$12. Better yet, if you have a camera with a lot of megapixels, you can print directly from your photographs, which you already own. My employer sees a lot of this for beautiful engagement photos on huge display at the wedding reception.

By

They didn’t taste very good, so diplomacy was the *only* option.

Mike's SPORE creation: The CroctawI’m sure we’ve all heard quite a bit about Electronic Art’s new game called SPORE.

It allows you to design a creature and develop it over time. You begin as a simple critter swimming around in water, grow onto land and into tribes, and eventually even become a space-faring civilization that travels the galaxy.

All the while you make choices about your creature’s disposition. My creature, the ‘Croctaw’ (pictured), is a hyper-aggressive, hyper-carnivore, war-mongering species that normally devours all other species that get in its way. It was a survival strategy that worked.

Except for once, that is. And the situation called for something drastic and different for dealing with the neighboring creature. You see, they didn’t taste very good, so diplomacy was the only option (unpleasant of an option as it was).

At least the other 99% of the creatures my Croctaw empire have encountered are quite delicious. Plus, I’m sure the Croctaw themselves (being some combination of crawdad, lobster, and crocodile) are probably quite delicious. Lucky for them that they are the dominant species.

SPORE has been fun, and now the Croctaw have been flying around the galaxy conquering all. The game is definitely worth trying.

By

Reconciling 21 months of old personal bank statements?


Checking line by line through 80 pages of old bank statements might not sound like fun. Well, it basically isn’t (though I don’t really mind).

Recently I just finished up a personal project to get the period of time between Jan 2006 and Sep 2007 completely reconciled in my personal books. There were about 1,600 transactions to individually verify.

Once I had eliminated about a dozen small transactions (recording $20 of money spent twice, etc) that didn’t belong on my side, something interesting showed up.

It looks like there was a bank error that had been hidden by my own small errors. The reconciled balance from Oct 2007 through the present had always been within a small margin of where it was supposed to be. Once the little duplicate transactions were stripped out, a $500 bank error became obvious.

So in exchange for the mind numbing work, it looks like the process will net $500 in adjustments that I wouldn’t have noticed otherwise. It had always bothered me a little that I hadn’t ever reconciled these ancient personal bank statements, and I finally did it, and it looks like there’ll even be a payoff.

Just thought I’d share. And now I’d better go something completely non-bookkeeping related before I get too many funny looks.