This visualization shows the composition of a function f: (X, Y) => V with a colorizing function c: V => Color over a rectangular range on the (X, Y) plane. LengthSpace[X, X, Double] and LengthSpace[Y, Y, Double] must be implicitly in scope.


A few imports:

import cats.implicits._

import spire.implicits.DoubleAlgebra

import axle._
import axle.visualize._

Define a function to compute an Double for each point on the plane (x, y): (Double, Double)

def f(x0: Double, x1: Double, y0: Double, y1: Double) = x0 + y0
// f: (x0: Double, x1: Double, y0: Double, y1: Double)Double

Define a toColor function. Here we first prepare an array of colors to avoid creating the objects during rendering.

val n = 100
// n: Int = 100

// red to orange to yellow
val roy = (0 until n).map(i => Color(255, ((i / n.toDouble) * 255).toInt, 0)).toArray
// roy: Array[axle.visualize.Color] = Array(Color(255,0,0), Color(255,2,0), Color(255,5,0), Color(255,7,0), Color(255,10,0), Color(255,12,0), Color(255,15,0), Color(255,17,0), Color(255,20,0), Color(255,22,0), Color(255,25,0), Color(255,28,0), Color(255,30,0), Color(255,33,0), Color(255,35,0), Color(255,38,0), Color(255,40,0), Color(255,43,0), Color(255,45,0), Color(255,48,0), Color(255,51,0), Color(255,53,0), Color(255,56,0), Color(255,58,0), Color(255,61,0), Color(255,63,0), Color(255,66,0), Color(255,68,0), Color(255,71,0), Color(255,73,0), Color(255,76,0), Color(255,79,0), Color(255,81,0), Color(255,84,0), Color(255,86,0), Color(255,89,0), Color(255,91,0), Color(255,94,0), Color(255,96,0), Color(255,99,0), Color(255,102,0), Color(255,104,0), Color(255,107,0), Color(255,109,0), Color(25...

def toColor(v: Double) = roy(v.toInt % n)
// toColor: (v: Double)axle.visualize.Color

Define a PixelatedColoredArea to show toColor ∘ f over the range (0,0) to (1000,1000) represented as a 400 pixel square.

val pca = PixelatedColoredArea(f, toColor, 400, 400, 0d, 1000d, 0d, 1000d)
// pca: axle.visualize.PixelatedColoredArea[Double,Double,Double] = PixelatedColoredArea(<function4>,<function1>,400,400,0.0,1000.0,0.0,1000.0)

Create PNG

import axle.awt._
// import axle.awt._

png(pca, "roy_diagonal.png")

ROY Diagonal

Second example

More compactly:

import spire.math.sqrt
// import spire.math.sqrt

val m = 200
// m: Int = 200

val greens = (0 until m).map(i => Color(0, ((i / m.toDouble) * 255).toInt, 0)).toArray
// greens: Array[axle.visualize.Color] = Array(Color(0,0,0), Color(0,1,0), Color(0,2,0), Color(0,3,0), Color(0,5,0), Color(0,6,0), Color(0,7,0), Color(0,8,0), Color(0,10,0), Color(0,11,0), Color(0,12,0), Color(0,14,0), Color(0,15,0), Color(0,16,0), Color(0,17,0), Color(0,19,0), Color(0,20,0), Color(0,21,0), Color(0,22,0), Color(0,24,0), Color(0,25,0), Color(0,26,0), Color(0,28,0), Color(0,29,0), Color(0,30,0), Color(0,31,0), Color(0,33,0), Color(0,34,0), Color(0,35,0), Color(0,36,0), Color(0,38,0), Color(0,39,0), Color(0,40,0), Color(0,42,0), Color(0,43,0), Color(0,44,0), Color(0,45,0), Color(0,47,0), Color(0,48,0), Color(0,49,0), Color(0,51,0), Color(0,52,0), Color(0,53,0), Color(0,54,0), Color(0,56,0), Color(0,57,0), Color(0,58,0), Color(0,59,0), Color(0,61,0), Color(0,62,0), Color(0,63,...

    (x0: Double, x1: Double, y0: Double, y1: Double) => sqrt(x0*x0 + y0*y0),
    (v: Double) => greens(v.toInt % m),
    400, 400,
    0d, 1000d,
    0d, 1000d),

Green Polar