Truly Hexagonal Cells Example
Do you have questions or comments about this model? Ask them here! (You'll first need to log in.)
WHAT IS IT?
This demonstrates how to make a model that uses an undistorted hexagonal grid of cells.
HOW IT WORKS
To achieve an undistorted hexagonal grid, the grid is not based on patches the same height as the cells (as in Uri Wilensky's "Hex Cells Example") because such a grid must be squashed vertically by a factor of 0.866 . Instead we use pixel patches that are pixels (with patch size = 1
) and create truly hexagonal cells (a breed of turtles) to form the grid.
setup
resizes the World (according to the user parameters) and creates the hex grid. The user parameters are constained so that the hex grid wraps exactly, and the World origin is set to the bottom-left corner as there must be an even number of patches on each axis.
After setup
, cells are used instead of patches (with the sole exception of inside the xNeighbor-reporter
). For example, selected-cell-reporter
reports the cell under the mouse, with no reference to patches. In order to conform with this avoidance of using patches, navigation around the grid uses xNeighbor-reporter
which reports the neighboring cell in the specified direction. Use of this navigation is exemplified by the walk
procedure in which walkers are centred on cells and navigate the grid by stepping to an immediate neighbor cell. They do a (mostly straight) random walk, bouncing off any non-wrapping boundary. The model allows for the World to have none-wrapping boundaries, and even for the grid to have holes in it (provided 'no cell is an island').
To demonstrate the use of manipilation of hexes, we define hex turtles to fit into cells exactly. The Interface tab has mouse and "Action key" combinations to create them, select them, recolor them, drag and drop them between cells, and delete them. Multiple hexes can be created on the same cell. Each new hex is created on top of the others and hides those below (until it is deleted or dragged away).
Only the setup-hex-grid
procedure is dependent upon whether the grid geometry is truly hexagonal. To demonstrate this the TrueHex
switch allows the use of either undistorted or distorted hexagonal grids.
RELATED MODELS
Uri Wilensky's "Hex Cells Example". This uses the distorted hexagonal grid, also modeled here for comparison purposes.
CREDITS AND REFERENCES
Partly based on Uri Wilensky's "Hex Cells Example" in the Models Library.
COPYRIGHT AND LICENSE
by Ian J Heath is licensed under a Creative Commons Attribution 4.0 International License
http://creativecommons.org/licenses/by/4.0/
Comments and Questions
;;Assumptions: ;; ;; - All turtles are centered on a cell, otherwise it won't work ;; - worldWidth is even, so that the cells wraps properly (imposed by slider) ;; - xHeight is even, so that cell centres are integral pixels (imposed by slider) ;; ;; PROCEDURE and VARIABLE USAGE ;; ;; setup <<< User ;; setup-hex-grid <<< setup ;; drag <<< User ;; drag-hex <<< drag ;; selected-hex-reporter <<< drag-hex, delete-hex-atMouse, recolor-hex-atMouse ;; selected-cell-reporter <<< selected-hex-reporter, add-hex-atMouse, add-walker-atMouse ;; add-hex-atMouse <<< User ;; add-hex <<< add-hex-atMouse ;; top-hex-reporter <<< selected-hex-reporter, add-hex ;; next-color-reporter <<< recolor-hex-atMouse, add-hex ;; recolor-hex-atMouse <<< User ;; delete-hex-atMouse <<< User ;; add-walker-atMouse <<< User ;; add-walker <<< add-walker-atMouse, setup (sets heading of walker) ;; xNeighborsInGrid-reporter <<< add-walker, walk, test-xNeighbors ;; walk <<< User (uses and updates heading of walker) ;; xNeighbor-reporter <<< walk, xNeighborsInGrid-reporter ;; test-xNeighbors <<< User globals [xDx xDy cell-size hex-size xNeighbors-xDx xNeighbors-xDy] breed [cells cell ] breed [hexs hex ] breed [walkers walker] to setup clear-all setup-hex-grid ;; create a hex grid of cells ;; setup walkers set-default-shape walkers "arrow half" repeat #walkers [ask one-of cells [add-walker] ] ;; add #walkers walkers reset-ticks end to setup-hex-grid ;; set up a grid of hexagonal cells ifelse TrueHex [ set-default-shape cells "truhex-outline" set-default-shape hexs "truhex" set xDx round(xHeight / 2 * sqrt 3) ;; pxcor displacement of xNeighbors set xDy xHeight ;; pycor displacement of xNeighbors set-patch-size 1 set cell-size round(2 * xDy / sqrt 3) set hex-size cell-size - 2 ;; so that it just fits within the outine. Used in add-hex ][ set-default-shape cells "hex-outline" set-default-shape hexs "hex" set xDx 1 ;; pxcor displacement of xNeighbors set xDy 1 ;; pycor displacement of xNeighbors set-patch-size xHeight set cell-size 4 / 3 ;; Make cells fit fully by expanding beyond patch . set hex-size cell-size - .04 ] set xNeighbors-xDx map [ ?1 -> xDx * ?1 ] [0 1 1 0 -1 -1 ] ;; xDx of xNeighbors, listed by xHeading. Used in xNeighbor-reporter set xNeighbors-xDy map [ ?1 -> xDy * ?1 ] [1 0.5 -0.5 -1 -0.5 0.5] ;; xDy of xNeighbors, listed by xHeading. Used in xNeighbor-reporter ;; resize the world no-display let pixels worldWidth * xDx * worldHeight * xDy if pixels > 999999 [error (word "World too big! It has " pixels " pixels. You should adjust its parameters.")] resize-world 0 (worldWidth * xDx - 1) 0 (worldHeight * xDy - 1) ;;subtract 1 is in order to wrap exactly ;;create cells to form a hex grid foreach n-values worldWidth [ ?1 -> ?1 ] [ ?1 -> let cellX ?1 foreach n-values worldHeight [ ??1 -> ??1 ] [ ??1 -> let cellY ??1 ;; sprout cell(cellX,cellY) at its central patch. Shift even columns half a cell up/down, so that the cells fit together. ask patch (cellX * xDx) (cellY * xDy) [ sprout-cells 1[ set size cell-size ;; Make cells fit fully by expanding beyond patch set color gray ;; In order to stay within the World (if bounded). Shift up if TrueHex, else shift down. if (xcor / xDx) mod 2 = 0 [set ycor ycor + (ifelse-value TrueHex [1][-1]) * (xDy / 2)] ] ] ] ] display end to drag ;; User method if mouse-down? [drag-hex] end to drag-hex ask selected-hex-reporter [hatch 1 die] ;; re-incarnate moused-hex so that it will be on top of any cell it is dragged over ask selected-hex-reporter [ ;; selected-hex is now the re-incarnated moused-hex while [mouse-down?] [ setxy mouse-xcor mouse-ycor display ;; force the display to update, to see the turtle moving around ] move-to min-one-of cells [distance myself] ;; snap to nearest cell at end of drag display ;; force the display to update, to see the snap ] end to-report selected-hex-reporter ;; set of the top (newest added) hex that the mouse is over ('empty' if none) report turtle-set [top-hex-reporter] of selected-cell-reporter end to-report top-hex-reporter ;; cell reporter: top-hex on this cell ('nobody' if none) report max-one-of (hexs-on self) [who] end to-report selected-cell-reporter ;; set of the cell the mouse is over ('empty' if outside world) report ifelse-value mouse-inside? [turtle-set min-one-of cells [distancexy mouse-xcor mouse-ycor]] [no-turtles] end to add-hex-atMouse ;; User method: add a hex on selected cell (on top of any others; no action if mouse outside world) ask selected-cell-reporter [add-hex] end to add-hex ;; cell method: add a hex on top, with the next color after the previous top hex (or red, if none) ;; First, get the current-color of the top hex on this cell, or "none" let top-hex top-hex-reporter let current-color ifelse-value (is-turtle? top-hex) [ [color] of top-hex ] ["none"] ;; Now, add a hex with the next color hatch-hexs 1 [ set size hex-size ;; shrink hex to fit just inside the grid lines set color next-color-reporter current-color ] end to-report next-color-reporter [current-color] ;; color after current-color (or red, if not in swatch) let swatch [red sky yellow lime red] let next-swatch-position ifelse-value (member? current-color swatch) [1 + position current-color swatch] [0] report item next-swatch-position swatch end to recolor-hex-atMouse ;; User method: set color of top moused hex to next color in swatch ask selected-hex-reporter [set color (next-color-reporter color)] end to delete-hex-atMouse ;; User method: delete the top moused hex ask selected-hex-reporter [die] end to add-walker-atMouse ;; User method: add a walker on selected cell (no action if mouse outside world) ask selected-cell-reporter [add-walker] end to add-walker ;; turtle method: create a walker at this turtle hatch-walkers 1 [ set size xDy * 2 ;; to extend head of walker to the centre of neighbor cell set color pink face one-of xNeighborsInGrid-reporter ;; face any xNeighbor in the grid (assumes 'no cell is an island') ] set #walkers count walkers end to walk ;; walker method: step randomly, staying in-grid. Between steps the walker heads from where it came. let xNeighbor xNeighbor-reporter ((round(heading / 60) + one-of [1 -1 0 0 0 0]) mod 6) ;; mainly head straight, turning just +/-60 a third of the time if xNeighbor = nobody [set xNeighbor one-of xNeighborsInGrid-reporter] ;; if not in-grid, set xNeighbor to any in-grid face xNeighbor move-to xNeighbor end to-report xNeighborsInGrid-reporter ;; turtle reporter: all xNeighbor cells in the grid report turtle-set map [ ?1 -> xNeighbor-reporter ?1 ] [0 1 2 3 4 5] end to test-xNeighbors ;; User method: shows the xNeighbors of each individual cell ask cells [ hatch-hexs 1[set color blue] ask xNeighborsInGrid-reporter [hatch-hexs 1[set color gray]] if user-yes-or-no? "Hit Enter to proceed to next cell" [] ask hexs [die] ] end to-report xNeighbor-reporter [xHeadingArg] ;; turtle reporter: report xNeighbor cell in given direction, or "nobody" if it's off-grid ;; xHeading is the index (0,1,2,3,4 or 5 - clockwise, starting from North) of any xNeighbor of a cell report one-of cells-at (item xHeadingArg xNeighbors-xDx) (item xHeadingArg xNeighbors-xDy) end
There are 19 versions of this model.
Attached files
File | Type | Description | Last updated | |
---|---|---|---|---|
Truly Hexagonal Cells Example.png | preview | Preview for 'Truly Hexagonal Cells Example' | over 10 years ago, by Ian Heath | Download |
Truly Hexagonal Cells Example.png | preview | Changed grid to white in order to show up better. | over 10 years ago, by Ian Heath | Download |
This model does not have any ancestors.
This model does not have any descendants.
John McKeown
Excellent
I grew up with paper hexagon maps for games. Great to see this code.
Posted about 8 years ago