Scott Hanselman

How To: Convert a PowerPoint Presentation from 4:3 ratio to 16:9 without distorted or stretched images

November 14, 2011 Comment on this post [21] Posted in Musings
Sponsored By

It always sucks to show up to a conference with a slide deck that looks lovely with lots of pictures and evocative hipster stock photography all nicely formatted for a 4:3 ratio (1024x768 pixels is common) and then find out their projectors are 16x9 and run something like 1280x720 pixels.

Nobody wants to be THIS stretchy guy:

My giant head with the wrong ratio

It is true that some people have a genetic inability to see that 4:3 content has been uncomfortably stretched to fit a 16:9 screen. We have a name for these people. They are called "Bad People.™"*

As an aside, there are few sadder technical things than 4:3 content stretched across an expensive 16:9 widescreen HDTV. From electronic stores to hotel lobbies, airport status displays to café menus. Make it stop.

What's the easiest way to convert your 4:3 slide deck to 16:9 in a pitch and still have your images look nice? There' s a number of ways on the internet but they all involve "math" and "ratios" and "thought." Nonsense. Too hard. Here's what I do.

Step 0 - Switch to 16:9 and see bad images.

Click Design | Page Setup and switch from 4:3 to 16:9. Other folks say to enter in custom numbers and do multiplication. Hang in there, this is easier.

First, switch to 16x9

At this point, all your images WILL be stretched out.

Step 1 - Fix image ratios without messing up their sizes

Here's the trick. Right click on an image and select Size and Position. From this dialog, click in the Height box. Now, just click up once and down once. As long as the Lock Aspect Ratio checkbox is checked, just changing the scale by 1 step and then switching back will fix your image. You can do it with your keyboard even faster.

Step two, change the scale up and down

Here's trick #2. You don't need to close the Size and Position dialog. It's modeless. You can leave it open and go from slide to slide quickly changing your images. Just click the image, click Height, then up/down, then do another image. Repeat until it's all done. It'll take just a few seconds per slide.

My enormous corectly ratio-ed head. Or IS IT?!

This trick will fix all your image ratios, but expect to do one more pass to make sure that you're using all the space afforded by this new widescreen layout. Regardless of how you images are sized you might want to make some tweaks to the layout and spacing of your text and images.

How to you switch your slides from 16:9 to 4:3? Exactly the same steps except adjust the ratio in Step 0.

Hope this helps someone.

* They aren't really bad people. They are good people with bad presentations. Relax. It's a blog post.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Solving the Shakespeare Million Monkeys Problem in Real-time with Parallelism and SignalR

November 11, 2011 Comment on this post [26] Posted in ASP.NET | Javascript | SignalR
Sponsored By

A monkey with a skull. Oh yes.A little over 18 months ago I was talking to Stephen Toub (he of the Parallel Computing fame) about parallelism and the kinds of problems it could solve.

I said, naively, "could we solve the million monkey's problem?"

He said, "the what?"

"You know, if you have an infinite number of monkeys and an infinite number of keyboards they will eventually write Shakespeare."

We brainstormed some ideas (since Stephen is a smarter than I, this consisted mostly of him gazing thoughtfully into the air while I sat on my hands) and eventually settled on an genetic algorithm. We would breed thousands of generations of (hypothetical) monkeys a second and then choose which ones would be allowed to perpetuate the species based solely on their ability to write Shakespeare.

We used the .NET 4 Task Parallel Library to make it easier for the algorithm to scale to available hardware. I mean, anyone can foreach over a million monkeys. But loops like that in parallel over 12 processors takes talent, right? Well, kinda. A lot of it is done for you by the Parallelism Features in .NET and that's the point. It's Parallel Processing for the Masses.

We created a WinForms version of this application and I've used it on and off to demonstrate parallel computing on .NET. Then Paul Batum and I went to the Keeping It Realtime conference to demonstrate SignalR this last week. I didn't want to do the same "here's a real-time chat app" or "here's a map that shows its results in real-time" demos that one always does at these kinds of things. I suggested that we port our WinForms Shakespeare Monkey demo to ASP.NET and SignalR and that's what Paul proceeded to do.

Looks like 80,000 generations of monkeys

When doing something that is crazy computationally intensive but also needs to return real-time results you might think to use node for the real-time notification part and perhaps spawn off another process and use C or something for the maths and then have them talk to each others. We like node and you can totally run node on IIS or even write node in WebMatrix. However, node is good at some things and .NET is good at some things.

For example, .NET is really good at CPU-bound computationally intensive stuff, like, I dunno, parallel matrix multiplication in F# or the like. ASP.NET is good at scaling web sites like Bing, or StackOverflow. You may not think IIS and ASP.NET when you think about real-time, but SignalR uses asynchronous handlers and smart techniques to get awesome scale when using long-polling and scales even more in our labs when using an efficient protocol like WebSockets with the new support for WebSockets in .NET 4.5.

So, we wanted to see if you combined asynchronous background work, use as many processors as you have, get real-time status updates via SignalR over long-polling or Web Sockets, using C#, .NET 4.5, ASP.NET and IIS.

It takes about 80,000 generations of monkeys at thousands of monkey generations a second (there's 200 monkeys per generation) to get the opening line of Hamlet. So that's ~16,000,000 monkeys just to get this much text. As they say, that's a lot of monkeys.

Here's the general idea of the app. The client is pretty lightweight and quite easy. There's two boxes, two buttons and a checkbox along side some text. There's some usual event wireup with started, cancelled, complete, and updateProgress, but see how those are on a monkey variable? That's from $.connection.monkeys. It could be $.connection.foo, of course, as long as it's hanging off $.connection.

Those functions are client side but we raise them from the server over the persistent connection then update some text.

<script src="Scripts/jquery-1.6.4.min.js"></script>    
<script src="Scripts/json2.min.js"></script>
<script src="Scripts/jquery.signalR.min.js"></script>
<script src="signalr/hubs"></script>
<script>
$(function () {
$('#targettext').val('To be or not to be, that is the question;\nWhether \'tis nobler in the mind to suffer\n\The slings and arrows of outrageous fortune,\n\Or to take arms against a sea of troubles,\n\And by opposing, end them.');

var monkeys = $.connection.monkeys,
currenttext = $('#currenttext'),
generationSpan = $('#generation'),
gpsSpan = $('#gps');

monkeys.updateProgress = function (text, generation, gps) {
currenttext.val(text);
generationSpan.text(generation);
gpsSpan.text(gps);
};

monkeys.started = function (target) {
$('#status').text('Working...');
$('#targettext').val(target);
$('#cancelbutton').removeAttr('disabled');
};

monkeys.cancelled = function () {
$('#status').text('Cancelled');
$('#cancelbutton').attr('disabled', 'disabled');
};

monkeys.complete = function () {
$('#status').text('Done');
$('#cancelbutton').attr('disabled', 'disabled');
};

$.connection.hub.start({}, function () {
$('#startbutton').click(function (event) {
$('#status').text('Queued...');
monkeys.startTyping($('#targettext').val(), $('#isparallel').is(':checked'));
});

$('#cancelbutton').click(function (event) {
monkeys.stopTyping();
});
});

});
</script>

The magic start with $.connection.hub.start. The hub client-side code is actually inside ~/signalr/hubs. See how that's include a the top? That client-side proxy is generated based on what hub or hubs are on the server side.

The server side is structured like this:

[HubName("monkeys")]
public class MonkeyHub : Hub
{
public void StartTyping(string targetText, bool parallel)
{
}

public void StopTyping()
{
}

}

The StartTyping and StopTyping .NET methods are callable from the client-side via the monkeys JavaScript object. So you can call server-side C# from the client-side JavaScript and from the C# server you can call methods in JavaScript on the client. It'll make the most sense if you debug it and watch the traffic on the wire. The point is that C# and Json objects can flow back and forth which blurs the line nicely between client and server. It's all convention over configuration. That's how we talk between client and server. Now, what about those monkeys?

You can check out the code in full, but StartTyping is the kick off point. Note how it's reporting back to the Hub (calling back to the client) constantly. Paul is using Hub.GetClients to talk to all connected clients as broadcast. This current implementation allows just one monkey job at a time. Other clients that connect will see the job in progress.

public void StartTyping(string targetText, bool parallel)
{
var settings = new GeneticAlgorithmSettings { PopulationSize = 200 };
var token = cancellation.Token;


currentTask = currentTask.ContinueWith((previous) =>
{
// Create the new genetic algorithm
var ga = new TextMatchGeneticAlgorithm(parallel, targetText, settings);
TextMatchGenome? bestGenome = null;
DateTime startedAt = DateTime.Now;

Hub.GetClients<MonkeyHub>().started(targetText);

// Iterate until a solution is found or until cancellation is requested
for (int generation = 1; ; generation++)
{
if (token.IsCancellationRequested)
{
Hub.GetClients<MonkeyHub>().cancelled();
break;
}

// Move to the next generation
ga.MoveNext();

// If we've found the best solution thus far, update the UI
if (bestGenome == null ||
ga.CurrentBest.Fitness < bestGenome.Value.Fitness)
{
bestGenome = ga.CurrentBest;

int generationsPerSecond = generation / Math.Max(1, (int)((DateTime.Now - startedAt).TotalSeconds));
Hub.GetClients<MonkeyHub>().updateProgress(bestGenome.Value.Text, generation, generationsPerSecond);

if (bestGenome.Value.Fitness == 0)
{
Hub.GetClients<MonkeyHub>().complete();
break;
}
}
}
}, TaskContinuationOptions.OnlyOnRanToCompletion);
}

If he wanted, he could use this.Caller to communicate with the specific client that called StartTyping. Inside ga.MoveNext we make the decision to go parallel or not based on that checkbox. This is where we pick two random high quality parent monkeys from our population for a potential future monkey. Hopefully one whose typing looks more like Shakespeare and less like a Regular Expression.

By simply changing from Enumerable.Range to ParallelEnumerable.Range we can start taking easily parallelizable things and using all the processors on our machine. Note the code is the same otherwise.

private TextMatchGenome[] CreateNextGeneration()
{
var maxFitness = _currentPopulation.Max(g => g.Fitness) + 1;
var sumOfMaxMinusFitness = _currentPopulation.Sum(g => (long)(maxFitness - g.Fitness));

if (_runParallel)
{
return (from i in ParallelEnumerable.Range(0, _settings.PopulationSize / 2)
from child in CreateChildren(
FindRandomHighQualityParent(sumOfMaxMinusFitness, maxFitness),
FindRandomHighQualityParent(sumOfMaxMinusFitness, maxFitness))
select child).
ToArray();
}
else
{
return (from i in Enumerable.Range(0, _settings.PopulationSize / 2)
from child in CreateChildren(
FindRandomHighQualityParent(sumOfMaxMinusFitness, maxFitness),
FindRandomHighQualityParent(sumOfMaxMinusFitness, maxFitness))
select child).
ToArray();
}
}

My 12 proc desktop does about 3800 generations a second in parallel.

Hexacore Super Computer!

Big thanks to Paul for the lovely port of this to SignalR and to Stephen Toub for the algorithm. 

The code for the SignalR monkeys demo is on my BitBucket. Right now it needs .NET 4.5 and the Visual Studio Developer Preview, but you could remove a few lines and get it working on .NET 4, no problem.

Note that that SignalR works on .NET 4 and up and you can play with it today. You can even chat with the developers in the SignalR chat app in the 'aspnet' room at http://chatapp.apphb.com. Just /nick yourself then /join aspnet.

No monkeys were hurt in the writing of this blog post.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Your users don't care if you use Web Sockets

November 09, 2011 Comment on this post [28] Posted in HTML5 | Musings
Sponsored By

I had a lovely time at the Keeping It Realtime Conference this week in Portland. The conference was put on by the lovely folks over at &yet and I'm glad I met them. "KRTConf" was a whole conference dedicated to "real-time" web applications. This largely meant node but also other real-time frameworks including Damien and David's SignalR that Paul Batum (Paul owns Web Sockets for .NET) and I presented. I'll blog our demo soon.

Some folks in the audience wanted to make a real-time app today but really wanted to use Web Sockets today and had concerns about broad support. Coincidentally, I had just visited &yet's new site for product called "&!" or "AndBang," a collaboration tool for teams. However, when I visited the page I got this message: https://andbang.com/you-need-websockets-yo.

Your browser doesn't support Web Sockets, so be sad

Switching protocols from HTTP to WebSocketsIn the near future, all browsers and servers will support Web Sockets, a technique by which a persistent connection is negotiated over HTTP and then a protocol switch happens from http:// to ws://. However, not every browser or server is there yet (IE9, for example) and there's some arguing about versioning, etc. If you don't support Web Sockets, falling back to "Long Polling" is a way to get effectively the same behavior that works everywhere.

Long polling is a technique that lets it look like your browser has a "persistent connection" to the server when in fact you've just got a really "persistent client." By this I mean, rather than a connection that doesn't shut down (persistent) you have a client that will make a call and when it completes, it'll call back. The very definition of persistent, eh? See what I did there?

Persistent: Continuing firmly or obstinately in a course of action in spite of difficulty or opposition.

Long Polling is not as efficient but it works nicely and is a totally reasonable fallback when Web Sockets support is unavailable. The latest preview release of IE10 includes Web Sockets support. But my mom doesn't know about Web Sockets and shouldn't have to.

The 10,000 people on the planet that care about Web Sockets are not your customers, and while using Web Sockets might get you mentioned on TechCrunch, supporting only Web Sockets is a great way to cut your potential audience in half.

You can use the lovely CanIUse web site to find out if you browser supports something you'd like to use. Here's their Web Sockets table.

Table of Browsers and their support for Web Sockets

Your users don't care about Web Sockets. They care about delightful sites. We use JavaScript polyfills when our browsers don't support HTML5 features we want. We use jQuery when JavaScript support is dodgy between browsers. And our web frameworks should gracefully fallback to use Long Polling when Web Sockets isn't available.

Related Links

P.S. Yes, if your app is a real-time trading app or and needs down-to-the-millisecond timing, then sure, maybe you need Web Sockets. But your app isn't that app.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Useful Visual Studio Extension - Web Essentials from Mads Kristensen

November 07, 2011 Comment on this post [62] Posted in ASP.NET | Tools | VS2010
Sponsored By

Visual Studio 2010 is really extensible and that's allowed many folks on the team to try out new features for Web Development without having to rebuild Visual Studio itself. One of those "playground" extensions is called "Web Essentials" by Mads Kristensen. Mads handles HTML5 and CSS3 tools for our team. You might remember Mads from when we released the Web Standards Update a few months back.

Web Essentials is Mads' playground and it's pretty awesome. It's so awesome that I think you should check it out and then, *ahem*,  leave a comment on this post encouraging Mads and gentle urging his boss(es) to get these features into the next version of Visual Studio.

First, it adds a few nice touches to the CSS Editor in Visual Studio. As you can see below, it adds a little glyph next to each color that shows you the actual color.

A nice visual refresh to the Visual Studio CSS editor

If you hover over the colors, you'll get a preview with more details. You'll also get font previews on mouse hover.

Font Previews are fun

These are just nice little touches that make you smile. They also smooth little issues with my daily workflow. After having them, I miss them when they are gone.

There's also some more functional features, like embedding tiny images in your CSS files as base64. Sounds nuts, but if  your small icons are so small that the HTTP Headers are larger than the image itself this can be an easy way to remove an HTTP request.

Here I'm taking the tiny "Ajax Loader" PNG in my app and turning it into an inline image.

Embed Resource as base64

So those

Look at the tiny images as base64

It even has some evil little helpers help you use well-known CSS hacks to control visibility of rules. Like, if you hate yourself and use IE6. ;)

Enabling evil with IE6 hacks

You can minify just a selection of text if it makes you happy.

image

You can drag and drop images and automatically get a background-image CSS rule, which is a HUGE time saver. Oy. All this and a bunch more little subtle stuff.

The best feature and feature that should cause you to download it NOW is that you can do live CSS editing in Visual Studio with a real-time preview.

Fire up a CSS file and hit Ctrl-Alt-Enter. Then click Settings and hit "Live Update CSS." Bam. As you type it'll get updated in the browser. Love this.

image

Do you like these playgrounds? Do you want some or all of these features in Visual Studio? Sound off below.

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Back to Basics: Daylight Savings Time bugs strike again with SetLastModified

November 05, 2011 Comment on this post [32] Posted in ASP.NET | Back to Basics | Learning .NET
Sponsored By

CC BY-NC 2.0 Creative Commons Clock Photo via Flickr ©Thomas Hawk No matter how well you know a topic, or a codebase, it's never to late (or early) to get nailed by a latest bug a over a half-decade old.

DasBlog, the ASP.NET 2 blog engine that powers this blog, is done. It's not dead, but it's done. It's very stable. We had some commits last year, and I committed a bug fix in February, but it's really well understood and very baked. My blog hasn't been down for traffic spike reasons in literally years as DasBlog scales nicely on a single machine.

It was 10:51pm PDT (that's Pacific Daylight Time) and I was writing a blog post about the clocks in my house, given that PST (that's Pacific Standard Time) was switching over soon. I wrote it up in Windows Live Writer, posted it to my blog, then hit Hanselman.com to check it out.

Bam. 404.

What? 404? Nonsense. Refresh.

404.

*heart in chest* Have I been hacked? What's going on? OK, to the logs!

l2    time    2011-11-06T05:36:31    code    1    message    Error:System.ArgumentOutOfRangeException: Specified argument was out of the range of valid values.
Parameter name: utcDate
at System.Web.HttpCacnhePolicy.UtcSetLastModified(DateTime utcDate)
at System.Web.HttpCachePolicy.SetLastModified(DateTime date)
at newtelligence.DasBlog.Web.Core.SiteUtilities.GetStatusNotModified(DateTime latest) in C:\dev\DasBlog\source\newtelligence.DasBlog.Web.Core\SiteUtilities.cs:line 1253
at newtelligence.DasBlog.Web.Core.SharedBasePage.NotModified(EntryCollection entryCollection) in C:\dev\DasBlog\source\newtelligence.DasBlog.Web.Core\SharedBasePage.cs:line 1182
at newtelligence.DasBlog.Web.Core.SharedBasePage.Page_Load(Object sender, EventArgs e) in C:\dev\DasBlog\source\newtelligence.DasBlog.Web.Core\SharedBasePage.cs:line 1213
at System.EventHandler.Invoke(Object sender, EventArgs e)
at System.Web.UI.Control.OnLoad(EventArgs e)
at System.Web.UI.Control.LoadRecursive()
at System.Web.UI.Page.ProcessRequestMain(Boolean includeStagesBeforeAsyncPoint, Boolean includeStagesAfterAsyncPoint) while processing http://www.hanselman.com/blog/default.aspx.

What's going on? Out of range? What's out of range. Ok, my site is down for the first time in years. I must have messed it up with my clock post. I'll delete that. OK, delete. Whew.

Refresh.

404.

WHAT?!?

Logs, same error, now the file is a meg and growing, as this messages is happening of hundreds of times a minute. OK, to the code!

UtcSetLastModified is used for setting cache-specific HTTP headers and for controlling the ASP.NET page output cache. It lets me tell HTTP that something hasn't been modified since a certain time. I've got a utility that figures out which post was the last modified or most recently had comments modified, then I tell the home page, then the browser, so everyone can decide if there is fresh content or not.

public DateTime GetLatestModifedEntryDateTime(IBlogDataService dataService, EntryCollection entries)
{
//figure out if send a 304 Not Modified or not...
return latest //the lastTime anything interesting happened.
}

In the BasePage we ask ourselves, can we avoid work and give a 304?

//Can we get away with an "if-not-modified" header?
if (SiteUtilities.GetStatusNotModified(SiteUtilities.GetLatestModifedEntryDateTime(dataService, entryCollection)))
{
//snip
}

However, note that I'm have to call SetLastModified though. Seems that UtcSetLastModified is private. (Why?) When I call SetLastModified it does this:

public void SetLastModified(DateTime date)
{
DateTime utcDate = DateTimeUtil.ConvertToUniversalTime(date);
this.UtcSetLastModified(utcDate);
}

Um, OK. Lame. So that means I have to work in local time. I retrieve dates and convert them ToLocalTime().

At this point, you might say, Oh, I get it, he's called ToLocalTime() too many times and double converted his times. That's what I thought. However, after .NET 2 that is possible.

The value returned by the conversion is a DateTime whose Kind property always returns Local. Consequently, a valid result is returned even if ToLocalTime is applied repeatedly to the same DateTime.

But. We originally wrote DasBlog in .NET 1.1 first and MOVED it to .NET 2 some years later. I suspect that I'm actually counting on some incorrect behavior deep in own our (Clemens Vasters and mine) TimeZone and Data Access code that worked with that latent incorrect behavior (overconverting DateTimes to local time) and now that's not happening. And hasn't been happening for four years.

Hopefully you can see where this is going.

It seems a comment came in around 5:36am GMT or 10:36pm PDT which is 1:36am EST. That become the new Last Modified Date. At some point we an hour was added in conversion as PDT wasn't PST yet but EDT was EST.

Your brain exploded yet? Hate Daylight Saving Time? Ya, me too.

Anyway, that DateTime became 2:36am EST rather than 1:36am. Problem is, 2:36am EST is/was the future as 6:46 GMT hadn't happened yet.

A sloppy 5 year old bug that has been happening for an hour each year that was likely always there but counted on 10 year old framework code that was fixed 7 years ago. Got Unit Tests for DST? I don't.

My server is in the future, but actually not as far in the future as it usually is. My server in on the East Coast and it was 1:51am. However, the reasons my posts sometimes look like they are from the future, is I store everything in the neutral UTC/GMT zone, so it was 5:51am the next day on my file system.

Moral of the story?

I need to confirm that my server is on GMT time and that none of my storage code is affected my Daylight Saving Time.

Phrased differently, don't use DateTime.Now for ANY date calculations or to store anything. Use DateTime.UTCNow and be aware that some methods will freak out if you send them future dates, as they should. Avoid doing ANYTHING in local time until that last second when you show the DateTime to the user.

In my case, in the nine minutes it took to debug this, it resolved itself. The future became the present and the future last modified DateTime became valid. Is there a bug? There sure it, at least, there is for an hour, once a year. Now the real question is, do I fix it and possibly break something that works the other 8759 hours in a year. Hm, that IS still four 9's of uptime. (Ya, I know I need to fix it.)

"My code has no bugs, it runs exactly as it was written." - Some famous programmer

Until next year. ;)

About Scott

Scott Hanselman is a former professor, former Chief Architect in finance, now speaker, consultant, father, diabetic, and Microsoft employee. He is a failed stand-up comic, a cornrower, and a book author.

facebook bluesky subscribe
About   Newsletter
Hosting By
Hosted on Linux using .NET in an Azure App Service

Disclaimer: The opinions expressed herein are my own personal opinions and do not represent my employer's view in any way.