Friday, November 15, 2013

Creating XAML hexagons without using a PATH element

I was recently working on a small Windows Phone 8 app that needed some tessellated (tiled) hexagons and after searching the web for a simple solution, I did not find what I wanted. 

All of the solutions involved drawing the hexagon using drawing primitives such as the PATH element.  I wanted a “simpler” solution.  I wanted a Grid based solution, and here it is.

To start with I needed to know two semi-basic points about hexagons: what is the ratio of height to width, and (with the hex “standing” on one of its flat sides) what is the the ratio between the horizontal length of the flat and the horizontal distance from the end of the flat side to the edge of the hexagon.  (Boy that is harder to write than it is to just see it)

I found both pieces of information at: Hexnet

Hexnet shows that if the length of one side of a hex has a length of 1, then the overall width is 2 and the height of the hexagon is √3 (approx. 1.73).  So, if we were to build this in a grid, we would have a single hexagon take up 6 cells (2 rows and 3 columns) and our two critical semi-basic points are: √3 to 2 and 1 to .5.

If we wanted a hexagon with an overall width of 200px (200 times our example above), our grid would be defined as:

   1:  <Grid x:Name="LayoutRoot" Background="White" Width="200" Height="173">
   2:      <Grid.ColumnDefinitions>
   3:          <ColumnDefinition Width="*"></ColumnDefinition>
   4:          <ColumnDefinition Width="2*"></ColumnDefinition>
   5:          <ColumnDefinition Width="*"></ColumnDefinition>
   6:      </Grid.ColumnDefinitions>
   7:      <Grid.RowDefinitions>
   8:          <RowDefinition></RowDefinition>
   9:          <RowDefinition></RowDefinition>
  10:      </Grid.RowDefinitions>
  11:  </Grid>



if you look at this grid with gridlines on, you get:


image



Now from here we can either create a outline of a hexagon using 6 line objects, create a solid hexagon using 3 rectangle objects or combine both for an outlined solid color hexagon.


“Wait!”, you say.  “I can see the six lines making up the outline of a hexagon, but how could you do a solid color hexagon with three squares?”


Well, I’m glad you asked.  If you fill the center column with a rectangle, that’s one.  If you make a copy but rotate it 60 degrees using the center as the pivot point and make a third and rotate it –60 degrees then you have your solid color hexagon.


And, if you just start repeating the second and third columns when you add columns, you can start to create a grid full of tessellated (tiled) hexagons, just like I did in this image.


image


And here is the complete xaml I used to create the above image (From this code it was simple to extract rules that I used to create large fields of tessellated (tiled) hexagons in code):


   1:  <UserControl x:Class="Hexagon.MainPage"
   2:      xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
   3:      xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
   4:      xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
   5:      xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
   6:      mc:Ignorable="d"
   7:      d:DesignHeight="346" d:DesignWidth="500">
   8:      <Viewbox Stretch="Uniform">
   9:          <Grid x:Name="LayoutRoot" Background="White" Width="500" Height="346" Margin="50">
  10:              <Grid.ColumnDefinitions>
  11:                  <ColumnDefinition Width="*"></ColumnDefinition>
  12:                  <ColumnDefinition Width="2*"></ColumnDefinition>
  13:                  <ColumnDefinition Width="*"></ColumnDefinition>
  14:                  <ColumnDefinition Width="2*"></ColumnDefinition>
  15:                  <ColumnDefinition Width="*"></ColumnDefinition>
  16:                  <ColumnDefinition Width="2*"></ColumnDefinition>
  17:                  <ColumnDefinition Width="*"></ColumnDefinition>
  18:              </Grid.ColumnDefinitions>
  19:              <Grid.RowDefinitions>
  20:                  <RowDefinition></RowDefinition>
  21:                  <RowDefinition></RowDefinition>
  22:                  <RowDefinition></RowDefinition>
  23:                  <RowDefinition></RowDefinition>
  24:              </Grid.RowDefinitions>
  25:              <Rectangle Fill="Pink" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.RowSpan="2"></Rectangle>
  26:              <Rectangle Fill="Pink" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.RowSpan="2">
  27:                  <Rectangle.RenderTransform>
  28:                      <RotateTransform Angle="-60" />
  29:                  </Rectangle.RenderTransform>
  30:              </Rectangle>
  31:              <Rectangle Fill="Pink" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.RowSpan="2">
  32:                  <Rectangle.RenderTransform>
  33:                      <RotateTransform Angle="60" />
  34:                  </Rectangle.RenderTransform>
  35:              </Rectangle>
  36:              <Rectangle Fill="Aquamarine" RenderTransformOrigin="0.5, 0.5" Grid.Column="3" Grid.Row="1" Grid.RowSpan="2"></Rectangle>
  37:              <Rectangle Fill="Aquamarine" RenderTransformOrigin="0.5, 0.5" Grid.Column="3" Grid.Row="1" Grid.RowSpan="2">
  38:                  <Rectangle.RenderTransform>
  39:                      <RotateTransform Angle="-60" />
  40:                  </Rectangle.RenderTransform>
  41:              </Rectangle>
  42:              <Rectangle Fill="Aquamarine" RenderTransformOrigin="0.5, 0.5" Grid.Column="3" Grid.Row="1" Grid.RowSpan="2">
  43:                  <Rectangle.RenderTransform>
  44:                      <RotateTransform Angle="60" />
  45:                  </Rectangle.RenderTransform>
  46:              </Rectangle>
  47:              <Rectangle Fill="LightBlue" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="2" Grid.RowSpan="2"></Rectangle>
  48:              <Rectangle Fill="LightBlue" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="2" Grid.RowSpan="2">
  49:                  <Rectangle.RenderTransform>
  50:                      <RotateTransform Angle="-60" />
  51:                  </Rectangle.RenderTransform>
  52:              </Rectangle>
  53:              <Rectangle Fill="LightBlue" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="2" Grid.RowSpan="2">
  54:                  <Rectangle.RenderTransform>
  55:                      <RotateTransform Angle="60" />
  56:                  </Rectangle.RenderTransform>
  57:              </Rectangle>
  58:              <Rectangle Fill="LemonChiffon" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.Row="2" Grid.RowSpan="2"></Rectangle>
  59:              <Rectangle Fill="LemonChiffon" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.Row="2" Grid.RowSpan="2">
  60:                  <Rectangle.RenderTransform>
  61:                      <RotateTransform Angle="-60" />
  62:                  </Rectangle.RenderTransform>
  63:              </Rectangle>
  64:              <Rectangle Fill="LemonChiffon" RenderTransformOrigin="0.5, 0.5" Grid.Column="1" Grid.Row="2" Grid.RowSpan="2">
  65:                  <Rectangle.RenderTransform>
  66:                      <RotateTransform Angle="60" />
  67:                  </Rectangle.RenderTransform>
  68:              </Rectangle>
  69:              <Rectangle Fill="Thistle" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="0" Grid.RowSpan="2"></Rectangle>
  70:              <Rectangle Fill="Thistle" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="0" Grid.RowSpan="2">
  71:                  <Rectangle.RenderTransform>
  72:                      <RotateTransform Angle="-60" />
  73:                  </Rectangle.RenderTransform>
  74:              </Rectangle>
  75:              <Rectangle Fill="Thistle" RenderTransformOrigin="0.5, 0.5" Grid.Column="5" Grid.Row="0" Grid.RowSpan="2">
  76:                  <Rectangle.RenderTransform>
  77:                      <RotateTransform Angle="60" />
  78:                  </Rectangle.RenderTransform>
  79:              </Rectangle>
  80:              <Line Grid.Column="0" Grid.Row="0" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  81:              <Line Grid.Column="1" Grid.Row="0" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Top" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  82:              <Line Grid.Column="2" Grid.Row="0" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  83:              <Line Grid.Column="0" Grid.Row="1" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  84:              <Line Grid.Column="1" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Bottom" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  85:              <Line Grid.Column="2" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  86:              <Line Grid.Column="3" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Top" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  87:              <Line Grid.Column="4" Grid.Row="0" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  88:              <Line Grid.Column="5" Grid.Row="0" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Top" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  89:              <Line Grid.Column="6" Grid.Row="0" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  90:              <Line Grid.Column="4" Grid.Row="1" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  91:              <Line Grid.Column="5" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Bottom" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  92:              <Line Grid.Column="6" Grid.Row="1" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  93:              <Line Grid.Column="0" Grid.Row="2" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  94:              <Line Grid.Column="2" Grid.Row="2" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  95:              <Line Grid.Column="0" Grid.Row="3" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  96:              <Line Grid.Column="1" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Bottom" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  97:              <Line Grid.Column="2" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  98:              <Line Grid.Column="3" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Top" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
  99:              <Line Grid.Column="4" Grid.Row="2" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 100:              <Line Grid.Column="6" Grid.Row="2" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 101:              <Line Grid.Column="4" Grid.Row="3" X1="1" Y1="1" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 102:              <Line Grid.Column="5" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="0" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" VerticalAlignment="Bottom" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 103:              <Line Grid.Column="6" Grid.Row="3" X1="1" Y1="0" X2="0" Y2="1" Margin="-5 -5" StrokeThickness="10" Stretch="Fill" Stroke="Black" StrokeEndLineCap="Round" StrokeStartLineCap="Round"></Line>
 104:          </Grid>
 105:      </Viewbox>
 106:  </UserControl>

No comments: