diff --git a/javascript/src/color.js b/javascript/src/color.js new file mode 100644 index 000000000..028b824ec --- /dev/null +++ b/javascript/src/color.js @@ -0,0 +1,256 @@ +/** + * @fileoverview A wrapper for colors. + * @suppress {useOfGoogProvide} + */ + +goog.provide('ee.Color'); + +goog.require('ee.ApiFunction'); +goog.require('ee.ComputedObject'); + + + +/** + * An object to represent colors. + * + * @param {string|!Array|!ee.ComputedObject} color + * 1) A W3C compatible color string. + * 2) A list of RGBA values in the range of [0:1]. + * 3) A ComputedObject returning a color. + * @constructor + * @extends {ee.ComputedObject} + * @export + */ +ee.Color = function(color) { + if (!(this instanceof ee.Color)) { + return new ee.Color(color); + } + + ee.Color.initialize(); + + if (color instanceof ee.ComputedObject) { + ee.Color.base(this, 'constructor', color.func, color.args, color.varName); + } else { + ee.Color.base( + this, 'constructor', new ee.ApiFunction('Color'), {'input': color}); + } +}; +goog.inherits(ee.Color, ee.ComputedObject); + + +/** + * Whether the class has been initialized. + * @type {boolean} + * @private + */ +ee.Color.initialized_ = false; + + +/** + * Imports API functions to this class. + */ +ee.Color.initialize = function() { + if (!ee.Color.initialized_) { + ee.ApiFunction.importApi(ee.Color, 'Color', 'Color'); + ee.Color.initialized_ = true; + } +}; + + +/** + * Removes imported API functions from this class. + */ +ee.Color.reset = function() { + ee.ApiFunction.clearApi(ee.Color); + ee.Color.initialized_ = false; +}; + + +/** + * @override + * @return {string} + */ +ee.Color.prototype.name = function() { + return 'Color'; +}; + + +/** + * Creates a Color given a list of HSV values. + * + * @param {!Array|!ee.ComputedObject} hsv A list of HSV (hue, + * saturation, value) values in the range [0:1]. + * @return {!ee.Color} + * @export + */ +ee.Color.fromHsv = function(hsv) { + return /** @type {!ee.Color} */ (ee.ApiFunction._call('Color.fromHsv', hsv)); +}; + + +/** + * Creates a Color given a list of HSL values. + * + * @param {!Array|!ee.ComputedObject} hsl A list of HSL (hue, + * saturation, luminosity) values in the range [0:1]. + * @return {!ee.Color} + * @export + */ +ee.Color.fromHsl = function(hsl) { + return /** @type {!ee.Color} */ (ee.ApiFunction._call('Color.fromHsl', hsl)); +}; + + +/** + * Creates a Color given a list of CIE-LAB values. + * + * @param {!Array|!ee.ComputedObject} lab A list of CIE-LAB values. + * @return {!ee.Color} + * @export + */ +ee.Color.fromLab = function(lab) { + return /** @type {!ee.Color} */ (ee.ApiFunction._call('Color.fromLab', lab)); +}; + + +/** + * Creates a Color given a list of CIE-LCH values. + * + * @param {!Array|!ee.ComputedObject} lch A list of CIE-LCH (lightness, + * chroma, hue) values. + * @return {!ee.Color} + * @export + */ +ee.Color.fromLch = function(lch) { + return /** @type {!ee.Color} */ (ee.ApiFunction._call('Color.fromLch', lch)); +}; + + +/** + * Creates a gray color. + * + * @param {number|!ee.ComputedObject} value The gray value in the range [0:1]. + * @param {number|!ee.ComputedObject=} opt_alpha The alpha value in the range + * [0:1]. + * @return {!ee.Color} + * @export + */ +ee.Color.gray = function(value, opt_alpha) { + return /** @type {!ee.Color} */ ( + ee.ApiFunction._call('Color.gray', value, opt_alpha)); +}; + + +/** + * Mixes two colors. + * + * @param {!ee.Color} start The starting color. + * @param {!ee.Color} end The ending color. + * @param {number|!ee.ComputedObject=} opt_ratio The mix ratio. + * @param {string|!ee.ComputedObject=} opt_colorspace The colorspace to mix + * in. + * @return {!ee.Color} + * @export + */ +ee.Color.mix = function(start, end, opt_ratio, opt_colorspace) { + return /** @type {!ee.Color} */ ( + ee.ApiFunction._call('Color.mix', start, end, opt_ratio, opt_colorspace)); +}; + + +/** + * Scale each of the RGB channels to produce a brighter color. + * + * @param {number|!ee.ComputedObject=} opt_scale The scale factor. + * @return {!ee.Color} + * @export + */ +ee.Color.prototype.brighter = function(opt_scale) { + return /** @type {!ee.Color} */ ( + ee.ApiFunction._call('Color.brighter', this, opt_scale)); +}; + + +/** + * Scale each of the RGB channels to produce a darker color. + * + * @param {number|!ee.ComputedObject=} opt_scale The scale factor. + * @return {!ee.Color} + * @export + */ +ee.Color.prototype.darker = function(opt_scale) { + return /** @type {!ee.Color} */ ( + ee.ApiFunction._call('Color.darker', this, opt_scale)); +}; + + +/** + * Convert a color to HSL. + * + * @return {!ee.ComputedObject} + * @export + */ +ee.Color.prototype.toHsl = function() { + return /** @type {!ee.ComputedObject} */ ( + ee.ApiFunction._call('Color.toHsl', this)); +}; + + +/** + * Convert a color to HSV. + * + * @return {!ee.ComputedObject} + * @export + */ +ee.Color.prototype.toHsv = function() { + return /** @type {!ee.ComputedObject} */ ( + ee.ApiFunction._call('Color.toHsv', this)); +}; + + +/** + * Convert a color to CIE-Lab. + * + * @return {!ee.ComputedObject} + * @export + */ +ee.Color.prototype.toLab = function() { + return /** @type {!ee.ComputedObject} */ ( + ee.ApiFunction._call('Color.toLab', this)); +}; + + +/** + * Convert a color to CIE-LCH. + * + * @return {!ee.ComputedObject} + * @export + */ +ee.Color.prototype.toLch = function() { + return /** @type {!ee.ComputedObject} */ ( + ee.ApiFunction._call('Color.toLch', this)); +}; + + +/** + * Convert a color to RGB. + * + * @return {!ee.ComputedObject} + * @export + */ +ee.Color.prototype.toRgb = function() { + return /** @type {!ee.ComputedObject} */ ( + ee.ApiFunction._call('Color.toRGB', this)); +}; + + +/** + * Returns value of a color as an RGBA hex string. + * + * @return {!ee.ComputedObject} + * @export + */ +ee.Color.prototype.toHexString = function() { + return /** @type {!ee.ComputedObject} */ ( + ee.ApiFunction._call('Color.toHexString', this)); +}; diff --git a/javascript/src/ee.js b/javascript/src/ee.js index 241ee95c1..f56ff4dc0 100644 --- a/javascript/src/ee.js +++ b/javascript/src/ee.js @@ -6,6 +6,7 @@ goog.provide('ee'); goog.require('ee.ApiFunction'); goog.require('ee.Collection'); +goog.require('ee.Color'); goog.require('ee.ComputedObject'); goog.require('ee.CustomFunction'); goog.require('ee.Date'); @@ -21,6 +22,7 @@ goog.require('ee.Image'); goog.require('ee.ImageCollection'); goog.require('ee.List'); goog.require('ee.Number'); +goog.require('ee.Palette'); goog.require('ee.String'); goog.require('ee.Terrain'); goog.require('ee.Types'); @@ -129,6 +131,7 @@ ee.reset = function() { ee.ready_ = ee.InitState.NOT_READY; ee.data.reset(); ee.ApiFunction.reset(); + ee.Color.reset(); ee.Date.reset(); ee.Dictionary.reset(); ee.Element.reset(); @@ -141,6 +144,7 @@ ee.reset = function() { ee.Geometry.reset(); ee.List.reset(); ee.Number.reset(); + ee.Palette.reset(); ee.String.reset(); ee.Terrain.reset(); ee.resetGeneratedClasses_(); @@ -295,12 +299,14 @@ ee.initializationSuccess_ = function() { ee.Image.initialize(); ee.Feature.initialize(); ee.Collection.initialize(); + ee.Color.initialize(); ee.ImageCollection.initialize(); ee.FeatureCollection.initialize(); ee.Filter.initialize(); ee.Geometry.initialize(); ee.List.initialize(); ee.Number.initialize(); + ee.Palette.initialize(); ee.String.initialize(); ee.Terrain.initialize(); diff --git a/javascript/src/palette.js b/javascript/src/palette.js new file mode 100644 index 000000000..abf0937a6 --- /dev/null +++ b/javascript/src/palette.js @@ -0,0 +1,239 @@ +/** + * @fileoverview A wrapper for palettes. + * @suppress {useOfGoogProvide} + */ + +goog.provide('ee.Palette'); + +goog.require('ee.ApiFunction'); +goog.require('ee.Color'); +goog.require('ee.ComputedObject'); + + + +/** + * An object to represent palettes. + * + * @param {string|!Array|!ee.ComputedObject|!Object=} opt_colors + * A list of colors or the name of a predefined color palette. + * @param {string=} opt_mode The colorspace in which to interpolate. + * @param {number=} opt_min The minimum value of the palette. + * @param {number=} opt_max The maximum value of the palette. + * @param {!Array=} opt_padding Shifts the color range by padding the + * end. + * @param {number|!Array=} opt_classes Create a palette representing + * discrete classes. + * @param {!Array=} opt_positions Set the positions for the colors. + * @param {boolean=} opt_correctLightness Correct the color spacing to spread + * lightness range. + * @param {number=} opt_gamma A gamma correction for the palette. + * @param {boolean=} opt_bezier Sets the palette to use Bezier interpolation. + * @constructor + * @extends {ee.ComputedObject} + * @export + */ +ee.Palette = function( + opt_colors, opt_mode, opt_min, opt_max, opt_padding, opt_classes, + opt_positions, opt_correctLightness, opt_gamma, opt_bezier) { + if (!(this instanceof ee.Palette)) { + return new ee.Palette( + opt_colors, opt_mode, opt_min, opt_max, opt_padding, opt_classes, + opt_positions, opt_correctLightness, opt_gamma, opt_bezier); + } + + ee.Palette.initialize(); + + if (opt_colors instanceof ee.ComputedObject && arguments.length == 1) { + ee.Palette.base( + this, 'constructor', opt_colors.func, opt_colors.args, + opt_colors.varName); + } else { + ee.Palette.base(this, 'constructor', new ee.ApiFunction('Palette'), { + 'colors': opt_colors, + 'mode': opt_mode || 'RGB', + 'min': opt_min != null ? opt_min : 0.0, + 'max': opt_max != null ? opt_max : 1.0, + 'padding': opt_padding || null, + 'classes': opt_classes || null, + 'positions': opt_positions || null, + 'correctLightness': opt_correctLightness || false, + 'gamma': opt_gamma != null ? opt_gamma : 1.0, + 'bezier': opt_bezier || false + }); + } +}; +goog.inherits(ee.Palette, ee.ComputedObject); + + +/** + * Whether the class has been initialized. + * @type {boolean} + * @private + */ +ee.Palette.initialized_ = false; + + +/** + * Imports API functions to this class. + */ +ee.Palette.initialize = function() { + if (!ee.Palette.initialized_) { + ee.ApiFunction.importApi(ee.Palette, 'Palette', 'Palette'); + ee.Palette.initialized_ = true; + } +}; + + +/** + * Removes imported API functions from this class. + */ +ee.Palette.reset = function() { + ee.ApiFunction.clearApi(ee.Palette); + ee.Palette.initialized_ = false; +}; + + +/** + * @override + * @return {string} + */ +ee.Palette.prototype.name = function() { + return 'Palette'; +}; + + +/** + * Returns the color at the given value. + * + * @param {number|!ee.ComputedObject} value The value to look up. + * @return {!ee.Color} + * @export + */ +ee.Palette.prototype.getColor = function(value) { + return /** @type {!ee.Color} */ ( + ee.ApiFunction._call('Palette.getColor', this, value)); +}; + + +/** + * Get colors from this palette. + * + * @param {number|!ee.ComputedObject=} opt_nColors The number of equally + * spaced colors to retrieve. + * @return {!ee.ComputedObject} + * @export + */ +ee.Palette.prototype.getColors = function(opt_nColors) { + return /** @type {!ee.ComputedObject} */ ( + ee.ApiFunction._call('Palette.getColors', this, opt_nColors)); +}; + + +/** + * Set the colorspace interpolation mode. + * + * @param {string|!ee.ComputedObject} mode The colorspace mode. + * @return {!ee.Palette} + * @export + */ +ee.Palette.prototype.mode = function(mode) { + return /** @type {!ee.Palette} */ ( + ee.ApiFunction._call('Palette.mode', this, mode)); +}; + + +/** + * Set the minimum and maximum limits for the palette. + * + * @param {number|!ee.ComputedObject} min The minimum value. + * @param {number|!ee.ComputedObject} max The maximum value. + * @return {!ee.Palette} + * @export + */ +ee.Palette.prototype.limits = function(min, max) { + return /** @type {!ee.Palette} */ ( + ee.ApiFunction._call('Palette.limits', this, min, max)); +}; + + +/** + * Set the position for each color in the palette. + * + * @param {!Array|!ee.ComputedObject} positions A list of values + * specifying the position for each color. + * @return {!ee.Palette} + * @export + */ +ee.Palette.prototype.positions = function(positions) { + return /** @type {!ee.Palette} */ ( + ee.ApiFunction._call('Palette.positions', this, positions)); +}; + + +/** + * Use discrete classes as opposed to a continuous gradient. + * + * @param {number|!Array|!ee.ComputedObject} classes + * Either a list of class break values, or a single number. + * @return {!ee.Palette} + * @export + */ +ee.Palette.prototype.classes = function(classes) { + return /** @type {!ee.Palette} */ ( + ee.ApiFunction._call('Palette.classes', this, classes)); +}; + + +/** + * Shifts the color range by padding the end of the color scale. + * + * @param {number|!ee.ComputedObject} left The left padding. + * @param {number|!ee.ComputedObject=} opt_right The right padding. + * @return {!ee.Palette} + * @export + */ +ee.Palette.prototype.padding = function(left, opt_right) { + return /** @type {!ee.Palette} */ ( + ee.ApiFunction._call('Palette.padding', this, left, opt_right)); +}; + + +/** + * Apply a gamma correction to the palette. + * + * @param {number|!ee.ComputedObject} gamma The gamma value. + * @return {!ee.Palette} + * @export + */ +ee.Palette.prototype.gamma = function(gamma) { + return /** @type {!ee.Palette} */ ( + ee.ApiFunction._call('Palette.gamma', this, gamma)); +}; + + +/** + * Sets the palette to use bezier interpolation. + * + * @param {boolean|!ee.ComputedObject=} opt_interpolate Whether to use bezier + * interpolation. + * @return {!ee.Palette} + * @export + */ +ee.Palette.prototype.bezier = function(opt_interpolate) { + return /** @type {!ee.Palette} */ ( + ee.ApiFunction._call('Palette.bezier', this, opt_interpolate)); +}; + + +/** + * Correct the color spacing to spread lightness range evenly. + * + * @param {boolean|!ee.ComputedObject=} opt_correct Whether to correct + * lightness. + * @return {!ee.Palette} + * @export + */ +ee.Palette.prototype.correctLightness = function(opt_correct) { + return /** @type {!ee.Palette} */ ( + ee.ApiFunction._call('Palette.correctLightness', this, opt_correct)); +}; diff --git a/python/ee/tests/color_test.py b/python/ee/tests/color_test.py new file mode 100644 index 000000000..c4270bdcd --- /dev/null +++ b/python/ee/tests/color_test.py @@ -0,0 +1,128 @@ +#!/usr/bin/env python3 +"""Tests for the ee.Color class.""" + +import json +import unittest +import ee +from ee import apitestcase + + +class ColorTest(apitestcase.ApiTestCase): + + def test_constructors(self): + """Test the ee.Color constructors.""" + self.assertEqual('Color', ee.Color('red').func.getSignature()['name']) + self.assertEqual({'input': 'red'}, ee.Color('red').args) + + self.assertEqual('Color', ee.Color([1, 0, 0]).func.getSignature()['name']) + self.assertEqual({'input': [1, 0, 0]}, ee.Color([1, 0, 0]).args) + + self.assertEqual('Color', ee.Color(input='blue').func.getSignature()['name']) + self.assertEqual({'input': 'blue'}, ee.Color(input='blue').args) + + def test_serialization(self): + """Test the ee.Color serialization.""" + # String input + color = ee.Color('red') + result = json.loads(color.serialize()) + expect = { + 'result': '0', + 'values': { + '0': { + 'functionInvocationValue': { + 'arguments': {'input': {'constantValue': 'red'}}, + 'functionName': 'Color', + } + } + }, + } + self.assertEqual(expect, result) + + # List input + color = ee.Color([1, 0, 0]) + result = json.loads(color.serialize()) + expect = { + 'result': '0', + 'values': { + '0': { + 'functionInvocationValue': { + 'arguments': {'input': {'constantValue': [1, 0, 0]}}, + 'functionName': 'Color', + } + } + }, + } + self.assertEqual(expect, result) + + def test_static_methods_serialization(self): + """Test serialization of ee.Color static methods.""" + # fromHsv + color = ee.Color.fromHsv([0.1, 0.2, 0.3]) + node = json.loads(color.serialize())['values']['0']['functionInvocationValue'] + self.assertEqual('Color.fromHsv', node['functionName']) + self.assertEqual([0.1, 0.2, 0.3], node['arguments']['hsv']['constantValue']) + + # fromHsl + color = ee.Color.fromHsl([0.4, 0.5, 0.6]) + node = json.loads(color.serialize())['values']['0']['functionInvocationValue'] + self.assertEqual('Color.fromHsl', node['functionName']) + self.assertEqual([0.4, 0.5, 0.6], node['arguments']['hsl']['constantValue']) + + # fromLab + color = ee.Color.fromLab([10, 20, 30]) + node = json.loads(color.serialize())['values']['0']['functionInvocationValue'] + self.assertEqual('Color.fromLab', node['functionName']) + self.assertEqual([10, 20, 30], node['arguments']['lab']['constantValue']) + + # fromLch + color = ee.Color.fromLch([40, 50, 60]) + node = json.loads(color.serialize())['values']['0']['functionInvocationValue'] + self.assertEqual('Color.fromLch', node['functionName']) + self.assertEqual([40, 50, 60], node['arguments']['lch']['constantValue']) + + # gray + color = ee.Color.gray(0.7, 0.5) + node = json.loads(color.serialize())['values']['0']['functionInvocationValue'] + self.assertEqual('Color.gray', node['functionName']) + self.assertEqual(0.7, node['arguments']['value']['constantValue']) + self.assertEqual(0.5, node['arguments']['alpha']['constantValue']) + + # mix (static) + color = ee.Color.mix(ee.Color('red'), ee.Color('blue'), 0.8, 'hsv') + result = json.loads(color.serialize()) + node = result['values'][result['result']]['functionInvocationValue'] + self.assertEqual('Color.mix', node['functionName']) + self.assertEqual(0.8, node['arguments']['ratio']['constantValue']) + self.assertEqual('hsv', node['arguments']['colorspace']['constantValue']) + self.assertEqual('Color', node['arguments']['start']['functionInvocationValue']['functionName']) + self.assertEqual('Color', node['arguments']['end']['functionInvocationValue']['functionName']) + + def test_instance_methods_serialization(self): + """Test serialization of ee.Color instance methods.""" + # brighter + color = ee.Color('red').brighter(0.9) + result = json.loads(color.serialize()) + node = result['values'][result['result']]['functionInvocationValue'] + self.assertEqual('Color.brighter', node['functionName']) + self.assertEqual(0.9, node['arguments']['scale']['constantValue']) + self.assertEqual('Color', node['arguments']['color']['functionInvocationValue']['functionName']) + + # darker + color = ee.Color('red').darker(0.1) + result = json.loads(color.serialize()) + node = result['values'][result['result']]['functionInvocationValue'] + self.assertEqual('Color.darker', node['functionName']) + self.assertEqual(0.1, node['arguments']['scale']['constantValue']) + + # toHsl, toHsv, toLab, toLch, toRgb, toHexString + for method in ['toHsl', 'toHsv', 'toLab', 'toLch', 'toRgb', 'toHexString']: + color = getattr(ee.Color('red'), method)() + result = json.loads(color.serialize()) + node = result['values'][result['result']]['functionInvocationValue'] + expected_name = 'Color.' + (method.replace('Rgb', 'RGB') if method == 'toRgb' else method) + self.assertEqual(expected_name, node['functionName']) + self.assertIn('color', node['arguments']) + + +if __name__ == '__main__': + unittest.main() diff --git a/python/ee/tests/palette_test.py b/python/ee/tests/palette_test.py new file mode 100644 index 000000000..982ed175d --- /dev/null +++ b/python/ee/tests/palette_test.py @@ -0,0 +1,167 @@ +#!/usr/bin/env python3 +"""Tests for the ee.Palette class.""" + +import json +import unittest +import ee +from ee import apitestcase + + +class PaletteTest(apitestcase.ApiTestCase): + + def test_constructors(self): + """Test the ee.Palette constructors.""" + self.assertEqual('Palette', ee.Palette('spectral').func.getSignature()['name']) + self.assertEqual({'colors': 'spectral'}, ee.Palette('spectral').args) + + self.assertEqual('Palette', ee.Palette(['red', 'blue']).func.getSignature()['name']) + self.assertEqual({'colors': ['red', 'blue']}, ee.Palette(['red', 'blue']).args) + + self.assertEqual('Palette', ee.Palette(colors=['red', 'blue']).func.getSignature()['name']) + self.assertEqual( + {'colors': ['red', 'blue']}, ee.Palette(colors=['red', 'blue']).args + ) + + def test_serialization(self): + """Test the ee.Palette serialization.""" + palette = ee.Palette(['red', 'blue']) + result = json.loads(palette.serialize()) + expect = { + 'result': '0', + 'values': { + '0': { + 'functionInvocationValue': { + 'arguments': { + 'colors': { + 'constantValue': ['red', 'blue'] + }, + }, + 'functionName': 'Palette', + } + } + }, + } + self.assertEqual(expect, result) + + # Full constructor (use positional to be dynamic-compatible) + palette = ee.Palette( + ['red', 'blue'], + 'hsv', + 10.0, + 20.0, + [0.1, 0.2], + [0, 10, 20], + [0, 0.5, 1], + True, + 2.2, + True, + ) + result = json.loads(palette.serialize()) + node = result['values'][result['result']]['functionInvocationValue'] + self.assertEqual('Palette', node['functionName']) + args = node['arguments'] + self.assertEqual(['red', 'blue'], args['colors']['constantValue']) + self.assertEqual('hsv', args['mode']['constantValue']) + self.assertEqual(10.0, args['min']['constantValue']) + self.assertEqual(20.0, args['max']['constantValue']) + self.assertEqual([0.1, 0.2], args['padding']['constantValue']) + self.assertEqual([0, 10, 20], args['classes']['constantValue']) + self.assertEqual([0, 0.5, 1], args['positions']['constantValue']) + self.assertTrue(args['correctLightness']['constantValue']) + self.assertEqual(2.2, args['gamma']['constantValue']) + self.assertTrue(args['bezier']['constantValue']) + + def test_static_methods_serialization(self): + """Test serialization of ee.Palette static methods.""" + # cubeHelix (use positional) + palette = ee.Palette.cubeHelix( + 0.5, + 1.0, + 2.0, + 1.5, + 0.1, + 0.9, + 0.8, + ) + result = json.loads(palette.serialize()) + node = result['values'][result['result']]['functionInvocationValue'] + self.assertEqual('Palette.cubeHelix', node['functionName']) + args = node['arguments'] + self.assertEqual(0.5, args['startHue']['constantValue']) + self.assertEqual(1.0, args['rotations']['constantValue']) + self.assertEqual(2.0, args['saturation']['constantValue']) + self.assertEqual(1.5, args['gamma']['constantValue']) + self.assertEqual(0.1, args['startLightness']['constantValue']) + self.assertEqual(0.9, args['endLightness']['constantValue']) + self.assertEqual(0.8, args['endHue']['constantValue']) + + def test_instance_methods_serialization(self): + """Test serialization of ee.Palette instance methods.""" + palette = ee.Palette('spectral') + + # getColor + res = palette.getColor(0.5) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.getColor', node['functionName']) + self.assertEqual(0.5, node['arguments']['value']['constantValue']) + self.assertEqual('Palette', node['arguments']['palette']['functionInvocationValue']['functionName']) + + # getColors + res = palette.getColors(5) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.getColors', node['functionName']) + self.assertEqual(5, node['arguments']['nColors']['constantValue']) + + # mode + res = palette.mode('HSL') + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.mode', node['functionName']) + self.assertEqual('HSL', node['arguments']['mode']['constantValue']) + + # limits + res = palette.limits(10, 20) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.limits', node['functionName']) + self.assertEqual(10, node['arguments']['min']['constantValue']) + self.assertEqual(20, node['arguments']['max']['constantValue']) + + # positions + res = palette.positions([0, 1]) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.positions', node['functionName']) + self.assertEqual([0, 1], node['arguments']['positions']['constantValue']) + + # classes + res = palette.classes(3) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.classes', node['functionName']) + self.assertEqual(3, node['arguments']['classes']['constantValue']) + + # padding + res = palette.padding(0.1, 0.2) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.padding', node['functionName']) + self.assertEqual(0.1, node['arguments']['left']['constantValue']) + self.assertEqual(0.2, node['arguments']['right']['constantValue']) + + # gamma + res = palette.gamma(2.0) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.gamma', node['functionName']) + self.assertEqual(2.0, node['arguments']['gamma']['constantValue']) + + # bezier + res = palette.bezier(True) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.bezier', node['functionName']) + self.assertTrue(node['arguments']['interpolate']['constantValue']) + + # correctLightness + res = palette.correctLightness(True) + node = json.loads(res.serialize())['values'][json.loads(res.serialize())['result']]['functionInvocationValue'] + self.assertEqual('Palette.correctLightness', node['functionName']) + self.assertTrue(node['arguments']['correct']['constantValue']) + + +if __name__ == '__main__': + unittest.main()