Monday, February 24, 2014

Quickly clustering a large number of items

It just happens that I have two different projects that have the need of cluster analysis, applied in two different ways: one has uses on maps, where a large number of items needs to be displayed quickly, while another implies finding clusters of news items, where the distance between them is determined by their content. The most used clustering algorithm and the first to be found by searching the web is the k-means clustering. Its purpose is to categorize a list of items into a number of k clusters, hence the name. Setting aside the use value of the algorithm for my purposes, the biggest problem I see is the complexity: in its general form it is at least O(n2), and most of the time a lot higher. The net abounds with scientific papers investigating the k-means complexity and suggesting improvements, but they are highly mathematical and I didn't have the time to investigate further. So I just built my own algorithm. It is clearly fuzzy, imperfect, may even be wrong in some situations, but at least it is fast. I will certainly investigate this area more, maybe even try to understand the math behind it and analyse my results based on this research. When I do that I will update this post or write others. But until then, let me present my work so far.

The first problem I had was, as I said, complexity. For one million points on the map, any algorithm that takes into account the distance between any two items will have to make at least one trillion comparisons. So my solution was to limit the number of items by grouping them in a grid:
Step 1: find the min and max on each dimension (that means going through the entire item collection once or knowing beforetime the map bounds)
Step 2: determine a number of cells that would be a bit more than what I need in the end. (that's a decision I have to take, no algorithmic complexity)
Example: for my map example I have only two dimensions: X and Y. I want to display an upper bound of 1000 clusters. Therefore I find the minimum and maximum X and Y and then split each dimension into 100 slots. That means I would cluster the items I have into 10000 cells.
Step 3: for each item, find its cell based on X,Y and add the item to the cell. This is done by simple division: (X-minX)/(maxX-minX). (again that means going once through the collection)
Step 4: find the smallest cell (the complexity is reduced now to working with cells)
Step 5: find its smallest neighbour (the complexity of this on the implementation)
Step 6: merge the two cells
Until the number of cells is larger than the desired number of clusters, repeat from Step 4.
In the end, the algorithm is O(n+p*log(p)), I guess, where p is the number of cells chosen at step 2.

Optimizations are the next issue.
  • How does one find the neighbours of a cell? On Step 3 we also create a list of neighbors for each new cluster by looking for a cluster that is at coordinates immediately above, below, left or right. When we merge two clusters, we get a cluster that is a neighbour to all the neighbours of the merged clusters.
  • How does one quickly get the cluster at a certain position? We create a dictionary that has the coordinates as the key. What about when we merge two clusters? Then the new cluster will be accessible by any of the original cluster keys (that implied that each cluster has a list of keys, as well)
  • How does one find the smallest cell in the cluster list? After Step 3 we sort the cluster list by the number of items they contain and each time we perform a merge we find the first item larger than the merged result and we insert it in the list at that location, so that the list always remains sorted.
  • How do we easily find the first item larger than an item? By employing a divide-et-impera method of splitting the list in two at each step and choosing to look into one bucket based on the item count of the cluster at the middle position

Before you use the code note that there are specific scenarios where this type of clustering would look a bit off, like items in a long line or an empty polygon (the cluster will appear to be in its center). But I needed speed and I got it.

Enjoy!

Update: The performance of removing or adding items from a List is very poor, so I created a LinkedList version that seems to be even faster. Here it is. The old List version is at the end

/// <summary>
/// Generic x,y positioned item class
/// </summary>
public class Item
{
    public double X { get; set; }
    public double Y { get; set; }
}

public class ClusteringDataProcessor
{
    /// <summary>
    /// the squared root of the initial cell number (100*100 = 10000)
    /// </summary>
    private const int initialClustersSquareSide = 100;
    /// <summary>
    /// the desired number of resulting clusters
    /// </summary>
    private const int numberOfFinalClusters = 1000;
    private static Random _rnd = new Random();

    /// <summary>
    /// In this implementation, the Cluster inherits from Item, so the result is a list of Item
    /// In the case of one Item Clusters, we actually return the original Item
    /// </summary>
    /// <param name="list"></param>
    /// <returns></returns>
    public List<Item> Process(List<Item> list)
    {
        if (list.Count <= numberOfFinalClusters) return list;
        // find bounds. If already known, this can be provided as parameters
        double minY = double.MaxValue;
        double minX = double.MaxValue;
        double maxY = double.MinValue;
        double maxX = double.MinValue;
        foreach (var item in list)
        {
            var y = item.Y;
            var x = item.X;
            minY = Math.Min(minY, y);
            maxY = Math.Max(maxY, y);
            minX = Math.Min(minX, x);
            maxX = Math.Max(maxX, x);
        }
        // the original list of clusters
        var clusterArr = new List<Cluster>();

        // the dictionary used to index clusters on their position
        var clusterDict = new Dictionary<string, Cluster>();

        // the unit for finding the cell position for the initial clusters
        var qX = (maxX - minX) / initialClustersSquareSide;
        var qY = (maxY - minY) / initialClustersSquareSide;

        foreach (var item in list)
        {
            // compute cell coordinates (integer and the values used as keys in the dictionary)
            var cx = Math.Min((int)((item.X - minX) / qX), initialClustersSquareSide - 1);
            var cy = Math.Min((int)((item.Y - minY) / qY), initialClustersSquareSide - 1);
            var key = getKey(cx, cy);
            Cluster cluster;
            // if the cluster for this position does not exist, create it
            if (!clusterDict.TryGetValue(key, out cluster))
            {
                cluster = new Cluster
                {
                    Keys = new List<string> { key },
                    X = minX + cx * qX + qX / 2,
                    Y = minY + cy * qY + qY / 2,
                    //Items = new List<Item>(),
                    Count = 0,
                    Neighbors = new List<string>()
                };
                // the neighbours of this cluster are the existing clusters that are below, above, left or right. If they exist, this cluster also is added to their neighbour list
                var nkeys = new[] { getKey(cx - 1, cy), getKey(cx + 1, cy), getKey(cx, cy - 1), getKey(cx, cy - 1) };
                for (var j = 0; j < 4; j++)
                {
                    Cluster nc;
                    if (clusterDict.TryGetValue(nkeys[j], out nc))
                    {
                        cluster.Neighbors.Add(nkeys[j]);
                        nc.Neighbors.Add(key);
                    }
                }
                clusterDict[key] = cluster;
                clusterArr.Add(cluster);
            }
            // add the item to the cluster (note that the commented lines hold the items in a list, while the current implementation only remember the number of items)
            //cluster.Items.Add(item);
            cluster.Item = item;
            cluster.Count++;
            // add the item position to the sums, so that we can compute the final position of the cluster at the Finalize stage without enumerating the items (or having to hold them in an Items list)
            cluster.SumX += item.X;
            cluster.SumY += item.Y;
        }
        // if the number of items is already smaller than the desired number of clusters, just return the clusters
        if (clusterArr.Count <= numberOfFinalClusters)
        {
            return clusterArr.Select(c => c.Finalize()).ToList();
        }

        // sort the cluster list so we can efficiently find the smallest cluster
        //clusterArr.Sort(new Comparison<Cluster>((c1, c2) => c1.Items.Count.CompareTo(c2.Items.Count)));
        LinkedList<Cluster> clusterLinkedList = new LinkedList<Cluster>(clusterArr.OrderBy(c => c.Count));

        // remember last merged cluster, as next merged clusters might have similar sizes
        var lastCount = int.MaxValue;
        LinkedListNode<Cluster> lastLinkedNode = null;

        // do this until we get to the desired number of clusters
        while (clusterLinkedList.Count > numberOfFinalClusters)
        {
            // we need to get the smallest (so first) cluster that has any neighbours
            var cluster1 = clusterLinkedList.First(c => c.Neighbors.Any());
            Cluster cluster2 = null;
            // then find the smallest neighbour
            var min = int.MaxValue;
            foreach (var nkey in cluster1.Neighbors)
            {
                var n = clusterDict[nkey];
                //var l = n.Items.Count;
                var l = n.Count;
                if (l < min)
                {
                    min = l;
                    cluster2 = n;
                }
            }
            // join the clusters
            var keys = cluster1.Keys.Union(cluster2.Keys).ToList();
            var cluster = new Cluster
            {
                Keys = keys,
                // approximate cluster position, not needed
                //X = (cluster1.X + cluster2.X) / 2,
                //Y = (cluster1.Y + cluster2.Y) / 2,

                // the result holds the count of both clusters
                //Items = cluster1.Items.Union(cluster2.Items).ToList(),
                Count = cluster1.Count + cluster2.Count,
                // the neighbors are in the union of their neighbours that does not contain themselves
                Neighbors = cluster1.Neighbors.Union(cluster2.Neighbors)
                    .Distinct()
                    .Except(keys)
                    .ToList(),
                // compute the sums for the final position
                SumX = cluster1.SumX + cluster2.SumX,
                SumY = cluster1.SumY + cluster2.SumY
            };
            foreach (var key in keys)
            {
                clusterDict[key] = cluster;
            }
            // efficiently remove clusters since LinkedList removals are fast
            clusterLinkedList.Remove(cluster1);
            clusterLinkedList.Remove(cluster2);

            // a little bit of magic to make the finding of the insertion point faster (LinkedLists go through the entire list to find an item)
            // if the last merged cluster is smaller or equal to the new merged cluster, then start searching from it.
            // this halves the insert time operation, but I am sure there are even better implementations, just didn't think it's so important
            LinkedListNode<Cluster> start;
            if (lastCount <= cluster.Count && lastLinkedNode.Value != cluster1 && lastLinkedNode.Value != cluster2)
            {
                start = lastLinkedNode;
            }
            else
            {
                start = clusterLinkedList.First;
            }
            var insertionPoint = nextOrDefault(clusterLinkedList, start, c => c.Count >= cluster.Count);
            // remember last merged cluster
            LinkedListNode<Cluster> link;
            if (insertionPoint == null)
            {
                link = clusterLinkedList.AddLast(cluster);
            }
            else
            {
                link = clusterLinkedList.AddBefore(insertionPoint, cluster);
            }
            lastLinkedNode = link;
            lastCount = cluster.Count;
        }
        return clusterLinkedList.Select(c => c.Finalize()).ToList();
    }

    private LinkedListNode<T> nextOrDefault<T>(LinkedList<T> list, LinkedListNode<T> start, Func<T, bool> condition)
    {
        while (start.Next != null)
        {
            if (condition(start.Value)) return start;
            start = start.Next;
        }
        return null;
    }

    private string getKey(int cx, int cy)
    {
        return cx + ":" + cy;
    }

    private class Cluster : Item
    {
        public Cluster()
        {
            SumX = 0;
            SumY = 0;
        }

        public double SumX { get; set; }
        public double SumY { get; set; }

        public List<string> Keys { get; set; }

        //public List<Item> Items { get; set; }
        public int Count { get; set; }
        public Item Item { get; set; }

        public List<string> Neighbors { get; set; }


        /// <summary>
        /// the function that finalizes the computation of the values or even decides to return the only item in the cluster
        /// </summary>
        /// <returns></returns>
        public Item Finalize()
        {
            //if (Items.Count == 1) return Items[0];
            if (Count == 1) return Item;
            /*Y = SumY / Items.Count;
            X = SumX / Items.Count;
            Count = Items.Count;*/
            Y = SumY / Count;
            X = SumX / Count;
            Count = Count;
            return this;
        }
    }
}

old List based code (click to show)

How to automatically log in through Remote Desktop Connection when the admin disabled saving credentials

Well, sometimes an admin will try to make the system secure by annoying the people who have to use it. Yeah, that always works. My situation is that I have to login every day into a virtual machine that is on a "secure network". So after using a very restrictive password policy that forces everybody to be creative in the way they write "password" and "123456", he also disallowed the saving credentials in Remote Desktop Connection. So every day I have to enter the damn complicated password. I couldn't have that. Here is a .js script that you execute with WScript and it logs you in automatically:
var shell = WScript.CreateObject("WScript.Shell");
shell.Run("mstsc /v:[remote server] /console");
while (!shell.AppActivate("Windows Security")) {
    WScript.Sleep(100);
}
WScript.Sleep(100);
shell.SendKeys("[password]{enter}");

Save this into a Javascript file and replace [remove server] and [password] with your settings and either double click the .js file or create a batch file like this:
@echo off
start "Auto log on!" wscript c:\Batches\autologin.js

Of course, this means your secure password will be stored in a stupid text file somewhere, so be warned.

Friday, February 21, 2014

The trailing spaces in T-SQL strings

This is one of those WTF moments. After more than a decade of working in software development I learn something this basic about T-SQL (or rather, any flavour based on SQL-92). What would you think happens when running this script?
IF ''='                 ' SELECT 'WTF?!' -- empty string compared to a bunch of spaces
IF '      '='           ' SELECT 'WTF?!' -- bunch or spaces compared to another bunch of spaces of different length
IF 'x'='x               ' SELECT 'WTF?!' -- 'x' compared to 'x' followed by a bunch of spaces
IF 'x'='               x' SELECT 'WTF?!' -- 'x' compared to 'x' preceded by a bunch of spaces

There will be three WTF rows returned, for the first three queries. You don't believe me? Try it yourself. The motive is explained here: INF: How SQL Server Compares Strings with Trailing Spaces. Short story shorter: in order for SQL to compare two strings of different lengths, it first right-pads the shorter one with spaces.

So what can you do to fix it? Easy enough, use LEN ,right? Nope. Read the definition carefully: Returns the number of characters of the specified string expression, excluding trailing blanks. A possible but weird solution is to use DATALENGTH. A string is empty only is it has a datalength of 0. In the case of NVARCHAR you could even divide the resulting number to 2 in order to get the true length of the string. WTF, right?

Tuesday, February 18, 2014

Careful when using equality in T-SQL

Well, it's pretty obvious, but I wanted to post it here, as well. You see, we have this query that was supposed to filter some values based on a parameter. The query was done like this: SELECT * FROM table WHERE value=(CASE WHEN @filter IS NULL THEN value ELSE @filter). Can you spot the problem? Indeed, if value is NULL, then value is NOT equal to value. Not only is this incorrect, but also bad from the standpoint of the SQL execution plan. It is much faster to do SELECT * FROM table WHERE (@filter is NULL OR value=@filter). If, for whatever reason, you need the first syntax, you need to do it like this: SELECT * FROM table WHERE ISNULL(value,@impossibleValueThatIsNotNull)=COALESCE(@filter, value, @impossibleValueThatIsNotNull). Yeah, a bit of a show off, but when the "no filter" value is null, it's better to use ISNULL and COALESCE wherever possible.

Coma - Chip

I was just thinking about Coma a few days ago. I don't know why. I thought I miss one of their beautiful songs. And here I see on YouTube they released a new video just when I was thinking of them. This one is a very nice combination of Catalin's lyrics, melodic and hard sounds and a cool interweave of the voices of Catalin and Dan - it's not the usual contrast between singing and shouting, but rather a vocal collaboration which works surprisingly well. Without further due, here it is.
Chip, by Coma:
\

Also, if you want to see a live version:

Monday, February 17, 2014

Using Prixovy to forward to an authenticated HTTP proxy without the annoyance of entering the username and password every time

I have at work a very annoying HTTP proxy that requires a basic authentication. This translates in very inconsistent behaviour between applications and the annoying necessity of entering the username and password whenever the system wants it. So I've decided to add another local proxy to the chain that handles the authentication for me forever.

This isn't as easy as it sounds. Sure, proxies are a dime a dozen, but for some reason most of them seem to be coming from the Linux world. That is really bad user interaction, vague documentation and unhelpful forums where any request for help ends up in some version of "read the fucking manual". Hence I've decided to help out by creating this lovely post that explains how you can achieve the purpose described above with very little headache. These are the very easy steps that you have to undertake:
  1. Download and install Privoxy
  2. Go to the Program Files folder and look for Privoxy. You will need to edit two files: config.txt and user.action
  3. Optional: change the listen-address port, otherwise the proxy will function on port 8118
  4. Enter your proxy authentication username and password in the fields below and press Help me configure Privoxy - this is strictly a client base Javascript so don't worry that I am going to steal your proxy credentials...
  5. Edit user.action and add the bit of text that appeared as destined for that file.
  6. Edit config.txt, look for examples of forward and add to it the bit that belongs to config.txt and replace proxy:port and the domains and IP masks with the correct values for you
  7. Restart Privoxy
  8. Configure your internet settings to use a proxy on 127.0.0.1 and the port you configured in step 2 (or the default 8118)

This should be it. Enjoy!

Username:

Password:





'decimal' is not a recognized built-in function name when using TRY_CONVERT (or some other SQL 2012 function)

I made a function in T-SQL that parsed some string and returned a decimal. It all worked fine until one of my colleagues wanted to use it on the test server. And here there was, a beautiful error message: 'decimal' is not a recognized built-in function name. I isolated the line, executed it, same error. It was like the server did not understand decimals anymore. The line in question was a simple SELECT TRY_CONVERT(DECIMAL(18,6),'10.33'). If I used CONVERT, though, it all worked fine. Another hint was that the function worked perfectly fine on the same server, in another database. The problem was that for that particular database, the defined SQL server version was 2008, not 2012. We changed it and it all worked fine after that. The setting in question is found in the Properties of the database, Options, Compatibility level.

Sunday, February 16, 2014

Animating a WPF property when it changes to a non specified value

While working on a small personal project, I decided to make a graphical tool that displayed a list of items in a Canvas. After making it work (for details go here), I've decided to make the items animate when changing their position. In my mind it had to be a simple solution, akin to jQuery animate or something like that; it was not.

The final solution was to finally give up on a generic method for this and switch to the trusted attached properties. But if you are curious to see what else I tried and how ugly it got, read it here:
Click here to get ugly!

Well, long story short: attached properties. I created two attached properties CanvasLeft and CanvasTop. When they change, I animate the real properties and, at the end of the animation, I set the value. Here is the code:
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media.Animation;

namespace Siderite.AttachedProperties
{
    public static class UIElementProperties
    {
        public static readonly DependencyProperty CanvasLeftProperty = DependencyProperty.RegisterAttached("CanvasLeft", typeof(double), typeof(UIElementProperties), new FrameworkPropertyMetadata(
                                                                                            0.0,
                                                                                            FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior,
                                                                                            CanvasLeftChanged));

        [AttachedPropertyBrowsableForType(typeof(UIElement))]
        public static double GetCanvasLeft(DependencyObject element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (double)element.GetValue(CanvasLeftProperty);
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public static void SetCanvasLeft(DependencyObject element, double value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(CanvasLeftProperty, value);
        }

        private static void CanvasLeftChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sb = new Storyboard();
            var oldVal = (double)e.OldValue;
            if (double.IsNaN(oldVal)) oldVal = 0;
            var newVal = (double)e.NewValue;
            if (double.IsNaN(newVal)) newVal = oldVal;
            var anim = new DoubleAnimation
            {
                From = oldVal,
                To = newVal,
                Duration = new Duration(TimeSpan.FromSeconds(1)),
                FillBehavior = FillBehavior.Stop
            };
            Storyboard.SetTarget(anim, d);
            Storyboard.SetTargetProperty(anim, new PropertyPath("(Canvas.Left)"));
            sb.Children.Add(anim);
            sb.Completed += (s, ev) =>
            {
                d.SetValue(Canvas.LeftProperty, newVal);
            };
            var fe = d as FrameworkElement;
            if (fe != null)
            {
                sb.Begin(fe, HandoffBehavior.Compose);
                return;
            }
            var fce = d as FrameworkContentElement;
            if (fce != null)
            {
                sb.Begin(fce, HandoffBehavior.Compose);
                return;
            }
            sb.Begin();
        }
        

        public static readonly DependencyProperty CanvasTopProperty = DependencyProperty.RegisterAttached("CanvasTop", typeof(double), typeof(UIElementProperties), new FrameworkPropertyMetadata(
                                                                                        0.0,
                                                                                        FrameworkPropertyMetadataOptions.OverridesInheritanceBehavior,
                                                                                        CanvasTopChanged));

        [AttachedPropertyBrowsableForType(typeof(UIElement))]
        public static double GetCanvasTop(DependencyObject element)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            return (double)element.GetValue(CanvasTopProperty);
        }

        [DesignerSerializationVisibility(DesignerSerializationVisibility.Visible)]
        public static void SetCanvasTop(DependencyObject element, double value)
        {
            if (element == null)
            {
                throw new ArgumentNullException("element");
            }
            element.SetValue(CanvasTopProperty, value);
        }

        private static void CanvasTopChanged(DependencyObject d, DependencyPropertyChangedEventArgs e)
        {
            var sb = new Storyboard();
            var oldVal = (double)e.OldValue;
            if (double.IsNaN(oldVal)) oldVal = 0;
            var newVal = (double)e.NewValue;
            if (double.IsNaN(newVal)) newVal = oldVal;
            var anim = new DoubleAnimation
            {
                From = oldVal,
                To = newVal,
                Duration = new Duration(TimeSpan.FromSeconds(1)),
                FillBehavior = FillBehavior.Stop
            };
            Storyboard.SetTarget(anim, d);
            Storyboard.SetTargetProperty(anim, new PropertyPath("(Canvas.Top)"));
            sb.Children.Add(anim);
            sb.Completed += (s, ev) =>
            {
                d.SetValue(Canvas.TopProperty, newVal);
            };
            var fe = d as FrameworkElement;
            if (fe != null)
            {
                sb.Begin(fe, HandoffBehavior.Compose);
                return;
            }
            var fce = d as FrameworkContentElement;
            if (fce != null)
            {
                sb.Begin(fce, HandoffBehavior.Compose);
                return;
            }
            sb.Begin();
        }
    }
}

and this is how you would use them:

<ListView ItemsSource="{Binding KernelItems}" 
          SelectedItem="{Binding SelectedItem,Mode=TwoWay}"
          SelectionMode="Single"
          >
    <ListView.ItemsPanel>
        <ItemsPanelTemplate>
            <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Black" />
        </ItemsPanelTemplate>
    </ListView.ItemsPanel>
    <ListView.ItemContainerStyle>
        <Style TargetType="{x:Type ListViewItem}">
            <Setter Property="FocusVisualStyle" Value="{x:Null}"/>
            <Setter Property="Foreground" Value="White"/>
            <Setter Property="att:UIElementProperties.CanvasLeft" >
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource CoordinateConverter}">
                        <Binding Path="X"/>
                        <Binding Path="ActualWidth" ElementName="lvKernelItems"/>
                        <Binding Path="DataContext.Zoom" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
                    </MultiBinding>
                </Setter.Value>
            </Setter>
            <Setter Property="att:UIElementProperties.CanvasTop" >
                <Setter.Value>
                    <MultiBinding Converter="{StaticResource CoordinateConverter}">
                        <Binding Path="Y"/>
                        <Binding Path="ActualHeight" ElementName="lvKernelItems"/>
                        <Binding Path="DataContext.Zoom" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
                    </MultiBinding>
                </Setter.Value>
            </Setter>
            <Style.Resources>
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
                <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
                <SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
            </Style.Resources>
            <Style.Triggers>
                <Trigger Property="IsSelected" Value="True">
                    <Setter Property="Foreground" Value="Cyan"/>
                    <Setter Property="Effect">
                        <Setter.Value>
                            <DropShadowEffect ShadowDepth="0" Color="White" Opacity="0.5" BlurRadius="10"/>
                        </Setter.Value>
                    </Setter>
                    <Setter Property="Canvas.ZIndex" Value="1000"/>
                </Trigger>
            </Style.Triggers>
        </Style>
    </ListView.ItemContainerStyle>
</ListView>

Hope it helps.

Whisper of the Heart (Mimi wo sumaseba) - Ah, young love! But there is so much more...

DVD cover I've reached the last of the animes in the Studio Ghibli series that I wanted to watch (again) and it was nice that this one got to be the final one. You see, before that I had watched The Cat Returns and I rated it mediocre, so unlike the beautiful movies from the same collection. Whisper of the Heart seems to be the film designed to redeem it.

The story is that of a young girl who likes to read a lot of books. She notices that most of the books that she borrowed from the library had the same name on their library cards, a boy that she didn't know. Coincidentally she follows a fat cat, apparently named Muta, to the shop of an old man who has a beautiful doll of a cat in a suit: the Baron of Gikkingen. You guessed it, two characters from The Cat Returns. And behold, the old man is the grandfather of the boy that kept borrowing the same books.

Whisper of the Heart seems to just take beautiful elements from other Ghibli animes and bring them all together in a wonderful union. The windy hills of Tokyo, which still has beauty despite the expansion of the city. The young girl who is not only smart and sensible, but also ambitious and kind. The family who is sometimes annoying and overbearing, but that in the end is the source of support for the development of the child it nurtures. The indolent fat cat :)

And then the love story, something that springs from common interests and a karmic connection between two people who seem to have been meant for each other. But there is more. They don't just click and that's it; they get motivated and energized to be the best of what they can be in order to honor the relationship in which they enter. In a way, it is a continuation of the warm and supporting family model from which both protagonists come.

One of the scenes in the anime was so funny to my wife that she spoke the Japanese words from it for a week. What a wonderful thing to have a film that not only makes me want to be a better man, but that already does make me be so by connecting me stronger to the one I love. And I watched it on Valentine's day, too! How can I rate it any less than with a perfect 10?

Returning to The Cat Returns, it somehow felt to me that the story linked to it also from the perspective of the ever aspiring artist; the rough and unpolished plot there sounds a lot like the story Shizuku writes, her first but one in many, the stone that will allow her to get to the skill and experience to do this story, which is so much better and complete. It does seem that way to me, since I watched The Cat Returns first, but chronologically Whisper of the Heart was made seven years earlier.

Now I don't know exactly in which proportion is Hayao Miyazaki responsible for the great quality of this film and story and how much Hiiragi Aoi, the writer of the original manga, but I heartily recommend the end result. I may be exaggerating, but this could be the best anime Studio Ghibli ever did, and that is saying much.

Displaying Listview/Listbox items in a Canvas in Windows Presentation Foundation

For a WPF project I wanted to create a graphical representation of a list of items. I computed some X,Y coordinates for each item and started changing the XAML of a Listview in order to reflect the position of each item on a Canvas. Well, it is just as easy as you imagine: change the ItemsPanel property to a Canvas and then style each item as whatever you want. The gotcha comes when trying to set the coordinates. The thing is that for each item in a listview a container is constructed and inside the item template is displayed. So here you have all you items displayed exactly as you want them, except the coordinates don't work, since what needs to be placed on the Canvas are the generated containers, not the items. Here is the solution:
<Window.Resources>
        <local:CoordinateConverter x:Key="CoordinateConverter"/>
        <DataTemplate DataType="{x:Type vm:KernelItem}"> <!-- the template for the data items -->
            <Grid>
                <Ellipse Fill="{Binding Background}" Width="100" Height="100" Stroke="DarkGray" Name="ellipse"
                         ToolTip="{Binding Tooltip}"/>
                <TextBlock Text="{Binding Text}" MaxWidth="75"  MaxHeight="75"
                           HorizontalAlignment="Center" VerticalAlignment="Center"
                           TextAlignment="Center"
                           TextWrapping="Wrap"
                           ToolTip="{Binding Tooltip}" />
            </Grid>
        </DataTemplate>
    </Window.Resources>
    <ListView ItemsSource="{Binding KernelItems}" Name="lvKernelItems"
              SelectedItem="{Binding SelectedItem,Mode=TwoWay}"
              SelectionMode="Single"
              >
        <ListView.ItemsPanel>
            <ItemsPanelTemplate>
                <Canvas HorizontalAlignment="Stretch" VerticalAlignment="Stretch" Background="Black" />
            </ItemsPanelTemplate>
        </ListView.ItemsPanel>
        <ListView.ItemContainerStyle>
            <Style TargetType="{x:Type ListViewItem}">
                <Setter Property="FocusVisualStyle" Value="{x:Null}"/><!-- no highlight of selected items -->
                <Setter Property="Foreground" Value="White"/>
                <Setter Property="(Canvas.Left)" >
                    <Setter.Value>
                        <MultiBinding Converter="{StaticResource CoordinateConverter}">
                            <Binding Path="X"/>
                            <Binding Path="ActualWidth" ElementName="lvKernelItems"/>
                            <Binding Path="DataContext.Zoom" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
                <Setter Property="(Canvas.Top)" >
                    <Setter.Value>
                        <MultiBinding Converter="{StaticResource CoordinateConverter}">
                            <Binding Path="Y"/>
                            <Binding Path="ActualHeight" ElementName="lvKernelItems"/>
                            <Binding Path="DataContext.Zoom" RelativeSource="{RelativeSource AncestorType={x:Type Window}}"/>
                        </MultiBinding>
                    </Setter.Value>
                </Setter>
                <Style.Resources><!-- no highlight of selected items -->
                    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightBrushKey}" Color="Transparent" />
                    <SolidColorBrush x:Key="{x:Static SystemColors.ControlBrushKey}" Color="Transparent" />
                    <SolidColorBrush x:Key="{x:Static SystemColors.HighlightTextBrushKey}" Color="Black" />
                    <SolidColorBrush x:Key="{x:Static SystemColors.ControlTextBrushKey}" Color="Black" />
                </Style.Resources>
                <Style.Triggers>
                    <Trigger Property="IsSelected" Value="True"><!-- custom selected item template -->
                        <Setter Property="Foreground" Value="Cyan"/>
                        <Setter Property="Effect">
                            <Setter.Value>
                                <DropShadowEffect ShadowDepth="0" Color="White" Opacity="0.5" BlurRadius="10"/>
                            </Setter.Value>
                        </Setter>
                        <Setter Property="Canvas.ZIndex" Value="1000"/>
                    </Trigger>
                </Style.Triggers>
            </Style>
        </ListView.ItemContainerStyle>
    </ListView>

As a bonus, you see the way to remove the default selection of an item: the ugly dotted line and the highlighting background.