Turkwise
Turkwise

Reputation: 147

Calculate Size of Hexagons to Fit Inside Rectangle

I will need to put a dynamic number of hexagons (28 in this example) inside a "rectangle" aka user's screen size (2000px width, 1000px height in this example). I wish to offset every other column. There will be a percentage margin.

I attempted to create a custom panel, my code is below:

Protected Overrides Function MeasureOverride(ByVal availableSize As Size) As Size
    On Error Resume Next

    Dim side As Double = ((((Me.ActualHeight * 2) + (Me.ActualWidth * 2)) / 6) - Me.InternalChildren.Count) / 6
    Dim w As Double = 2 * side
    Dim h As Double = side * Math.Sqrt(3.0)

    For Each child As FrameworkElement In InternalChildren
        Dim childMaxSize = New Size(w, h)
        child.Measure(childMaxSize)
    Next

    Return availableSize
End Function

Protected Overrides Function ArrangeOverride(ByVal finalSize As Size) As Size
    On Error Resume Next

    Dim side As Double = ((((Me.ActualHeight * 2) + (Me.ActualWidth * 2)) / 6) - Me.InternalChildren.Count) / 6
    Dim w As Double = 2 * side
    Dim h As Double = side * Math.Sqrt(3.0)

    Dim x As Double = 0
    Dim y As Double = 0
    Dim shift As Boolean = True
    Dim shiftOffset As Double

    Dim offsetChild As FrameworkElement = TryCast(InternalChildren(0), FrameworkElement)
    shiftOffset = h / 2

    For i As Integer = 0 To InternalChildren.Count - 1 'For Each child As FrameworkElement In Me.InternalChildren
        Dim child As FrameworkElement = TryCast(InternalChildren(i), FrameworkElement)
        child.Margin = New Thickness(side * 0.02)

        If child IsNot Nothing Then
            Dim finalY As Double = y

            If shift Then finalY += shiftOffset

            shift = Not shift
            child.Arrange(New Rect(New Point(x, finalY), New Size(w, h)))

            x += w * 0.75
            Dim nextWidth As Double = 0

            If i + 1 < InternalChildren.Count Then nextWidth = w

            If x + nextWidth > Me.ActualWidth Then
                shift = True
                x = 0
                y += h
            End If
        End If
    Next

    Return finalSize
End Function
End Class

My issue is trying to calculate the side. I am close, but it doesn't seem to fit quite right in the "rectangle", or perhaps there is something else I'm not getting.

Any help is appreciated - thanks in advance!

EDIT: To explain a little differently - my formula works if I don't offset every other hexagon column (tested). I'm pretty sure my root issue is that my code is measuring the size each hexagon needs to be in order to fit in the specified rectangle before placing them. However, after they are placed and then offset, I'm finding the hexagons appearing out of the rectangle. Oh also my hexagons are flat-topped. Thanks! Pictures are below - top is what I need, bottom is what I'm getting. The red is the area the hexagons need to fit in:

want have

EDIT 2: I attempted MBo's suggestion of side = side - offset (my offset is height * 0.5). It appears to make the hexagons too small:

Protected Overrides Function MeasureOverride(ByVal availableSize As Size) As Size
    On Error Resume Next

    Dim side As Double = ((((Me.ActualHeight * 2) + (Me.ActualWidth * 2)) / 6) - Me.InternalChildren.Count) / 6
    Dim h As Double = side * Math.Sqrt(3.0)

    'new
    side = side - (h * 0.5)
    h = side * Math.Sqrt(3.0)

    Dim w As Double = 2 * side

    For Each child As FrameworkElement In InternalChildren
        Dim childMaxSize = New Size(w, h)
        child.Measure(childMaxSize)
    Next

    Return availableSize
End Function

Protected Overrides Function ArrangeOverride(ByVal finalSize As Size) As Size
    On Error Resume Next

    Dim side As Double = ((((Me.ActualHeight * 2) + (Me.ActualWidth * 2)) / 6) - Me.InternalChildren.Count) / 6
    Dim h As Double = side * Math.Sqrt(3.0)

    'new
    side = side - (h * 0.5)
    h = side * Math.Sqrt(3.0)

    Dim w As Double = 2 * side

Upvotes: 1

Views: 2870

Answers (1)

MBo
MBo

Reputation: 80287

Consider bounding box size for dense hexagonal grid with R rows, C columns, vertical hexagons orientation (pointy topped, two vertical edges), edge size A: enter image description here

Height = A/2 * (3 * R + 1)

Width depends on configuration:

Odd and even rows with the same number C of hexagons (N=R*C):

Width = A * Sqrt(3)/2 * (2 * C + 1)
E E E E
 O O O O
E E E E
 O O O O

Odd rows are shorter with C-1 hexagons (also R=1 case) (N~R*(C-1/2)):

Width = A * Sqrt(3) * C
E E E E
 O O O
E E E E

Could you apply these formulas to fit needed number N of hexes into predefined box WxH?

You probably need also to consider some factorisations of N (or slightly larger number) into R, C factors to minimize empty space (for example, 38=2x19 is not suitable for near-square regions, but 40=5x8 or 42=6x7 could be better)

Edit
For your configuration - flat-topped, first column is lower than second:

BoundWidth = Side/2 * (3 * C + 1)
BoundHeight = Side * Sqrt(3)/2 * (2 + R) = Height * (R/2 + 1)

Upvotes: 2

Related Questions