Thursday, July 30, 2009

Isometric Tile Positioning on Canvas

In order to avoid moving tiles on the Content Canvas completely, the Content Canvas is created big enough to accommodate the entire map. For those who think too much about performance like me, a Silverlight canvas can be created in different sizes without costing more (or less) memory, even when CacheMode is set to BitmapCache.

A square map (that has the same number of columns as the number of rows) will have its origin tile M(0,0) positioned in the exact middle-top of the canvas. But a rectangular map (that has number of columns not equal to the number of rows) will have its origin tile M(0,0) located some distance off the canvas center point (click to enlarge):



Since square maps are special cases of rectangular maps, we will attempt to solve the equations for rectangular maps as a generic solution. Let us use the following symbols to denote various components for calculation:


  • C = Number of columns on map

  • R = Number of rows on map

  • O = Offset from canvas center point

  • Wc = Canvas width in pixels

  • Hc = Canvas height in pixels

  • Wt = Tile width in pixels

  • Ht = Tile height in pixels



Based on observation, one additional row will increase the overall map dimension by Wt*0.5 pixels. This increment will offset origin tile M(0,0) by Wt*0.25 pixels to the right. Hence, offset from canvas center point O can be derived from the following equation:

O = (R-C) * Wt * 0.25


Again, based on the same observation, the canvas dimension can be obtained from the following equations:

Wc = (R+C) * Wt * 0.5
Hc = (R+C) * Ht * 0.5


Each tile on the map is represented by an Image object, whose first pixel lies on the top-left corner of the tile. A tile is positioned in the canvas at a location denoted by P(x,y), with respect to the top-left corner of the canvas. We can determine the values of P(x,y) for a given map tile M(x,y) with the following equations:

P(x) = O + [[M(x)-M(y)-1] * Wt * 0.5
P(y) = [[M(x)+M(y)] * Ht * 0.5

4 comments:

mogadanez said...

Thank for interesting post,
But about performance.
i try create Entire canvas for map with size 500x500 tiles. and my browser is hangs.

did you have a working snippet that works with big canvas?

Captain said...

Hi Mogadanez, I'm sorry for such a long delay in responding. Blogspot does not notify me of a comment on my blog (I'll see how I can change that). If in the future you don't get a respond on time, please feel free to drop me a line at haywireguy [at] hotmail [dot] com.

Back to your question, I think 250,000 tiles are too much for a browser, perhaps you can remove those beyond the screen boundaries, and reuse them when they scroll back into view. Please let me know if you need more explanation.

Regards,
Ben.

mogadanez said...

For me, for big map i choose deep zoom.
this way need to additional steps:

1. map editor that can export map to deep_zoom format.

2. need write wrapper-canvas that represents current visible fragment that contains map objects. this canvas must be synchronized with deepzoom drag/zoom actions

i was done first, second is in process.
here is sample:
http://silverlight.services.live.com/invoke/108327/DeepZoomMap/iframe.html

Captain said...

Ah, if you're using Deep Zoom then there is no other way than loading all the tiles on-screen.

One alternative though, perhaps you can consider this: having integral zoom levels such as 1x, 2x ... Nx. If user zooms out (to show more tiles on screen), you can compose new set of tiles from a much larger map in a way that your screen still contains the same number of physical tiles. What I mean is...

Consider the case with a gigantic map that consists of 2048x1600 tiles. Say when the user stays at 1x zoom level, she can see 320x200 tiles fill up the entire screen. Then she does a zoom out to 2x, in which case you should show 640x400 tiles on screen. When this happen, you can compose (with WriteableBitmap) a new set of tiles, by taking four neighboring tiles and render them on a single tile. So what happens in this case? You still get 320x200 tiles on screen at 2x. And this applies to all the zoom levels, with tiles clearly drawn on screen. Composing four tiles into one can be done very quickly in GPU, so I wouldn’t be worried about the performance (except on Mac OS, where GPU only comes in full screen).

IMHO, Deep Zoom is for different kind of application, zooming images where you do not care about blurring, for map editor like yours, it does not work that well.

Hope that helps.