0

Winnipeg Jets unveil their new logos

Wiiipeg

Since I used to be a print graphic designer and I fancy myself a decent eye for art, I wanted to weigh in on the new Winnipeg Jets logo that has been unveiled. In general I think it’s pretty nice – with some caveats.

Let’s start with the circular logo. It’s pretty awesome. It evokes a compass rose and in that visualization it works very well. The lines are clean and I like the shape of the jet aircraft. I like the incorporation of Canada’s symbol of the red maple leaf. However one thing really perplexes me. The hues of the red are very close to those grays in the aircraft. A black and white or greyscale version of that mark would show the problem – visual separation of the jet and the maple leaf is very low. My guess is there a distinct black and white and greyscale version of this mark – I don’t have a problem with that at all. But in it’s colored version I think the colors are too close. Perhaps a little white bounding rule for the jet might have worked better here. But I like the mark a lot.

The pilot wings mark. I like this too. You know those wings pilots wear to show they earned their place in the sky? It’s been incorporated here with the crossed hockey sticks and the maple leaf. There is no jet per sé, but I like the flying metaphor a lot. It makes sense if you are aware of the symbol. I wonder if there was a version that mimicked actual aircraft wings or not too.

The typographic logo is nice as well. Modern fonts combined and the “Jets” reminds me of skywriting a little bit. The letters are joined together in a kind of quasi-cursive implementation. I imagine that the “s” evokes the most feeling of the skywriting to me, and it’s very effective.

Overall whomever was responsible for the graphic design of these marks did a very good job. Grey and red together is a tough marriage of hues but they mostly made it work here. Well done.

Popularity: 2%

0

Have a cool coding weekend

Melt you f'ing face off!

I think that Hades might sprung a leak on us here for a little while over most of the United States. It’s reading 98 degrees here in Boston with a humidity index of around 106 degrees Fahrenheit (a little over 41 Celsius). Stay inside and code in a nice climate controlled environment or get your butt in some water. Else you’ll suffer the fate of this happy camper above.

Popularity: 1%

2

Poll: Mac OS X Lion

It hasn’t been long, but I wanted to poll some visitors to see where they have landed mentally in regards to this Mac OS X Lion operating system.

This poll expires at the end of the month since it’s timely.

Popularity: 2%

25

Keyboard shortcut to Launchpad on OS X Lion

By default when running OS X Lion there isn’t a keyboard shortcut to Launchpad. You could use a trackpad or a magic mouse, but what if you don’t want to buy one? Keyboard shortcut.

Launchpad is something I could use to avoid going to the dock for anything (Alfred has been updated and works with Lion). So if I don’t want to depend on Alfred (which totally rocks), Launchpad is great for finding an application when I forget it’s name or isn’t properly indexed by Alfred yet.

Go to System Preferences > Keyboard > Keyboard Shortcuts. Click on Launchpad & Dock. You’ll see that Show Launchpad is selected but there is no shortcut associated with it. In a fit of usability awkwardness (very un-Apple), it’s not clear how to set the shortcut. Double-click to the right of the row below the dock hiding shortcut, and an input field will appear. Perform your shortcut input there (I chose to use CMD-OPT-L). It’s set and will trigger now.

I’m not sure what genius thought that double clicking into a white area would be discoverable, but there you go. Launchpad on demand.

Popularity: 100%

0

Window shadowing has changed in Lion

Screen Shot
And I think I like it. Much larger and bolder effect.

Popularity: 2%

0

Follow up to “Optimize a search in an NSMutableDictionary”

Warp Speed

This is a follow up post to Optimize a search in an NSMutableDictionary. Where that post left off I had only managed to save about 500ms of processing time in a song search. With some help I employed hashKeys and reduced that processing time by much more than half. Depending on the quantity of stored elements in a NSMutableDictionary associated with a hashKey, it could be a lot more than that, say 500ms total.

So employing 2 methods to aid in the creation of a hashKey and in the retrieval of a candidate NSMutableDictionary object is the heart of the speed increase. I present for you the two methods (non-sanitized… if you want to employ yourself, use your own objects).

#pragma mark - hashKey stuff for song titles

- (int) computeHashKeyForString:(NSString *)songName {
    NSUInteger length = [songName length];
    uint hash = length << 24;
    uint sumOfChars = 0;
    for (NSUInteger i = 0; i < length; i ++) {
        sumOfChars += [songName characterAtIndex:i];
    }
    return hash + ( sumOfChars / 8 ) & 0x00ffffff;
}

- (NSMutableDictionary *) candidatesForString:(NSString *)userString {
    int hash = [self computeHashKeyForString:userString];
    int fuzzyKey = 0;
    for (id key in [testSongsDictionary allKeys]) {
        if (hash == [key intValue])
            return [testSongsDictionary objectForKey:key];

        if (abs (hash - [key intValue]) < (hash - fuzzyKey)) {
            fuzzyKey = [key intValue];
        }
    }
    id realKey = [NSNumber numberWithInt:fuzzyKey];
    return [testSongsDictionary objectForKey:realKey];
}

Now, in my data generation method this is how I am storing the songs based on hashKey:

testSongsDictionary = [[NSMutableDictionary alloc] init];
    @autoreleasepool {
        MPMediaQuery *allSongsQuery = [MPMediaQuery songsQuery];
        NSArray *mySongsArray = [allSongsQuery items];
        //testSongsDictionary is the master songs NSMutableDictionary
        for(MPMediaItem *song in mySongsArray){
            NSString *songTitle = [[song valueForProperty: MPMediaItemPropertyTitle] lowercaseString];
            //Remove 01 -, 02 - from titles
            NSRegularExpression *regex = [NSRegularExpression regularExpressionWithPattern:@"^\\d+ - "
                                                                                   options:NSRegularExpressionCaseInsensitive
                                                                                     error:NULL];
            songTitle = [regex stringByReplacingMatchesInString:songTitle
                                                        options:0
                                                          range:NSMakeRange(0,[songTitle length])
                                                   withTemplate:@""];
            //Remove all (Album Version), etc.
            NSRegularExpression *regex2 = [NSRegularExpression regularExpressionWithPattern:@"(.*?)" options:NSRegularExpressionCaseInsensitive error:NULL];
            songTitle = [regex2 stringByReplacingMatchesInString:songTitle options:0 range:NSMakeRange(0, [songTitle length]) withTemplate:@""];

            NSRegularExpression *regex3 = [NSRegularExpression regularExpressionWithPattern:@" - " options:NSRegularExpressionCaseInsensitive error:NULL];
            songTitle = [regex3 stringByReplacingMatchesInString:songTitle options:0 range:NSMakeRange(0, [songTitle length]) withTemplate:@" "];
            //NSDictionary require object for key
            int hashKey = [self computeHashKeyForString:songTitle];
            id realKey = [NSNumber numberWithInt:hashKey];
            if([testSongsDictionary objectForKey:realKey] != nil){
                NSMutableDictionary *foundDictionary = [testSongsDictionary objectForKey:realKey];
                [foundDictionary setObject:song forKey:songTitle];
            } else {
                NSMutableDictionary *subDictionary = [[NSMutableDictionary alloc] init ];
                [subDictionary setObject:song forKey:songTitle];
                [testSongsDictionary setObject:subDictionary forKey:realKey];
            }
        }
    }

That’s the crux of the entire optimization. To snag a match requires very little code.

NSMutableDictionary *tester = [self candidatesForString:[stringValue lowercaseString]];
        MPMediaItem *song = [tester objectForKey:[stringValue lowercaseString]];
        NSArray *dis;
        MPMediaItemCollection *collection;
        if(song != nil)
        {
            dis = [NSArray arrayWithObject:song];
            collection = [[MPMediaItemCollection alloc] initWithItems:dis];
        }

Have a wonderful day, it’s OS X Lion day afterall!

Popularity: 2%

0

Optimize a search in an NSMutableDictionary

A data blender

This post will likely be updated a few times as I learn on the fly and relay experience gained information. If anyone is reading this.

I have a situation where I need to search through an NSMutableDictionary in the hopes of getting a fuzzy match to happen. The NSMutableDictionary in question is populated by items on an iOS device’s onboard music library. Currently I am testing with 1,777 items. That doesn’t seem like a huge amount, but running a traditional for…in loop takes about 6 seconds to run through the whole thing.

Here is my original loop code without surrounding method context (it’s simply lifted out to make things easier):

for (id key in songsDictionary) {
                NSString *thisSong = key;
                int suppliedCount = [stringValue length];
                int keyCount = [thisSong length];
                if(suppliedCount > keyCount){
                    match= [StringDistance stringDistance:thisSong :stringValue];
                } else {
                    match= [StringDistance stringDistance:stringValue :thisSong];
                }
                //Get the very best match there is to be found for song.
                if(match < currentFoundValue){
                    currentFoundValue = match;
                    test = [songsDictionary objectForKey:thisSong];
                    dis = [NSArray arrayWithObject:test];
                    collection = [[MPMediaItemCollection alloc] initWithItems:dis];
                }
            }

This takes about 6 seconds to process and get through. Now, someone suggested I try something else. So I looked at enumerateKeysAndObjectsWithOptions. I had to change my code a little bit (outside the loop) so they were block compliant. I never heard of that before, but basically you just prefix your vars with __block. That’s two underscores and lowercase block. So my MPMediaItem *test became __block MPMediaItem *test. Simple enough when you know about it, I had to Google after the compiler yelled at me, then I figured it out. Anyway, I tried this method and it’s shaved off MAYBE a second from the total time. Not a great optimization, but it’s something.

int suppliedCount = [stringValue length];
            [songsDictionary enumerateKeysAndObjectsWithOptions:NSEnumerationConcurrent usingBlock:^(id key, id obj, BOOL *stop) {
                NSString *thisSong = key;
                int keyCount = [thisSong length];
                if(suppliedCount > keyCount){
                    match = [StringDistance stringDistance:thisSong :stringValue];
                } else {
                    match = [StringDistance stringDistance:stringValue :thisSong];
                }
                if(match < currentFoundValue)
                {
                    currentFoundValue = match;
                    test = [songsDictionary objectForKey:thisSong];
                    dis = [NSArray arrayWithObject:test];
                    collection = [[MPMediaItemCollection alloc] initWithItems:dis];
                }
            }];

Now, someone else told me to try something else too. This part I have not done yet. I will quote them but won’t give named credit as I didn’t ask them permission to use their name.

Simple example. Init a NSMutableDictionary. For each string, compute a hash key as the sum of all chars composing it (in a short or int), divide it by 8 (that’s quick). This way, all the words that differ by just one char being exchanged (metathesis), or look more or less the same, will get an identical hash code.

In your mutable dictionary, use the hash key as the key, the object being an NSMutableArray where you put all the strings that share the same hash key. Then use the contents of this NSMutableArray as proposals for your user. That should be really fast. Refine the process if you need more grain.

You can try to use that (enumerateKeysAndObjectsWithOptions), but, basically, it is the same problem: you enumerate all entries in your dictionary and compute your string distance for each, which is cumbersome. The algorithm I propose you is way faster, because you don’t have to recompute this distance each time you search. But, once again, the crude way to compute a hash code might not suit your needs.

So now I am looking into trying this technique out and see what it produces. It’s going to be a bit of extra code to slap in there, but since I’m rocking SVN I can just blow it out if it doesn’t work correctly.

Update:

I think that I finally understand what that quote above is actually telling me to do. I wasn’t exactly clear before.

  • Compute a key for the song during NSMutableDictionary creation that’s based on the number of characters in the string. This could be an int for example.
  • All songs with the same length of characters in it’s string would be ganged up under the auspices of that key… the object for that key (let’s say for example the key is 20) would be an NSMutableDictionary whose key would be song title and object be an MPMediaItem.
  • So now when searching one doesn’t need to rip through every single item, but intelligently narrow down which (or which few if one employs a close but not exact match allowance) group of potential matches to dig through, saving oodles of time.

Instead of tearing up code to give this a go, I plan on just adding another step in the data generation method and optionally use it’s architecture to see how it speeds things up without degrading accuracy.

Fun stuff :)

Update2: 

That’s not what he was exactly on about. (I need clarification because I don’t quite get the intricacies quite yet).

> a song search for “Cold Wind to Valhalla” … char count of 21, look in the dictionary for key of int 21 (or close), and check it (probably another dictionary) for it’s key of the title to get the MPMediaItem.

Not quite. “Cold Wind…” you add all the ascii codes of the chars – you may want to lowercase the string before, so it is case insensitive, thus you hash key would be :
key = 21 [length of your string] << 24 + (‘c’ + ‘o’ + ‘l’ + ‘d’ + ‘ ‘ + …) % (2^24 – 1).

You then look up all the keys whose upper byte is 21 ((keys >> 24) == 21) and then selected the one whose hash key (hashkey & 0x00ffffff) is the nearest to (key & 0x00ffffff).

Update 2:

Wow did I get a lot of help. After a lot of massaging and patting, the search that took 6 seconds each time now takes 500ms to 1000ms. That’s a HUGE improvement!!! I don’t have the time to post up how it works just yet, but it’s a great solution. Thank you Vincent!

Popularity: 3%

0

MSF Day Two (final day)

MSF Card

In all of it's glory

Woohoo! After another day of course instruction and a multiple choice test (really easy, scored a 100%) and then off to the course.

We got there early and watched the morning driving class take their driving exam. Watching them made me nervous and I was basically one form or another unsettled. We did the box and the swerve in 2nd gear  & stop and drop to 1st.

The box wasn’t easy at all, but after awhile I was starting to get it. And then off to quicker speed drills. After a while I thought to myself, “Screw it, relax and have fun on this loaner motorcycle.” And things got better.

After the previous day being around 90 degrees, today was around 95. But there was a slight breeze and my body had become accustomed to the heat. Given that though throughout the day I drank about a gallon of water, with no exaggeration.

I was in the middle of the pack and after seeing some others struggling a bit with he box, I felt a little more relaxed. Of course I broke a boundary marker doing the box, but the did the swerve and stop just fine.

Next was quick stop in 2nd gear. I forgot what my distance was, and I can’t tell you how fast I was travelling because my speedometer didn’t work. Neither did the gearing really and 1st gear was exceptionally jumpy without really giving it the business. It could be some fault of mine (I’m sure some) but I hated that bike. And of course I got it two days in a row. I did well on the stop.

Then there was the two turns things, slowing for the second before rolling through it. Did really well again.

A guy who dropped his bike HARD about 30 minutes before the evaluation test did among the best of the group. One guy (who was really annoying) scored perfect (a zero).

Everyone in the class passed and overall it was a whole lot of fun. I can’t say those of us without bikes could handle a 675cc bike or not, but at least the equation has been laid out and is fully understandable.

Good day :)

 

Popularity: 1%

0

MSF Day One

I have just completed my first day of motorcycle training and it was awesome. The first half of the day was spent in the classroom watching a few videos, breaking into groups of four (there were twelve total). So we were able to get to know each other and it is a great group. There are a few women in it which was a pleasant surprise. Some of the people there came with lots of fancy gear and some already owned bikes.

The story of the day in regards to those who had machines already was because they found people on Craigslist selling their bikes because they had accumulated too many DUIs. One guy who is active duty Air Force got his bike from someone being stationed elsewhere and didn’t want to bother shipping it. So many already made the decision already. I was surprised by that too.

The book part of the class was basically open-book answering questions. Groups would take a set of them and answer them on their own, and then the instructor would go through each one and the answers would ring out from those assigned to get the answers. It seemed a bit tedious at first, but the result was that the information was much easier to remember. And he added a lot of little tips here and there beyond the book’s supplied answers.

A super-quick lunch and a twenty minute drive to the training center field and it was time to start riding. I won’t bore you with the drills, but each built upon the previous. My bike wouldn’t shift into neutral, it simply refused. So I had to turn the bike off and then it would. Strange and a pain in the ass, but most of the time I just held the clutch lever and moved it around in 1st when I needed to. No big deal.

I struggled with first gear quite a lot. By the end of the day I figured out how to smoothly take off from a stop, but in first the throttle is extremely twitchy. I managed to learn how to work it better, but in general first gear was a little jerky for me and I disliked it.

Second gear was a lot better as more throttle affects the bike a little less. And third gear was a joy. Moving at speed felt so good, the bike was under greater control and I was leaning well in the corners rolling as much as I liked (seemingly).

There was only one drop and that was at the end of the day during the quick stop drills (2nd to first and all brakes hard). He was fine and got right back on. Some of the girls early in the day were stalling like mad. Hell, I stalled a few times too. I suppose it’s expected. My stalls usually came from being in second gear at a stop and trying to smoothly take off.

The only thing I was frustrated about was the offset cones slalom courses. Sometimes I’d get them down pretty well, and then other times by speed was too fast or my throttle wasn’t smooth and I’d miss a cone here or there. Still pisses me off, I was fine at everything else.

It was 90 degrees today and holy wow was it hot. I wore a full-faced helmet (visor up with sunglasses), and of course long sleeves and jeans with bike boots. I brought 2 gatorade bottles and a few 16 ounce Poland Spring bottles, and it wasn’t enough. Tomorrow is going to be hotter. Not looking forward to that AT ALL.

Driving home I felt like I was driving so slowly, that sensation of speed missing due to the windshield of the car. As hot as it was, and as sore as my neck is (look to turn, etc.) I could have driven an hour or two more.

Today I gained a lot of confidence and I am looking forward to tomorrow. I suspect we’ll be doing some harder turning and more twistys and I hope I get the hang of it. Do I think I’ll pass and get my license? I’m honestly not sure, but if that’s my only goal, then this is all merely a waste of time. When I relax on the bike and moving at speed, I am having a lot of fun. On someone else’s motorcycle.

Here’s to tomorrow.

Popularity: 1%

1

The perils of digging into an MPMediaQuery (speed matters)

Hands dirty

Getting your hands dirty is better when it happens quicker.

The Problem. 

If you have ever wanted to repeatedly search an iOS device’s media items you might have found that the more items in the collection the slower your search loop will be. What I have found is that generally speaking instantiating the query itself is nearly instant.

However, any time you perform a valueForProperty on an MPMediaItem – you’re adding to the slowdown. It seems that this is quick operation on it’s own (quick enough) can really add up when run in a loop. The larger the collection, the longer it will take.

I’ve tested on a collection of about 1,800 items and it could potentially take 1-2 minutes to loop through everything in a search, depending on the specifics. That’s not good. Tried CoreData and it didn’t seem to be a ton better either.

My Initial Solution.

Create a few NSMutableDictionaries or perhaps a single über one & loop through collecting all the information you’ll ultimately need to search through. Do this up front.

In my experience with the 1,800 items, it took about 5 seconds to create these dictionaries & then it was done. Searching for artist, album, and genre now takes under one second. Searching for a song takes around six seconds. That’s much improved over what I was experiencing before.

It’s a little more upfront work but it pays for itself easily. You’ll balloon your memory footprint doing this, but it shouldn’t be too terribly bad.

Popularity: 2%

Pages ... 1 2