Techblog

Tech Blog

Our latest geek adventures!

Archive for November, 2008

26 November Generating thumbnails

I would like to thank you all for helping us build our thumbnail database!
I presume this statement might be in need of some clarification, so bear with me when I go into the technical details on this one.

For every design that is saved on Floorplanner, we create a thumbnail in JPEG format. We use these thumbnails for the gallery, and now we have included them on everyone’s dashboard. However, for various reasons, we do not have a thumbnail available for every design. However, with your help, we are improving the thumbnail database while you our browsing the site!

The thumbnail images are stored on Amazon AWS S3. We know the URL that a thumbnail of a design should have, but we do not know if it actually is available. In the latter case, the result is a nasty image not found placeholder on the dashboard. This of course is not acceptable! We cannot know if a thumbnail exists other than doing a request to the URL and see whether we get an image back from Amazon, or a HTTP 404 status. This is a very time-consuming procedure to run server side so we chose to find a client-side solution.

We found that javascript can be used to check if an image exist. An AJAX call cannot be used, because cross-site calls are not supported. However, the javascript Image object can be used for this purpose.

var img = new Image();
img.onload = function(event) {
  // image was found and loaded successfully
  document.getElementById('img-tag').src = img.src;
};
img.onerror = function(event) {
  // An error occured while loading the image
  document.getElementById('img-tag').src = '/images/thumb-unavailable.jpg';
}
 
// Setting the src property will trigger the events.
img.src = 'http://link.to.amazon.s3/design/thumbnail.jpg';

A nice thumbnail not available image will be shown if the thumbnail file cannot be found on S3. This is much nicer, and the check is completely done client-side! However, we found a way this could even be better by changing the onerror event handler. Instead of displaying a thumbnail not available image, we can simply load a small instance of the Floorplanner application to display a small version of the design. Moreover, we can instruct it to generate a thumbnail JPEG and save it to S3!

So, every time you see a small Floorplanner loading on your dashboard, you are creating a missing thumbnail. Next time, the thumbnail will be available on S3 and there will be no need to start the Floorplanner application. A nice example of distributed computing, mixed with a hint of SETI@home. I like it!

No Comments - Tags: , , , ,

23 November Alchemy – first looks

Adobe has recently released a preview version of Alchemy. From their site:

Welcome the preview release of codename “Alchemy.” Alchemy is a research project that allows users to compile C and C++ code that is targeted to run on the open source ActionScript Virtual Machine (AVM2). The purpose of this preview is to assess the level of community interest in reusing existing C and C++ libraries in Web applications that run on Adobe® Flash® Player and Adobe AIR®.

So, what does this mean? This means that we can use existing C/C++ code and compile that down to AS3. Initially I though that this implied a hefty increase in code execution speed, but as all is compiled down to AS3 this is not true in most cases. Code will only run faster if you ’stay’ on the C-side and only return to ‘AS3′ when your C code is done processing. The reason is that AS3 method-calls are sloooow (params need to be ‘unboxed’ etc.)! When in C this slowness doesn’t occur and hence execution speed will be faster (Adobe claims a potential speed increase by a factor 2 to 10 I beleive). Wow! That of course made me wonder whether Alchemy would be usefull for 3D engines like Papervision3D, more soon! :-)

Branden Hall did a nice writeup on Alchemy explaining above better then me.

I couldn’t resist myself and started off immediately with something I always wanted to code for our 3D engine: Polygon Triangulation with support for holes.

I installed the Alchemy Toolkit, got the Flex 3.2 SDK and got some C code from the Department of Computer Science, UNC Chapel Hill.

Setting up the toolkit was bit tricky, but with help from this page I finally succeeded to get my environment right (OSX). Then I hit ‘make’ and presto! Got my swc! Download the swc and sample code or view a live example.

Some notes on swc usage:
1] ‘outer’ polygons must be defined anti-clockwise
2] ‘inner’ polygons (holes) must be defined clock-wise

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
// import
import cmodule.triangulation.CLibInit;
 
// initialize
var loader:CLibInit = new CLibInit;
var lib:Object = loader.init();
 
// @vertices is an array of XY-pairs: [ [], [x0, y0], [x1, y1], ...]
//                NOTE: @vertices[0] should always be [] 
// @contours is an array containing the number of points of each polygon
//                => [4, 3, 3, 3] indicates 4 polygons with the first poly having 4 points, the second 3, etc.
// @ncontours is the number of polygons (in our example: 4)
//
// @return An array of indices into the vertices array in form: [ [p0, p1, p2], [p0, p1, p2], ...]
var indices : Array = lib.triangulate( vertices, contours, ncontours );

Here’s the relevant C code which was simply added to tri.c :

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
static AS3_Val triangulate(void* self, AS3_Val args)
{
	int ncontours = 0;
	int ccount;
	int npoints, first, last, n, nmonpoly;
	register int i;
	int op[SEGSIZE][3], ntriangles;
	AS3_Val dataVal;
	AS3_Val contourVal;
	AS3_Val retVal;
	AS3_Val temp;
 
	/* initialze the AS3 values */
	dataVal = AS3_Undefined();
	contourVal = AS3_Undefined();
	retVal = AS3_Undefined();
	temp = AS3_Undefined();
 
	//parse the arguments.
	AS3_ArrayValue( args, "AS3ValType, AS3ValType, IntType", &dataVal, &contourVal, &ncontours );
 
	//if no argument is specified
	if(dataVal == NULL || contourVal == NULL || ncontours <= 0)
	{
		AS3_Trace( AS3_String("Invalid input data!") );
		return AS3_Null();
	}
 
	ccount = 0;
	i = 1;
 
	while (ccount < ncontours)
   	{
		int j;
		int k;
		//fscanf(infile, "%d", &npoints);
 
		npoints = AS3_IntValue( AS3_Get(contourVal, AS3_Int(ccount)) );
 
		first = i;
		last = first + npoints - 1;
		for (j = 0, k = 1; j < npoints; j++, i++, k += 2)
		{
			//fscanf(infile, "%lf%lf", &seg[i].v0.x, &seg[i].v0.y);
			temp = AS3_Get(dataVal, AS3_Int(i) );
 
			seg[i].v0.x = AS3_NumberValue( AS3_Get(temp, AS3_Int(0)) );
			seg[i].v0.y = AS3_NumberValue( AS3_Get(temp, AS3_Int(1)) );
 
			if (i == last)
			{
				seg[i].next = first;
				seg[i].prev = i-1;
				seg[i-1].v1 = seg[i].v0;
			}
			else if (i == first)
			{
				seg[i].next = i+1;
				seg[i].prev = last;
				seg[last].v1 = seg[i].v0;
			}
			else
			{
				seg[i].prev = i-1;
				seg[i].next = i+1;
				seg[i-1].v1 = seg[i].v0;
			}
 
			seg[i].is_inserted = FALSE;
		}
 
		ccount++;
	}
 
	n = i - 1;
	initialise(n);
	construct_trapezoids(n);
	nmonpoly = monotonate_trapezoids(n);
	ntriangles = triangulate_monotone_polygons(n, nmonpoly, op);
 
	retVal = AS3_Array("AS3ValType", NULL);
 
	for (i = 0; i < ntriangles; i++)
   	{
		AS3_Val data = AS3_Array("IntType, IntType, IntType", op[i][0], op[i][1], op[i][2] );
 
		AS3_Set(retVal, AS3_Int(i), data);
	}
 
	return retVal;
}
 
//entry point for code
int main()
{
	//define the methods exposed to ActionScript
	//typed as an ActionScript Function instance
	AS3_Val echoMethod = AS3_Function( NULL, echo );
	AS3_Val triMethod = AS3_Function( NULL, triangulate );
 
	// construct an object that holds references to the functions
	AS3_Val result = AS3_Object( "echo: AS3ValType", echoMethod );
	AS3_SetS(result, "triangulate", triMethod);
 
	// Release
	AS3_Release( echoMethod );
	AS3_Release( triMethod );
 
	// notify that we initialized -- THIS DOES NOT RETURN!
	AS3_LibInit( result );
 
	// should never get here!
	return 0;
}

14 Comments - Tags:

6 November Drag’n'drop from HTML to Flash

Last week we released a new Floorplanner account, the Enterprise account. With it, companies can have an online Floorplanner solution completely branded to their wishes. It includes the Roomplanner module and a custom library of their own furniture elements.  Steelcase and Maxon are the first to have an active Enterprise account.

The interesting part of this release, from a tech point of view, is the new drag-and-drop functionality. The Roomplanner is running in the Flash Player. However, the library of furniture elements is in HTML. We chose to build the furniture library in the HTML sidebar for a couple of reasons. It’s not in the Roomplanner, so there is more space left to design. It gives us more freedom in the page layout. All items on the page are indexed and things like sorting and tags we’re easier to build in Ruby on Rails than in Flash.

Now we only needed a way to get the furniture from the sidebar to the Roomplanner. That’s where the drag’n'drop comes in, drag from HTML and drop in Flash. To drag an image of a furniture element in HTML we used the fantastic JavaScript script.aculo.us framework. The next step was to swap the image to an element in the Roomplanner and to update the position of the element while moving the cursor on the Flash content. For this we used ExternalInterface to communicate between JavaScript and ActionScript. That’s all there is to it. The theory is actually quite simple, but in practice it was very (I repeat, VERY) difficult to get it working in all the main browsers. All those nitty gritty browser details…

At this moment everything seems to work just fine, so please take a look at Steelcase and/or Maxon and let me know what you think!

4 Comments - Tags: , , , , , ,