Creating a 3D tagcloud in Silverlight (part 2)

5 comments

In part 1 I showed you how to create the basics for a 3D tagcloud in Silverlight. In this part I’ll show how to get the tags from your html for inserting it into a blog template, we’ll change the color of the tag based on the weight of the tag and let the hyperlink button actually function as a hyperlink (until now it does nothing when you click on it).

There are a few different ways you can pass or get information from the page the Silverlight control is hosted in:

  • Set the initParams parameter in the Silverlight object definition
  • Read the HtmlDocument in your Silverlight code
  • Use JavaScript to call methods in your Silverlight code

You can only use the first option if your hosting your Silverlight application on the same domain. When using the hosting service on silverlight.live.com, no interaction between your application and HTML page is allowed.

For the second option you can use the HtmlPage object in your code to traverse through the HTML DOM, basic methods such as GetElementById are available.

The last option is the one I’ll be using in this post. To accomplish this you need to follow a few steps:

  • Decorate your page class with a “ScriptableType” attribute
  • Decorate the method you want to call with the “ScriptableMember” attribute and make sure it’s public
  • Register your object in the HtmlPage
  • Give your Silverlight object definition an id, so you can easily reference it from your JavaScript

The reason I’m going with this technique is the flexibility it provides when you want to reuse the tagcloud in other applications. Every blog engine has a different way to generate the HTML for a tagcloud, my blog just shows a list of tags.

To change the color based on the weight and set the url of the HyperlinkNutton we need to make some changes to the Tag3D object I showed you in the last post:

public class Tag3D
{
public HyperlinkButton btnLink { get; set; }
private TextBlock textBlock { get; set; }
public Point3D centerPoint { get; set; }
private Color TagColor { get; set; }

public Tag3D(double x, double y, double z, string text, string url, Color tagColor, int weight)
{
centerPoint = new Point3D(x, y, z);
textBlock = new TextBlock();
textBlock.Text = string.Format("{0} ({1})", text, weight);
btnLink = new HyperlinkButton();
btnLink.Content = textBlock;
btnLink.NavigateUri = new Uri(url);
this.TagColor = tagColor;
}

public void Redraw(double xOffset, double yOffset)
{
double zFactor = ((centerPoint.Z + 300) / 450.0);
btnLink.FontSize = 30.0 * zFactor;
double alpha = zFactor * 255;
//Debug.WriteLine("Z: {0}; zFactor: {1}; alpha: {2}", centerPoint.Z, zFactor, alpha);
btnLink.Foreground = new SolidColorBrush(Color.FromArgb(Convert.ToByte(alpha), TagColor.R, TagColor.G, TagColor.B));

Canvas.SetLeft(btnLink, centerPoint.X + xOffset - (btnLink.ActualWidth / 2));
Canvas.SetTop(btnLink, -centerPoint.Y + yOffset - (btnLink.ActualHeight/ 2));
Canvas.SetZIndex(btnLink, Convert.ToInt32(centerPoint.Z));
}
}

In the constructor we now pass in the url and a Color to show when the tag is the most important (I chose to use a scale of 1 to 10, with 10 being the most important).
The url is simply assigned to the NavigateUrl property of the HyperlinkButton. The Color is used when setting the new Foreground Brush. I also made some modifications in the calculations of the font size and alpha of the Brush to make it look a bit more realistic.
To let the JavaScript in the page add the tags I’ve created a AddTag method and decorated it with the ScriptableMember attribute:

[ScriptableMember()]
public void AddTag(string tag, string url, int weight)
{
if (weight > 10)
weight = 10;

Color color = new Color();

color.R = Convert.ToByte(Math.Round(209.0 * ( weight / 10.0)));

color.G = Convert.ToByte(Math.Round(18.0 * (weight / 10.0)));

color.B = Convert.ToByte(Math.Round(65.0 * (weight / 10.0)));

tagBlocks.Add(new Tag3D(0.0, 0.0, 0.0, tag, url, color));
}

In this method we calculate the Color of the tag based on the weight. Then we add a new tag to the tagBlocks List<Tag3D>.

After calling this method a couple of times we need to place the tags and display them. I’ve changed the FillTags method shown in the previous post and renamed it to ProcessTags to make the name a bit more meaningful:

[ScriptableMember()]
public void ProcessTags()
{

double radius = RootCanvas.Width / 3;

int max = tagBlocks.Count;

double phi = 0;

double theta = 0;

for (int i = 1; i < max + 1; i++)

{

phi = Math.Acos(-1.0 + (2.0 * i – 1.0) / max);

theta = Math.Sqrt(max * Math.PI) * phi;

double x = radius * Math.Cos(theta) * Math.Sin(phi);

double y = radius * Math.Sin(theta) * Math.Sin(phi);

double z = radius * Math.Cos(phi);
Tag3D tag = tagBlocks[i -1];

tag.centerPoint = new Point3D(x, y, z);

tag.Redraw(RootCanvas.Width / 2, RootCanvas.Height / 2);

RootCanvas.Children.Add(tag.btnLink);
}
}

We need one more thing to make the methods callable from JavaScript. Register the
object with the HtmlPage in the constructor:

HtmlPage.RegisterScriptableObject("TagCloud", this);

No you can call the methods from JavaScript:

function addTags() {
var control = document.getElementById("Xaml1");
control.content.TagCloud.AddTag("Silverlight", "http://silverlight.net", 5);
control.content.TagCloud.AddTag("Tagcloud", "http://blogs.tamtam.nl", 2);
control.content.TagCloud.AddTag("Tam Tam", "http://www.tamtam.nl", 10);
control.content.TagCloud.AddTag("Axelerate3D", "http://www.codeplex.com", 8);
control.content.TagCloud.AddTag("WPF", "http://www.microsoft.com", 1);
control.content.TagCloud.AddTag("SharePoint", "http://www.microsoft.com", 4);
control.content.TagCloud.ProcessTags(); }

I’m just attaching some code to the onclick of a button and hard-coding the tags. Normally you would handle the onload of the document (or better yet the $(document).ready in jQuery) and get your tags from the Html to pass them to the Silverlight object.

And that wraps it up for this tutorial.

Related posts:

  1. Creating a 3D tagcloud in Silverlight (part 1)
  2. Hosting your Silverlight application and media in the cloud
  3. Silverlight: Multiple animations on one property through Transforms
  4. SP2010 Installation – Error creating configuration database
Tagged , , . Bookmark the permalink. Post a comment or leave a trackback: Trackback URL.

4 Comments

  1. Andras Velvart
    Posted January 20, 2010 at 7:56 pm | Permalink

    Hi Peter,

    I would just like to let you know that we have used your tag cloud idea with
    our Summitr application created for the
    MVP Summit. Thanks for your great job, and
    please let me know if you can use a complimentary Zoomery Gallery
    license because I would be more than happy to get one for you!

    Cheers,
    Andras

  2. Peter
    Posted January 20, 2010 at 7:57 pm | Permalink

    @Andras

    I’m glad to see you were able to reproduce the results based on my tutorial.

  3. Einar Ingebrigtsen
    Posted January 20, 2010 at 7:57 pm | Permalink

    Hi,
    cool stuff!

    I just wanted to let you know that there is a 3rd alternative for 3D support in Silverlight. I’ve been working on an engine called Balder for quite some time (actually started back in 2007 for Silverlight 1.1). And it is highly optimized, and provides all the necessary math you’d need to do this.

    We’ve shifted the focus a bit for the engine from being focused on providing 3D capabilities in normal Silverlight applications to be more game centric, but one could with great ease implement a TagCloud like yours in it as well.

    The project is up on codeplex : http://balder.codeplex.com

    Lately, we’ve started working on a "software" rendering engine, to provide the necessary speed for doing advanced 3D in Silverlight.

    http://www.ingebrigtsen.info/post/2009/05/05/Software-rendering-in-Balder.aspx

  4. Himanshu Rastogi
    Posted May 20, 2010 at 1:59 pm | Permalink

    Hi Peter,
    It would be really great if you can share the working copy of the code.

    I am still new to silverlight and seeing something as complex as this work would be ever more enriching experience.

    Himanshu Rastogi

One Trackback

  1. By Creating a 3D tagcloud in Silverlight (part 1) on February 2, 2010 at 12:29 pm

    [...] the next part I’ll show you a way to dynamically set the tags, base their fontsize on the actual weight of the [...]

Post a Comment

Your email is never published nor shared. Required fields are marked *

*
*

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>