Sitemaps

By
Dave
Project
Published
10 Jul 2010 17:19
Last Modified
13 Jan 2013 18:20

As part of optimising the results from search engines such as Bing and Google, I added a dynamic site map. The structure of this file is defined by sitemaps.org. In order to generate a suitable file, I added a simple action as shown below in Listing 1.

public ContentResult Sitemap()
{
    XNamespace xmlns = "http://www.sitemaps.org/schemas/sitemap/0.9";
    XElement root = new XElement(xmlns + "urlset");

    // home
    root.Add(GetUrlElement(xmlns, ""));

    // posts, categories etc
    foreach (...)
    {
        string url = Url.RouteUrl("MyRoute", new { myparam = value });
        root.Add(GetUrlElement(xmlns, url);
    }

    return Content(root.ToString(), "text/xml", Encoding.UTF8);
}

private XElement GetUrlElement(XNamespace xmlns, string relativeUri)
{
    return new XElement(xmlns + "url",
        new XElement(xmlns + "loc", new Uri(Request.Url, relativeUri).AbsoluteUri));
}

Listing 1. Action to generate dynamic sitemap

While not shown above in Listing 1, I also added <lastmod /> elements, e.g. which corresponded to the last-modified date of a post. The final step was to add a suitable route to global.asax, as shown below in Listing 2.

routes.MapRoute(
    "Sitemap",
    "sitemap.xml",
    new { controller = "MyController", action = "Sitemap" }
);

Listing 2. Sitemap route

The sitemap is then available at http://{domain}/sitemap.xml and can be submitted to search engines using their webmaster tools. For Bing, the webmaster pages can be found at http://www.bing.com/webmaster. For Google the link is http://www.google.com/webmasters/tools.

Legacy & Not-Found Links

By
Dave
Project
Published
10 Jul 2010 17:06
Last Modified
3 Jan 2011 01:32

I needed to deal with existing links to my previous blog engine, as well as invalid requests to the new engine.

Legacy Links

As I mentioned previously, my new blog engine has opted for a different URL structure to my old engine. In order to support existing links to my old blog posts, I added some legacy routes to my routing table, as follows:

  1. Category links of the format http://{domain}/{directory}/category/{name}.aspx are mapped to my existing controller with the following route:
    routes.MapRoute(
        "MyRoute",
        "category/{name}.aspx",
        new { controller = "MyController", action = "MyAction" });
    
  2. Post links of the format http://{domain}/{directory}/post/{name}.aspx are mapped to my existing controller with the following route:
    routes.MapRoute(
        "MyRoute",
        "post/{name}.aspx",
        new { controller = "MyController", action = "MyAction" });
    
  3. Month links of the format http://{domain}/{directory}/{year}/{month}/default.aspx are mapped to my existing controller with the following route:
    routes.MapRoute(
        "MyRoute",
        "{year}/{month}/default.aspx",
        new { controller = "MyController", action = "MyAction" });
    
  4. The syndication link http://{domain}/{directory}/syndication.axd?format={format} is mapped to my existing controller with the following route:
    routes.MapRoute(
        "MyRoute",
        "syndication.axd",
        new { controller = "MyController", action = "MyAction" });
    

The nice thing about this approach is that I can support these legacy links without any additional code, just these additions to my routing table.

Not-Found Links

Requests to the new blog engine might not return anything for a couple of reasons:

  1. An invalid controller or action is specified
  2. A invalid parameter is passed to a controller action

For invalid controller requests, a catch-all route is a good starting point, such as the following:

routes.MapRoute(
    "Error",
    "{*url}",
    new { controller = "Error", action = "Http404" });

This ensures that requests of an unrecognised pattern will be passed to an error controller, and an appropriate view used to render an error.

For invalid parameters, additional code is required in the relevant controllers to redirect to a "Not Found" view, for example when a requested post is not found.

Checkbox Lists

By
Dave
Project
Published
28 Jun 2010 01:17
Last Modified
8 Dec 2012 16:42

Categories and tags are commonly used to classify blog posts. In order to specify the relevant categories and tags when creating and editing posts, two different approaches were taken, as shown below in Figure 1.

Checkbox list

Figure 1. Category checkbox list and comma-seperated tags

For the tags, a simple textbox can be used, with a controller action which parses a comma-seperated list. For the category list, a series of checkboxes can be rendered with the following view code:

<div>
    <% foreach (SelectListItem item in Model.CategoryList)
       {%>
    <input type="checkbox" class="checkbox" name="Categories"
        value="<%= item.Value%>"
        <%= item.Selected ? "checked=\"checked\"" : "" %> />
    <%= Html.Label(item.Text)%>
    <%} %>
</div>

In order to simplify the markup in the view, I created a simple HtmlHelper. The view code then becomes as follows:

<%= Html.CheckBoxList(Model.CategoryList, "Categories", new { Class = "checkbox" }, null)%>

The HtmlHelper is as follows:

public static string CheckBoxList(this HtmlHelper helper, List<SelectListItem> selectList,
    string name, object checkboxHtmlAttributes, object labelHtmlAttributes)
{
    StringBuilder stringBuilder = new StringBuilder();

    foreach (SelectListItem item in selectList)
    {
        TagBuilder tagBuilder = new TagBuilder("input");
        tagBuilder.MergeAttribute("type", "checkbox");
        tagBuilder.MergeAttribute("name", name);
        tagBuilder.MergeAttribute("value", item.Value);
        tagBuilder.MergeAttributes(new RouteValueDictionary(checkboxHtmlAttributes));

        if (item.Selected)
            tagBuilder.MergeAttribute("checked", "checked");

        // add checkbox
        stringBuilder.AppendLine(tagBuilder.ToString(TagRenderMode.SelfClosing));

        tagBuilder = new TagBuilder("label");
        tagBuilder.SetInnerText(item.Text);
        tagBuilder.MergeAttributes(new RouteValueDictionary(labelHtmlAttributes));

        // add label
        stringBuilder.AppendLine(tagBuilder.ToString());
    }

    return stringBuilder.ToString();
}

The model must include both the SelectList to pass the list of categories to the view, and an array of strings to post the selected values back to the controller, as follows:

public class MyModel
{
    ...
    public List<SelectListItem> CategoryList { get; set; }
    public string[] Categories { get; set; }
}

Regardless of which approach is used, the controller code looks like the following:

[HttpPost]
public ActionResult MyAction(MyModel model)
{
    if (model.Categories != null)
    {
        foreach (string item in model.Categories)
        {
            ...
        }
    }
    ...
}

Note that in both cases, only the checkboxes that are checked will have their values submitted to the controller, which is fine for this approach, as I (re)consruct the list of selected categories on each action.

Valid Comments

By
Dave
Project
Published
26 Jun 2010 23:02
Last Modified
8 Dec 2012 16:44

One of the trickier things I had to do for my blog engine was to support comment submission. Comments are only shown for a post when viewing the "Detail" view of a single blog post, and I wanted to show a form to submit a new comment at the bottom of this page.

In order to do this I had to use a specific ViewModel class to pass both the blog post, and a skeleton comment to the view. In this way, when the form is posted to the server, the default ModelBinder will present the controller with the comment. However, since the blog post is not on the HTML form (it only has the comment), I have to maintain a reference to the blog post using an HTML hidden form field:

<%= Html.HiddenFor(model => model.Comment.Post.Id) %>

The normal controller pattern to use when validating and saving data is as follows:

public ActionResult MyAction(...)
{
    ...
    Post post = myServiceLayer.GetPost(...);
    return View(post);
}

[HttpPost]
public ActionResult MyAction(...)
{
    ...
    if (myServiceLayer.AddComment(comment));
    {
        return RedirectToAction("MyAction");
    }
    else
    {
        // rebuild ViewModel and return view
        return View("MyView", new MyViewModel { Post = myServiceLayer.GetPost(...), Comment = comment });
    }
}

This pattern is used such that a successful post returns a client-side redirect to a page which re-displays the view. If the user refreshes their browser page, since we've done a re-direct there will be no prompts to re-submit the HTML form. This is called Post-Redirect-Get (PRG).

What happens if the comment fails validation? In this case, I need to re-display the form with the relevant errors and submitted values. I also need to ensure that the view is scrolled to the correct location so that the Comment form is displayed. Firstly I rebuild the ViewModel using the the submitted comment and blog post id from the hidden HTML field. Next, in order to return the ViewModel and ModelState, which contains the validation errors, I need to return a view. The only way I can find to specify a HTML anchor tag for this view is to include a HTML Action property on the HTML form in the view itself:

<div id="MyAnchor">
    MyTitle</div>
<%= Html.ValidationSummary(true, "...") %>
<% using(Html.BeginForm("MyAction", "MyController", FormMethod.Post,
    new { Action = string.Format("{0}#MyAnchor", Request.Url.AbsoluteUri) })) %>

A screenshot is shown below in Figure 1.

Blog comment validation

Figure 1. Blog comment validation.

I could have used AJAX to do this and avoid the problem of having to scroll the form, however I would still have to provide downlevel support for browsers with Javascript disabled.

A New Blog

By
Dave
Project
Published
21 Jun 2010 23:04
Last Modified
13 Jan 2013 18:21

I finally decided to switch to using my own blog engine. There were several reasons for this:

  1. I was getting too much spam with my existing engine
  2. I wanted complete control over rendering
  3. But really...I needed an excuse to learn the ASP.NET MVC2 Framework! :)

I didn't need anything particularly fancy, and the following requirements would initially suffice:

  1. Single author, single blog
  2. View posts, with filtering by category, tag, year, month, day, and name
  3. Paging on post views
  4. Submit comments
  5. View comments with post detail
  6. Author sign-in to create & edit posts, edit & delete comments
  7. Syndication
  8. Recent posts, archive list, tag cloud, blogroll

Going forward, the following were good candidate next-steps:

  1. Live-writer support
  2. SEO
  3. Further feed-format support for ATOM
  4. Mobile support

Storage

I decided to stick with XML files for the time-being, mainly since my previous engine used them and it would save having to migrate the content to a database. Going forward, a database would clearly be a more scalable option, but it will be a while before I generate enough content for this to be an issue.

Syndication

I used the WCF SyndicationFeed and SyndicationItem classes to build an RSS 2.0 feed, and returned the feed from my controller as a derived ActionResult, using an approach outlined in this post on Joe Wardell's blog, and shown below in Listing 1.

public class RssResult : ActionResult
{
    public SyndicationFeed Feed { get; set; }

    public RssResult() { }

    public RssResult(SyndicationFeed feed)
    {
        this.Feed = feed;
    }

    public override void ExecuteResult(ControllerContext context)
    {
        context.HttpContext.Response.ContentType = "application/rss+xml";
        Rss20FeedFormatter formatter = new Rss20FeedFormatter(this.Feed);
        using (XmlWriter writer = XmlWriter.Create(context.HttpContext.Response.Output))
        {
            formatter.WriteTo(writer);
        }
    }
}

Listing 1. Returning an RSS feed as a derived ActionResult

URL Structure

Backwards-compatibility with any links to the existing blog were something I needed to consider. An appropriate route specification should ensure compatibility, but at the expense of having to use .aspx extensions.

Looking at popular blog engines such as WordPress and MSDN, I decided to use the following scheme:

  1. http://{domain}/blog/ for all posts
  2. http://{domain}/blog/arhive/{year} for all posts in a given year>
  3. http://{domain}/blog/arhive/{year}/{month} for all posts in a given month
  4. http://{domain}/blog/arhive/{year}/{month}/{day} for all posts in a given day
  5. http://{domain}/blog/arhive/{year}/{month}/{day}/{name} for a specific post
  6. http://{domain}/blog/category/{name} for posts in a given category
  7. http://{domain}/blog/tag/{name} for posts with a given tag
  8. http://{domain}/blog/feed for an RSS 2.0 feed

Posts are paged and ordered chronologically with newest posts first.

I'm hoping to switch in a couple of days, at which point any feed subscriptions will need updating. Apologies for any inconvenience.

Planetary Rings

By
Dave
Project
Published
31 May 2010 11:59
Last Modified
13 Jan 2013 17:46

The planetary bodies wouldn't be complete without their rings. Whilst Saturn has the most spectacular system, Jupiter, Uranus and Neptune also possess systems of their own. I chose to start with Saturn, and Figure 1 shows an initial screenshot.

Saturn Ring System

Figure 1. Initial rendering of saturn ring system.

I had to consider the following points, and will discuss each in turn:

  1. Mesh
  2. Texture & Lighting
  3. Size & Orientation
  4. Shadows

Mesh

I implemented a simple algorithmic disc mesh. For the planetary bodies I can use a single mesh, scaled appropriately. However for each instance of a ring system I generate a seperate mesh since both inner and outer radii can change. In the future I may look at using a single mesh and using vertex displacement in the vertex shader to size the ring appropriately.

Texture & Lighting

I chose to start by lighting the ring equally from each side, though in reality the rings look different from above and below. Using a diffuse, per-pixel shader gives a term such as the following, where the absolute value gives the same lighting from above and below.

float diffuse = saturate(abs(dot(direction, normal)));

In order to be able to see through the gaps in the ring system, I blended the rings using the following render states:

SourceBlend = Blend.SourceAlpha;
DestinationBlend = Blend.InverseSourceAlpha;

There are numerous textures available for Saturn's rings. I took a thin slice for a texture map, and added an alpha channel corresponding to the ring transmittence.

Size & Orientation

I added a further xml file for the physical data on a ring system, and used it to size the ring mesh and apply the appropriate texture.

In order to actually see any light on the ring system, I also had to add the correct obliquity (axial tilt) to the planet, available from the NASA JPL Horizons system. This parameter also needed to be applied to the positions and orbits of the planet's moons.

Shadows

The final piece for my first iteration on the ring system was to add shadows. Shadows are cast from the planet onto the rings, and also from the rings onto the planet. For the former, when rendering the ring I implemented a ray-sphere intersection algorithm to add shadow whenever a ray fired toward the sun intersected the planet. For the latter, when rendering the planet, I implemented a simple ray-plane algorithm to add shadow whenever a ray fired from the planet intersected the ring.

A Matter of Controls

By
Dave
Project
Published
19 May 2010 23:46
Last Modified
13 Jan 2013 17:45

Up to this point, I've had numerous settings which I can control using a keyboard-driven menu. One of the key things I've been looking at recently is to "touch-enable" these settings, so that a keyboard is not necessary. This will enable interaction via touchscreen, Microsoft Surface etc.

There are several different types of settings, including:

  1. Boolean values
  2. Floating-point values
  3. Integer values

The control had to support the following:

  1. Different data types
  2. Multidirectionality
  3. Multitouch manipulations, at least for translation and rotation
  4. Multiple simultaneous instances of the control
  5. Keyboard support with "tab stops"

Boolean Values

For boolean values, I've used a "radial" control which renders an arbitrary number of segments which can be selected/deselected. An early version is shown below in Figure 1.

Boolean settings

Figure 1. Radial control for boolean settings

Floating-Point Values

When floating-point values are selected, I add a radial scale and a dial which can be rotated to set the appropriate value. An early version is shown below in Figure 2.

Float settings

Figure 2. Radial control for float settings

Navigation

In order to navigate a "tree-view" of settings, the current "category" is displayed, along with a "Back" button to navigate back to the parent category. Child categories are shown as segments on the menu, along with settings for the current category. The currently-selected setting is indicated by a "focus" arc, and "pressed" buttons can be highlighted using different colors and opacities.

Precision Matters

By
Dave
Project
Published
28 Feb 2010 13:26
Last Modified
13 Jan 2013 17:46

The Vector3 struct uses 32-bit floating-point values. On a solar-system scale, this does not have sufficient precision to accurately position objects such as the far outer planets.

A single-precision (32-bit) floating-point value stores 7 decimal digits of precision. Neptune, for example, has an approximate distance from the sun of 4.5x109km. Expressed with 7 significant figures, this has an error of approximately 500km (one half of the value of the last significant place). For Pluto (although no longer classified by the IAU as a planet), at a distance of approximately 5.9x109km, 500km is approximately one quarter of its diameter! Clearly single-precision floating point numbers are not sufficient to store these positions to the required level of accuracy.

One option is to use double-precision (64-bit) values, which store 15 decimal digits of precision. Most modern GPUs remain incapable of performing double-precision arithmetic, however this level of precision is only required to calculate the position of the object relative to the camera. This can be done on the CPU, after which point single-precision calculations are just fine, as shown below in Figures 1-3.

Precision

Figure 1. Uranus and moon orbits

Precision

Figure 2. Neptune and moon orbits

Precision

Figure 3. Pluto and moon orbits

An example struct for dealing with double-precision vectors is as follows:

public struct Vector3Double
{
    double _x, _y, _z;

    public double X { get { return _x; } set { _x = value; } }
    public double Y { get { return _y; } set { _y = value; } }
    public double Z { get { return _z; } set { _z = value; } }

    public Vector3Double(double x, double y, double z)
    {
        _x = x;
        _y = y;
        _z = z;
    }
}

Using operator overloading it is possible to work with Vector3Double the same way as Vector3, for example:

public static Vector3Double operator -(Vector3Double vector1, Vector3Double vector2)
{
    return new Vector3Double(
        vector1._x - vector2._x,
        vector1._y - vector2._y,
        vector1._z - vector2._z);
}

Conversions, for example to and from Vector3 can also be easily supported, for example:

public static implicit operator Vector3(Vector3Double vector)
{
    return new Vector3(
        (float)vector._x,
        (float)vector._y,
        (float)vector._z);
}

Note that while double-precision solves the problem to the levels of accuracy described here, a better long-term option will be to use fixed-precision types such as 64-bit integers.

Constellation Boundaries

By
Dave
Project
Published
27 Feb 2010 22:38
Last Modified
13 Jan 2013 17:48

The International Astronomical Union (IAU) defines an "official" set of boundaries for the 88 constellations. I've now added these.

What I initially saw as a bug turned out to result from the fact that Serpens is actually divided into two boundaries (all other constellations have a single boundary).

Constellation Boundaries

Constellation Boundaries

Constellation Boundaries

Figures 1-3. Constellation Boundaries

Deep Sky Objects

By
Dave
Project
Published
19 Feb 2010 23:10
Last Modified
13 Jan 2013 17:55

I've started looking into adding some deep sky objects such as star clusters and galaxies, and have been thinking about the following:

  1. Loading the data.
  2. Sprite markers for the position, size and type of deep sky object.
  3. Rendering a text label at the appropriate position and scale.

Loading Data

Once again there is plenty of data available in the public domain. I found some basic lists of objects to get me started, converted the data to XML and used the automatic content serialization in XNA Game Studio 3.1 to compress the XML to an XNB file, as per my Background Stars.

Sprite Markers

Adding sprites in the appropriate positions was relatively straightforward. In order to specify the size of the object, I created a custom VertexElement structure, VertexPositionColorSize, which let me set a VertexBuffer with all the data the shader needed to position, color and scale the sprite.

Rendering Text

The labels were more interesting in that there were several options. I had the following requirements:

  1. I wanted the labels to render efficiently, as there may be many instances visible at once.
  2. I was considering scaling the labels as a size/proximity dimension.
  3. I didn't need to use the depth buffer since these labels would always be at the "back".

With these requirements in mind, the following approaches were apparent:

  1. Use SpriteBatch.DrawString() with SpriteFonts.
  2. Using line primitives to render vector-based fonts.

The SpriteBatch approach is shown below in Figure 1. While not ideal for scaling labels to large sizes (the fonts are drawn as scaled bitmaps), the approach proved surprisingly performant.

Deep Sky Objects

Figure 1. Deep Sky Objects from Messier Catalog with SpriteBatch text

It is possible to extract vector definitions from the "Modern", "Roman", and "Script" Windows Fonts. I took the approach of pre-generating the line primitives for a given label at load time and passing them to the shader with a given scale and offset per frame. This proved slightly more performant than the SpriteBatch approach, and provided a good option for scaling the text to large sizes without occluding any objects in the background. Examples are shown in Figures 2 - 4.

Deep Sky Objects

Figure 2. Globular and Open Clusters with scaled PointSprites and Vector Fonts

Deep Sky Objects

Figure 3. Globular Clusters with Right Ascension, Declination Grid

Deep Sky Objects

Figure 4. Globular and Open Clusters with Background Stars and Earth

Note that the Deep Sky Objects are shown at an exaggerated scale for clarity, and without anti-aliasing.

Page