Generación de mapa de altura utilizando desplazamiento medio -- random campo con image campo con f# camp codereview Relacionados El problema

Heightmap generation using midpoint displacement


3
vote

problema

Español

Estoy escribiendo un programa para generar un mapa de altura después del algoritmo de desplazamiento de punto medio (algo similar a Diamond-Square).

Estoy en el punto en que tengo un programa recursivo que pinte todo el mapa, pero aún necesita algunas modificaciones para proporcionar el resultado deseado.

Antes de hacerlo, me gustaría una revisión de código sobre lo que tengo hasta ahora para asegurarme de que todo lo lejos está bien.

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] <- value  // returns a square matrix of size 2^n + 1 let newHeightMap n : HeightMap =     let size = ( pown 2 n ) + 1     {Size = size; Map = Array.zeroCreate (size * size)}    // normalize a single value to constrain it's value between 0.0 and 1.0 let normalizeValue v =     match v with     | v when v < 0.0 -> 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_displement.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, y2) (x3, y3) (x4, y4) (variation) =     // set left middle     if hm.Get x1 (avgi y1 y3) = 0.0 then          hm.Set x1 (avgi y1 y3) (avgf (hm.Get x1 y1) (hm.Get x3 y3) |> variation |> normalizeValue)            // set upper middle     if hm.Get (avgi x1 x2) y1 = 0.0 then         hm.Set (avgi x1 x2) y1 (avgf (hm.Get x1 y1) (hm.Get x2 y2) |> variation |> normalizeValue)      // set right middle     if hm.Get x2 (avgi y2 y4) = 0.0 then          hm.Set x2 (avgi y2 y4) (avgf (hm.Get x2 y2) (hm.Get x4 y4) |> variation |> normalizeValue)      // set lower middle     if hm.Get (avgi x3 x4) y3 = 0.0 then         hm.Set (avgi x3 x4) y3 (avgf (hm.Get x3 y3) (hm.Get x4 y4) |> variation |> normalizeValue)             // set the center value of the current matrix to the average of all middle values + variation function let center (hm:HeightMap) (x1, y1) (x2, y2) (x3, y3) (x4, y4) (variation) =     // average height of left and right middle points     let avgHorizontal = avgf (hm.Get x1 (avgi y1 y3)) (hm.Get x2 (avgi y2 y4))     let avgVertical = avgf (hm.Get (avgi x1 x2) y1) (hm.Get (avgi x3 x4) y3)      // set center value     hm.Set (avgi x1 x4) (avgi y1 y4) (avgf avgHorizontal avgVertical |> variation |> normalizeValue)   let rec displace (hm) (x1, y1) (x4, y4) (rnd) =     let ulCorner = (x1, y1)      let urCorner = (x4, y1)     let llCorner = (x1, y4)     let lrCorner = (x4, y4)      // the lambda passed in as a parameter is temporary until a define a better function     middle hm ulCorner urCorner llCorner lrCorner (fun x -> x + (randomize rnd 100))     center hm ulCorner urCorner llCorner lrCorner (fun x -> x + (randomize rnd 100))      if x4 - x1 >= 2 then         let xAvg = avgi x1 x4         let yAvg = avgi y1 y4         displace hm (x1, y1) (xAvg, yAvg) rnd         displace hm (xAvg, y1) (x4, yAvg) rnd         displace hm (x1, yAvg) (xAvg, y4) rnd         displace hm (xAvg, yAvg) (x4, y4) rnd  let generate hm =     initCorners hm          let size = hm.Size - 1     let rnd = System.Random()      displace hm (0, 0) (size, size) rnd   

Entre otras cosas, dos cosas me parecen particularmente malas, pero no sé cómo refactorizarlas.

  1. Tengo un avgi2 avgi3 función Debido a la división por 2 o 2.0, que depende del tipo de entrada.

  2. |> variation |> normalizeValue se repite 5 veces.

Original en ingles

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] <- value  // returns a square matrix of size 2^n + 1 let newHeightMap n : HeightMap =     let size = ( pown 2 n ) + 1     {Size = size; Map = Array.zeroCreate (size * size)}    // normalize a single value to constrain it's value between 0.0 and 1.0 let normalizeValue v =     match v with     | v when v < 0.0 -> 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, y2) (x3, y3) (x4, y4) (variation) =     // set left middle     if hm.Get x1 (avgi y1 y3) = 0.0 then          hm.Set x1 (avgi y1 y3) (avgf (hm.Get x1 y1) (hm.Get x3 y3) |> variation |> normalizeValue)            // set upper middle     if hm.Get (avgi x1 x2) y1 = 0.0 then         hm.Set (avgi x1 x2) y1 (avgf (hm.Get x1 y1) (hm.Get x2 y2) |> variation |> normalizeValue)      // set right middle     if hm.Get x2 (avgi y2 y4) = 0.0 then          hm.Set x2 (avgi y2 y4) (avgf (hm.Get x2 y2) (hm.Get x4 y4) |> variation |> normalizeValue)      // set lower middle     if hm.Get (avgi x3 x4) y3 = 0.0 then         hm.Set (avgi x3 x4) y3 (avgf (hm.Get x3 y3) (hm.Get x4 y4) |> variation |> normalizeValue)             // set the center value of the current matrix to the average of all middle values + variation function let center (hm:HeightMap) (x1, y1) (x2, y2) (x3, y3) (x4, y4) (variation) =     // average height of left and right middle points     let avgHorizontal = avgf (hm.Get x1 (avgi y1 y3)) (hm.Get x2 (avgi y2 y4))     let avgVertical = avgf (hm.Get (avgi x1 x2) y1) (hm.Get (avgi x3 x4) y3)      // set center value     hm.Set (avgi x1 x4) (avgi y1 y4) (avgf avgHorizontal avgVertical |> variation |> normalizeValue)   let rec displace (hm) (x1, y1) (x4, y4) (rnd) =     let ulCorner = (x1, y1)      let urCorner = (x4, y1)     let llCorner = (x1, y4)     let lrCorner = (x4, y4)      // the lambda passed in as a parameter is temporary until a define a better function     middle hm ulCorner urCorner llCorner lrCorner (fun x -> x + (randomize rnd 100))     center hm ulCorner urCorner llCorner lrCorner (fun x -> x + (randomize rnd 100))      if x4 - x1 >= 2 then         let xAvg = avgi x1 x4         let yAvg = avgi y1 y4         displace hm (x1, y1) (xAvg, yAvg) rnd         displace hm (xAvg, y1) (x4, yAvg) rnd         displace hm (x1, yAvg) (xAvg, y4) rnd         displace hm (xAvg, yAvg) (x4, y4) rnd  let generate hm =     initCorners hm          let size = hm.Size - 1     let rnd = System.Random()      displace hm (0, 0) (size, size) rnd 

Among other things, two things seem particularly wrong to me but I don't know how to refactor them.

  1. I have both an avgf and avgi function because of the division by 2 or 2.0 which depends on the input type.

  2. |> variation |> normalizeValue is repeated 5 times.

        

Lista de respuestas

1
 
vote
vote
La mejor respuesta
 

Así es como puede tener una única función promedio de dos puntos que funciona tanto para flotar como para intentes, así como otros tipos numéricos:

  // returns the average between two values     let inline avg (a:^n) (b:^n) : ^n =     (a + b) / (LanguagePrimitives.GenericOne + LanguagePrimitives.GenericOne)   

En cuanto a la repetición de |> variation |> normalizeValue no tengo nada súper inteligente que sugerir, pero podría acortarlo usando el operador 9988776665544332 para combinar esos dos Funciones, algo así:

  let middle (hm:HeightMap) (x1, y1) (x2, y2) (x3, y3) (x4, y4) (variation) =     let vn = variation >> normalizeValue      // set left middle     if hm.Get x1 (avg y1 y3) = 0.0 then          hm.Set x1 (avg y1 y3) (avg (hm.Get x1 y1) (hm.Get x3 y3) |> vn)            // set upper middle     if hm.Get (avg x1 x2) y1 = 0.0 then         hm.Set (avg x1 x2) y1 (avg (hm.Get x1 y1) (hm.Get x2 y2) |> vn)      // set right middle     if hm.Get x2 (avg y2 y4) = 0.0 then          hm.Set x2 (avg y2 y4) (avg (hm.Get x2 y2) (hm.Get x4 y4) |> vn)      // set lower middle     if hm.Get (avg x3 x4) y3 = 0.0 then         hm.Set (avg x3 x4) y3 (avg (hm.Get x3 y3) (hm.Get x4 y4) |> vn)    

En lugar de llamar a una función para convertir un entero literal a un flotador con int (float 255 * pct) Yo probablemente utilice un flotador literal: 9988777665544335 .

Su función en su mayoría solo devuelve 0.0 porque está haciendo una división entera. Dividir un entero menor que el límite por el límite, que es un número entero, devolverá un número menor que 1, que se truncará a cero por la división de enteros. Es posible que encuentre esta versión un poco más cerca de su intención.

  let randomize (rnd:System.Random) (bound:int) : float =     let max = float bound     let min = -max      min + (rnd.NextDouble() * ((1.0 + max) - min))   
 

Here's how you can have a single two-point average function that works for both float and int, as well as other numeric types:

// returns the average between two values     let inline avg (a:^n) (b:^n) : ^n =     (a + b) / (LanguagePrimitives.GenericOne + LanguagePrimitives.GenericOne) 

As far as the repetition of |> variation |> normalizeValue I don't have anything super-clever to suggest, but you could shorten it by using the >> operator to combine those two functions, something like this:

let middle (hm:HeightMap) (x1, y1) (x2, y2) (x3, y3) (x4, y4) (variation) =     let vn = variation >> normalizeValue      // set left middle     if hm.Get x1 (avg y1 y3) = 0.0 then          hm.Set x1 (avg y1 y3) (avg (hm.Get x1 y1) (hm.Get x3 y3) |> vn)            // set upper middle     if hm.Get (avg x1 x2) y1 = 0.0 then         hm.Set (avg x1 x2) y1 (avg (hm.Get x1 y1) (hm.Get x2 y2) |> vn)      // set right middle     if hm.Get x2 (avg y2 y4) = 0.0 then          hm.Set x2 (avg y2 y4) (avg (hm.Get x2 y2) (hm.Get x4 y4) |> vn)      // set lower middle     if hm.Get (avg x3 x4) y3 = 0.0 then         hm.Set (avg x3 x4) y3 (avg (hm.Get x3 y3) (hm.Get x4 y4) |> vn)  

Instead of calling a function to convert a literal integer to a float with int (float 255 * pct) I would probably use a literal float: int (255.0 * pct).

Your randomize function mostly only returns 0.0 because it's doing integer division. Dividing an integer less than your bound by the bound, which is an integer, will return a number less than 1, which will be truncated to zero by the integer division. You might find this version a little closer to your intent.

let randomize (rnd:System.Random) (bound:int) : float =     let max = float bound     let min = -max      min + (rnd.NextDouble() * ((1.0 + max) - min)) 
 
 
3
 
vote

Lo primero que debe hacer cuando vea un código de la placa de calderas es representar el código como datos. Por lo tanto, su función media es bastante simple, todos los lados de un cuadrado (representados como un par de puntos) usan puntos laterales y un punto entre ellos y apliquen cambio. En resumen, podrías escribir algo así

  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)         

Un truco adicional cuando trabaja con una matriz con algún cursor dentro de él y tiene que hacer un trabajo recursivo, entonces podría aplicar Comonads.

 

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.

 
 

Relacionados problema

11  Enfoque para construir programáticamente componentes gui jerárquicos  ( Approach to programmatically building hierarchical gui components ) 
En el trabajo, estoy desarrollando una aplicación usando el swing codificado a mano, y he encontrado que tengo un tiempo más fácil de leer, escribir y mantene...

13  Poker Hands Kata en F #  ( Poker hands kata in f ) 
Soy un F # Newbie, y me gustaría compartir mi implementación de la Poker Hands Kata Problem Para obtener algunos comentarios. string1 En la primera mi...

2  F # Patrón activo con póquer y naipes  ( F active pattern with poker and playing cards ) 
de Esta pregunta y John Palmers responden Sobre el patrón activo Descubrí una brecha en mi conocimiento sobre F # y tomé el desafío de codificarme en algún ...

4  ¿Cómo refactorizar este método de Groupby por igual pero con el índice?  ( How to refactor this groupby alike method but with index ) 
Tengo una matriz jaggada 2D (la fila es la primera dimensión, la columna es la segunda dimensión, la parte inferior de la derecha, de izquierda a derecha) ...

9  Eliminación de árbol negro rojo en F #  ( Deleting from red black tree in f ) 
Sí, estoy muy lentamente a través de las estructuras de datos puramente funcionales. Así que pasé por la sección sobre árboles negros rojos. Lo que presenta e...

3  Mi implementación de permutación lexicográfica en F # es 3x más lenta que C #  ( My implementation of lexicographic permutation in f is 3x slower than c ) 
¿Puede alguien ayudarme con la siguiente micro optimización para el código F # para la permutación lexicográfica? Tengo código en C # que se ejecuta para 0,...

3  Convertir el valor de bitcoin basado en el tipo de cambio de JSON API  ( Convert bitcoin value based on exchange rate from json api ) 
Estoy aprendiendo F # e intentando encontrar una forma más 'funcional' de codificar un programa simple que recupera el precio de BTC y calcula el valor del EU...

2  Usando tipos en F #  ( Using types in f ) 
He jugado con F # por más de un año, pero recientemente he comenzado tentativamente lo usándolo para el trabajo profesional. Me preocupa que mis principios de...

10  Obteniendo la última fecha donde ocurrió un día de la semana dado  ( Getting the last date where a given week day occurred ) 
Estoy tratando de aprender un poco sobre la programación funcional y como mi herramienta, elegí F # ya que soy un desarrollador de .NET y el medio ambiente es...

4  Clases de datos puros inmutables con artículos públicos en propiedades  ( Immutable pure data classes with public setters on properties ) 
Estoy reuniendo algunas clases como modelo para alguna información (que actualmente estoy tirando de un sitio web). Las clases se implementan en C #, porque...




© 2022 respuesta.top Reservados todos los derechos. Centro de preguntas y respuestas reservados todos los derechos