mw-engine/src/mw_engine/heightmap.clj
Simon Brooke e48ad3f891 Added a function to add gradient properties to cells when applying a
heightmap, and, in doing so, added a potentially useful generalised function
for mapping onto two dimensional arrays. Needs testing!
2014-07-25 09:49:44 +01:00

95 lines
3.8 KiB
Clojure

;; Functions to apply a heightmap to a world.
;;
;; Heightmaps are considered only as greyscale images, so colour is redundent (will be
;; ignored). Darker shades are higher.
(ns mw-engine.heightmap
(:import [java.awt.image BufferedImage])
(:use mw-engine.utils
mw-engine.world
;; interestingly the imagez load-image is failing for me, while the
;; collage version is problem free.
[mikera.image.core :only [filter-image get-pixels]]
[mikera.image.filters]
[fivetonine.collage.util]
))
(defn- abs
"Surprisingly, Clojure doesn't seem to have an abs function, or else I've
missed it. So here's one of my own. Maps natural numbers onto themselves,
and negative integers onto natural numbers. Also maps negative real numbers
onto positive real numbers.
* `n` a number, on the set of real numbers."
[n]
(cond (< n 0) (- 0 n) true n))
(defn tag-gradient
"Set the `gradient` property of this `cell` of this `world` to the difference in
altitude between its highest and lowest neghbours."
[cell world]
(let [heights (map '(:altitude %) (get-neighbours world cell))
highest (apply max heights)
lowest (apply min heights)]
#(merge cell {:gradient (- highest lowest)})))
(defn tag-gradients
"Set the `gradient` property of each cell in this `world` to the difference in
altitude between its highest and lowest neghbours."
(map-world world tag-gradient))
(defn transform-altitude
"Set the altitude of this cell from the corresponding pixel of this heightmap.
If the heightmap you supply is smaller than the world, this will break.
* `world` not actually used, but present to enable this function to be
passed as an argument to `mw-engine.utils/map-world`, q.v.
* `cell` a cell, as discussed in world.clj, q.v. Alternatively, a map;
* `heightmap` an (ideally) greyscale image, whose x and y dimensions should
exceed those of the world of which the `cell` forms part."
([world cell heightmap]
(transform-altitude cell heightmap))
([cell heightmap]
(merge cell
{:altitude
(+ (get-int cell :altitude)
(- 256
(abs
(mod
(.getRGB heightmap
(get-int cell :x)
(get-int cell :y)) 256))))})))
(defn- apply-heightmap-row
"Set the altitude of each cell in this sequence from the corresponding pixel
of this heightmap.
If the heightmap you supply is smaller than the world, this will break.
* `row` a row in a world, as discussed in world.clj, q.v. Alternatively, a
sequence of maps;
* `heightmap` an (ideally) greyscale image, whose x and y dimensions should
exceed those of the world of which the `cell` forms part."
[row heightmap]
(apply vector (map #(transform-altitude % heightmap) row)))
(defn apply-heightmap
"Apply the image file loaded from this path to this world, and return a world whose
altitudes are modified (added to) by the altitudes in the heightmap. It is assumed that
the heightmap is at least as large in x and y dimensions as the world.
* `world` a world, as defined in `world.clj`, q.v.; if world is not supplied,
a world the size of the heightmap will be created.
* `imagepath` a file path or URL which indicates an image file."
([world imagepath]
;; bizarrely, the collage load-util is working for me, but the imagez version isn't.
(let [heightmap (filter-image (grayscale)(load-image imagepath))]
(map-world
(map-world world transform-altitude (list heigtmap))
tag-gradient)))
([imagepath]
(let [heightmap (filter-image (grayscale)(load-image imagepath))
world (make-world (.getWidth heightmap) (.getHeight heightmap))]
(map-world
(map-world world transform-altitude (list heigtmap))
tag-gradient))))