HiveBrain v1.2.0
Get Started
← Back to all entries
patternMinor

Heightmap generation using midpoint displacement

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
midpointdisplacementheightmapgenerationusing

Problem

I am writing a program to generate a height map following the midpoint displacement algorithm (somewhat similar to diamond-square).

I'm at the point where I have a recursive program that paints the whole map but still needs some modifications to provide the desired result.

Before I do, I would like a code review on what I have so far to make sure everything so far is ok.

height_map.fs

module HeightMap

// contains the height map types and common functions that can be re-used for 
// different generation algorithms

type HeightMap = {Size:int; Map:float array} with     
    member this.Get x y =
        this.Map.[x * this.Size + y]      

    member this.Set x y value =
        this.Map.[x * this.Size + y]  0.0
    | v when v > 1.0 -> 1.0
    | _ -> v

// converts a float point ranging from 0.0 to 1.0 to a rgb value
// 0.0 represents black and 1.0 white. The conversion is in greyscale 
let convertFloatToRgb (pct:float) : int * int * int =
    let greyscale = int (float 255 * pct)
    (greyscale, greyscale, greyscale)

// returns the average between two values    
let avgf (a:float) (b:float) =
    (a + b) / 2.0

// find the middle between two points in our map
let avgi (a:int) (b:int) =
    (a + b) / 2

// returns a floating number which is generated using bounds as a control of the range of possible values
let randomize (rnd:System.Random) (bound:int) : float = 
    float (rnd.Next(-bound, bound) / bound)


midpoint_displacement.fs

```
module MidpointDisplacement

open HeightMap

// set the four corners to random values
let initCorners (hm:HeightMap) =
let rnd = System.Random()
let size = hm.Size

hm.Set 0 0 (rnd.NextDouble())
hm.Set 0 (size - 1) (rnd.NextDouble())
hm.Set (size - 1) 0 (rnd.NextDouble())
hm.Set (size - 1) (size - 1) (rnd.NextDouble())

// set the middle values between each corner (c1 c2 c3 c4)
// variation is a function that is applied on each pixel to modify it's value
let middle (hm:HeightMap) (x1, y1) (x2

Solution

First thing to do when you see a boilerplate code is to represent code as data.
So your function middle is quite simple iterate all sides of a square(represented as a pair of points) use side points and a point between them and apply change. In short you could write something like that

let middle (hm:HeightMap) (x1, y1) (x2, y2) (x3, y3) (x4, y4) (variation) =
    let points = [|x1, y1; x2, y2; x4, y4; x3, y3; x1, y1|]//clockwise iterate sides
    for i in 0..3 do
        let x1, y1 = points.[i]
        let x2, y2 = points.[i + 1]
        let mx, my = avgi x1 x2, avgi y1 y2

        if hm.Get mx my = 0.0 then 
            hm.Set mx my (avgf (hm.Get x1 y1) (hm.Get x2 y2) |> variation |> normalizeValue)


One additional trick when you work with an array with some cursor inside it and have to do some recursive work then you could apply comonads.

Code Snippets

let middle (hm:HeightMap) (x1, y1) (x2, y2) (x3, y3) (x4, y4) (variation) =
    let points = [|x1, y1; x2, y2; x4, y4; x3, y3; x1, y1|]//clockwise iterate sides
    for i in 0..3 do
        let x1, y1 = points.[i]
        let x2, y2 = points.[i + 1]
        let mx, my = avgi x1 x2, avgi y1 y2

        if hm.Get mx my = 0.0 then 
            hm.Set mx my (avgf (hm.Get x1 y1) (hm.Get x2 y2) |> variation |> normalizeValue)

Context

StackExchange Code Review Q#122875, answer score: 3

Revisions (0)

No revisions yet.