Monday, May 03, 2004

At the beginning of the year I started an initiative with my current employer which I call Chalktalks - a name I blatently and unabashedly stole from my previous employer, Microsoft.  This is a special biweekly meeting that occurs at 7:00 AM each Monday.  To this meeting we, the various consultants, gather to share insights, knowledge, and experiences with other team members in an effort to bolster our technical abilities and broaden our horizons.

The chalktalks that we conducted at Microsoft targeted the Rocky Mountain District (comprised of Denver and Salt Lake City) and were held weekly on Fridays at 7:00 AM.  When I joined my current company I noticed a marked difference in the team atmosphere and camaraderie.  For the most part people have been unwilling to participate in team-building activities and generally have a bad attitude towards the company pent up from years passed - an attitude with which I don't deal well.

It has been a goal of mine since the beginning to help encourage team members and bolster the atmosphere here.  To this end (and with the approval of management) I started two initiatives that have been moderately successful: the chalktalks as well a a team-meeting-only 5-10 minutes 'Tips of the Trade' or (TOTT).

Although we've had so far around 9 - 10 separate chalktalks, only two individuals have actually gotten up and presented a topic to the group (usually consisting of 3-5 people of a possible 35+): myself and Scott Golightly (a coworker and our local MS Regional Director).  Not to say that I expected otherwise.  I am not really disappointed...it gives me the opportunity to plan topics with which I'm not very familiar and give a presentation on it, expanding my horizons.  The way I look at it is that they are missing out on expanding their repertoire.

Notwithstanding, I am happy to say that we've not yet missed out on a single week and have had such delightful topics as Asynchronous Processing, SharePoint Portal Server, Code Access Security, GDI+, HttpHandlers, SQL Server Tips and Tricks, et al.  It does require work above and beyond our normal responsibilities (which I think is the primary factor in others not participating) but the effort is well worth it.  I just wish the guys could see that and actually do something of their own initiative and volition toward self improvement, rather than relying on being compensated or feeling that the company owes them.

Monday, May 03, 2004 2:21:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, May 02, 2004

I ran into this while working with a client over the weekend.  My client is using VS.NET 2002 (with the .NET Framework 1.0) and is creating an in-house, intranet application to be used simultaneously by 35-50 users...ok that part doesn't matter.  Basically what it came down to is they are in the process of performing some personnel changes and wanted to ensure that prior to taking action that they had all of the source code and could continue on making changes and improvements to their software.

First of all, I'd copy the files (mostly .aspx pages) locally to my machine simply to see if I could compile the project from within Visual Studio.  Everything loaded fine except the web project.  It failed with an error 'Unable to get the project file from the web server'.  This was disconcerting.  I know I'd seen the error before (about 15 months ago) and had solved the problem but with my client looking over my shoulder the heat was on!  I had to get this to work.  I double checked all of my settings - everything seemed in order.  Interestingly I didn't get this problem with any of my web applications.

Then my memory started to function again...I recalled that VS maintains a folder called VSWebCache found in <root>\Documents and Settings\<user>\VSWebCache\<machinename>.  All I had to do was delete all of the contents of this folder and voila! it worked - I could open the project!  It turns out that VS.NET creates and uses this directory in order to facilitate offline development and as a working directory.

Simple, but stupid, solution.

Sunday, May 02, 2004 4:51:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [11]  |  Trackback
 Tuesday, April 27, 2004
Chris Brumme is quite illuminating on his finalization post.  An excellent discussion and in depth.  Therein, Chris educates us on how finalization really works and what it's all about.  A must read!
Tuesday, April 27, 2004 2:48:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Saturday, April 24, 2004

John Topley hits the nail on the head with this article about Microsoft and the world's negative view of the software giant.  This is just a copy and paste of my comments to his post:

I've had the privilege of being on the inside of MS as a consultant and on the outside as an observer. I won't reiterate your points as you make them very well and I couldn't state them better, but I'm sick and tired of the constant MS bashing and general disdain in the computing world. It's personally hurtful at times - especially when it comes from coworkers or family members. Most of the loathing and bad-mouthing is completely unfounded and without any real understanding of what goes on or why. Having been on the inside I caught a first-hand view of the energy and passion. It is awesome! People claim that MS isn't innovative or they simply steal other company's ideas (or take the companies themselves). That's just a load of bull. The folks at MS have an astounding desire to get things done and get them done well. This leads to amazing innovation. Well I won't rant on, but thank you for putting into words my exact feelings. :)

Excellent points, John!  Keep up the good work.

Saturday, April 24, 2004 12:58:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [1]  |  Trackback
 Friday, April 23, 2004

I'm a big fan of application optimizations (who as a developer isn't?).  There's a cool technique that many an ASP.NET programmer doesn't employ (but should) that can increase the performance of a web application.  The vast majority of pages that a web server will return is HTML-based (e.g. via .aspx or .ascx pages).  However, the time may arise in which you want to return straight XML or some binary data such as a JPEG or GIF, perhaps even a WAV file or something of that nature.

Well, it's pretty easy to write the following in an .aspx page:

private void Page_Load(object sender, EventArgs e) {
   Response.Clear();
   Response.ContentType = “text/xml“;
   // output your xml here
}

This works pretty well.  The client makes a request to the server and gets the xml back.  Simple.

But...every .aspx page is (by default) processed by the PageHandlerFactory class.  This class has a lot of code (and overhead) to handle page requests.  If all you're wanting to do is return XML or an image for example, you can create your own handler for the request.  You do this in one of two ways:  1) implement the IHttpHandler interface or 2) create an .ashx page.

If you implement the IHttpHandler interface you'll have to do a little bit of wireup code in your web.config file to associate a client request with your class, for example:

<add verb=“*” path=“*.ext” type=“NS.MyHandler, MyHandlerAssembly” />

I'm being simplistic here and not going into all of the details, of course, but that's the gist.  Note, you'll also need to go into IIS and map the extension to the aspnet_isapi.dll ISAPI extension.  This is a powerful concept!  The requested file doesn't even need to exist!  IIS (or rather, the ISAPI) will intercept the call to the file (extant or not) and route the request to ASP.NET which in turn takes the request and your IHttpHandler implementation will do the processing.

The second approach, may be a bit more cumpersome on the dev-side but simpler on the admin side.  All you do is create a file with the .ashx extension.  This extension is already mapped in IIS to the aspnet_isapi.dll ISAPI and will automatically be picked up by ASP.NET and processed.

A disadvantage of using the .ashx approach is there is no intellisense/color-coding within VS.NET 2003 and prior (haven't yet tested on Whidbey - but I'll give it a try and report back).  As a point of clarification, there is no intellisense nor color coding when editing your source directly within the .ashx file.  However, if you opt to use code behind, you will have full IDE support.  Also, you add the <% @ WebHandler %>directive to the top of your document.

<%@ WebHandler language="C#" class="classname" %><%@ WebHandler language="C#" class="classname" %>

I found little to no documentation on this handler directive but it has been mentioned in a few MSDN articles and what not.

Regardless of the route you take, all you then need to do is provide implementation for two methods on the IHttpHandler interface:

bool IHttpHandler.IsReusable {
  get { return true; }
}
 
void IHttpHandler.ProcessRequest(HttpContext context) {
}

Then you're off to the races!

This is a powerful technology that was really only previously existed for C++ ISAPI developers and is now at our fingertips.

 

Friday, April 23, 2004 6:17:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback

Well, here I am in sunny, beautiful Santa Barbara, California.  Coming from the cold, windy weather in Salt Lake City, Utah to this absolutely gorgeous weather is a welcome change.  I periodically travel to SB to write code with a good friend of mine.  We're working on a variety of projects, but the main one is an online (web) product configurator.  The tool is very powerful, if I do say so myself and we're excited about the progress.

Looking forward to a productive weekend of “splab app whapping.

Friday, April 23, 2004 5:52:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, April 21, 2004

Hey, this is cool!

Microsoft is making available (for free!) the same C++ compiler that ships with Visual Studio .NET 2003 Pro.  Now you can write apps that target the Windows platform and the CLR using the Visual C++ language.

Wednesday, April 21, 2004 3:38:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Sunday, April 18, 2004

As a family we have a hard time getting together each night to read scriptures.  This isn't to say that we don't remember to, it's the doing it that is the hard part.  It's great that the girls are so enthusiastic and desirous to read scriptures and hold Family Home Evening.

Tonight we sat down and started the book of Jacob in the Book of Mormon.  Anyah is such a great reader!  Leiel is doing a great job too of wanting to read and having Mommy or Daddy help.  After reading the first chapter we played a family game of Settlers of Catan (the girls love it!).  It's a good object lesson for them - especially Anyah who has a very hard time not winning all the time.  We're working on that.  Lisa ended up winning.  Anyah and Leiel were tied for second place.  I could see Anyah starting to pout when she discovered she lost but I quickly diverted her attention by having everyone give mom a high-five and that seemed to offer some good levity and she realized that not winning wasn't so bad.

I was very impressed with her and how she handled having the robber placed on her resource hexes - she laughed it off and played along.  Leiel also had a great time moving the robber when she rolled a 7.

Following the family game, we ate chocolate pudding - Addie was gobbling it up voraciously!  She sure is a cutie!

Well, I have to go to bed to get up early tomorrow!

Sunday, April 18, 2004 4:47:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback

In writing my Settlers of Catan computer version, I came upon the following conundrum: how do I perform coordinate hit tests on a hexagonal grid?  Dealing with cartesian, square grid is very simple and well understood.  A hex grid, in reality is not very much more complex, but does throw a few curve balls (i.e. there are angles to consider, cells overlap, etc).  I did a little online research and my search wasn't too fruitful, but what I did find confirmed some ideas I already had which I'll outline here.

A few points of interest with respect to hex grids:

  • There are two ways of rendering the grid (flat-side up and point up).  Each of these presents a different algorithm and a solution to the point hit test problem.
  • If the flat-side-up orientation is chosen, you will actually end up with fewer hexes visible on the screen at a time.

Here is the scenario I ended up with:

  • I chose the point-up orientation of the hexes.
  • Each successive column/row of hexes overlaps by one pixel on its neighboring hex to provide a seamless edge - this greatly simplified the creating of hexagonal tiles.
  • My hex-grid (map) is rendered graphically offset from the upper-left corner of the image so we must take that offset into consideration during our hit test.

In solving the problem, I made the following assumptions and assertions on my code:

  • My hex tiles are a fixed size: 153 x 177 pixels
  • The origin (0, 0) of the map is offset to the upper right of (1, 0)
  • In order to provide graphical symmetry in my maps, every other row starting with 0 (continuing with 2, 4, 6, etc) has one fewer column than the odd rows (1, 3, 5, etc).
  • We have to take the 'missing' column as well as points that fall off of the grid into consideration while performing our hit test or we might return invalid coordinates.

Now to the solution:

In order to perform the hit test, I decided to treat the entire map in a traditional, grid-based manner, dissecting the map into a grid where each square was 153 x 133 (or MAPTILE_WIDTH x MAPTILE_HEIGHT * .75).  Each square then contains points that belong to up to three hexes.

If my y coordinate falls on an 'odd' row, then the lower 66% of the square belongs to two adjacent hexes.  The upper 1/3rd belongs to three hexes.  If the y coordinate falls on an 'even' row, the lower 2/3rds of the square belong entirely to a hex and the upper 1/3rd belongs to two other hexes as per the diagram.

The other issue to resolve is that of calculating points in relation to a given slope (very easy using the point-slope equations that I learned back in junior high-school).  If you observe the code you'll see that I cheat a little...I preresolve the slopes (since they are constant and I don't want to have to reevaluate a known value repetitively).

I'm not going to give a play-by-play of my code, but if you have any questions, feel free to let me know; I've commented that which I felt was important to comment and have included it below.

Just a few points of interest about my code because it does target my game and isn't entirely generic, though breaking it out to be completely generic would be a no-brainer, I don't have the immediate need to do so:

  • Map.Current is a singleton instance of a Map object (defined elsewhere) that represents the map in question.
  • Several of the constants that I use in the code are defined in a Constants class.
  • InvalidPoint is a point object (defined as private readonly Point InvalidPoint = new Point(-1, -1)) and represents any coordinate position on the map that doesn't correlate to a coordinate on the grid.

Undoubtedly, there are better algorithms for detecting hit tests against a hexagon and I'd love to see them...this merely represents a first-go at the problem and I think it addresses it well.  This function is intended to return a Point object containing the map x-y coordinate pair as resolved against a pixel x-y coordinate pair (e.g. from the current mouse position).

Ok, here's the code:

private Point getHex(Point pt) {
  
if ( null == Map.Current ) return InvalidPoint;

  
// precalculate the 'hotspot' regions
  
const float MAPTILE_HEIGHT_HOT = Constants.MAPTILE_HEIGHT * .75F;
  
const float MAPTILE_UPPERTHIRD = MAPTILE_HEIGHT_HOT * .33F;
  
const float ANGLE = 0.589F;
  
const float ANGLE2 = 1.8095F;
  
const int MAPTILE_LEFTHALF = Constants.MAPTILE_WIDTH >> 1;

  
// subdivide the map up into squares.
  
// each square contains 1/2 of two opposing hexes horizontally and a portion
   // of one or two hexes vertically
  
// for ease of calculation, offset the point by the map margins
  
pt.Offset(-MAP_MARGIN, -MAP_MARGIN);

  
int row = (int)( (pt.Y + ( pt.Y / MAPTILE_HEIGHT_HOT )) / MAPTILE_HEIGHT_HOT );
  
int col = (int)( (pt.X + ( pt.X / Constants.MAPTILE_WIDTH )) / Constants.MAPTILE_WIDTH );

  
// convert the point (mouse coord) to coordinates relative to the current square
  
pt.X -= ( col * Constants.MAPTILE_WIDTH ) - col;
  
pt.Y -= (int)( row * MAPTILE_HEIGHT_HOT ) - row;

  
if ( pt.Y < MAPTILE_UPPERTHIRD ) {
     
if ( row % 2 == 0 ) {
        
if ( pt.X < MAPTILE_LEFTHALF && pt.X * ANGLE > pt.Y )
           
row--;
        
else if ( pt.X >= MAPTILE_LEFTHALF && ( pt.X - MAPTILE_LEFTHALF ) / ANGLE2 < ( MAPTILE_UPPERTHIRD - pt.Y ) )
           
row--;
        
else if ( pt.X < MAPTILE_LEFTHALF )
           
col--;
     
}
     
else {
        
if ( pt.X >= MAPTILE_LEFTHALF && ( pt.X - MAPTILE_LEFTHALF ) * ANGLE > pt.Y )
           
row--;
        
else if ( pt.X < MAPTILE_LEFTHALF && ( pt.X / ANGLE2 < ( MAPTILE_UPPERTHIRD - pt.Y ) ) ) {
           
row--;
           
col--;
        
}
     
}
   }
  
else if ( row % 2 == 0 && pt.X < MAPTILE_LEFTHALF ) {
     
// if the point of interest is within the lower 2/3 of the hex, then there are
      // two possibilities - even rows (0, 2, 4, ...) contain 
two hexes, whereas odd
      //
rows (1, 3, 5, ...) have only one hex.
     
col--;
  
}

  
if ( col < 0 || row < 0 || row >= Map.Current.Rows || col >= ( ( row % 2 == 0 ) ? Map.Current.Columns - 1 : Map.Current.Columns ) || pt.X < 0 )
     
return InvalidPoint;
  
else
     
return new Point(col, row);
}

Sunday, April 18, 2004 11:26:00 AM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [4]  |  Trackback
 Thursday, April 15, 2004

Well, I had the opportunity to attend a Microsoft MSDN event today entitled “Writing Secure Code - Threat Defense”.  I went in with high hopes.  I must of course say that I wasn't entirely disappointed, but my expectations were not met.

Years ago (in a past life) I used to present at Tech Net and MSDN events (I'm starting to get back into it too - but that's another blog) so I have some insight into what a presenter goes through.

All in all, he did a fairly okay job but left out many ideas and concepts.  Ultimately I came away with a few things:

Valuable Resource: O'Reilly's book '.NET Security'

Perhaps the best, most useful information gained was concerning Code Access Security.  CAS is one of the most misunderstood aspects of .NET development and the presenter shed some light on the topic.  He illustrated the use of the .Demand() and .Assert() methods to control the stack walk as well as how to configure (briefly) an application to take advantage of CAS...that part was worth while.

 

Thursday, April 15, 2004 2:58:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback
 Wednesday, April 14, 2004

Hey, it finally feels like I made some progress!  I have never taken the time to really sit down and use the Xml objects built into the .NET framework.  It sounds asinine, I know, but honestly I have never had/found a need to use them.  I've used the DomDocument before in very limited ways in VB and Javascript, but never had broken down to use the .NET stuff.

Well, long story short, I got the ScenarioEditor utility to save and load my xml scenario files.  Plus they get validated on load against my schema file!  Hey, this is progress for me.  Previously, I would have saved the data to a binary file with my own proprietary data structure.  That would have been way to much for what I wanted to accomplish for such a small utility that didn't need such functionality.  I wanted scenarios to be creatable in tools such as Notepad.

I have yet to profile the code and go back over a few loose ends, but the utility is now in a functional state and ready to save scenarios for the game - that is goodness!  I'll get some screenshots posted shortly.

Wednesday, April 14, 2004 6:30:00 PM (Mountain Standard Time, UTC-07:00)  #    Disclaimer  |  Comments [0]  |  Trackback