aboutsummaryrefslogtreecommitdiffhomepage
path: root/tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html
diff options
context:
space:
mode:
Diffstat (limited to 'tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html')
-rw-r--r--tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html360
1 files changed, 360 insertions, 0 deletions
diff --git a/tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html b/tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html
new file mode 100644
index 0000000000..8a3503468f
--- /dev/null
+++ b/tensorflow/tensorboard/components/vz_heatmap_d3v4/vz-heatmap.html
@@ -0,0 +1,360 @@
+<!--
+@license
+Copyright 2016 The TensorFlow Authors. All Rights Reserved.
+
+Licensed under the Apache License, Version 2.0 (the "License");
+you may not use this file except in compliance with the License.
+You may obtain a copy of the License at
+
+ http://www.apache.org/licenses/LICENSE-2.0
+
+Unless required by applicable law or agreed to in writing, software
+distributed under the License is distributed on an "AS IS" BASIS,
+WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+See the License for the specific language governing permissions and
+limitations under the License.
+-->
+
+<link rel="import" href="../polymer/polymer.html">
+<link rel="import" href="../iron-resizable-behavior/iron-resizable-behavior.html">
+<link rel="import" href="../tf-imports/d3.html">
+
+<!--
+`vz-heatmap` A simple heatmap to visualize 2D data using predefined or user
+defined colour scheme, with a dependency on d3.js. The heatmap automatically
+fits itself to the width of the container it is placed in.
+
+@element vz-heatmap
+@demo demo/index.html
+-->
+<dom-module id="vz-heatmap">
+ <template>
+ <div>
+ <canvas id="heatmap"
+ style="width: 100%; image-rendering:pixelated"></canvas>
+ </div>
+ </template>
+ <script>
+ Polymer({
+ is: 'vz-heatmap',
+ properties: {
+ /**
+ * An 2-element array which defines the lower and upper bound of the
+ * automatically created color scale for the heatmap.
+ */
+ colors: {
+ type: Array,
+ value: ['white', 'steelblue'],
+ observer: '_onColorsChange'
+ },
+ /**
+ * A function which returns a hex-formatted color string
+ * given a number type.
+ * i.e. (input: number) => string
+ *
+ * Passing a colorFunction deactivates the color
+ * function generated by the `colors` and `values` fields.
+ */
+ colorFunction: {
+ type: Object,
+ observer: '_onColorFunctionChange'
+ },
+ /**
+ * A 2D data array containing the data to be visualized.
+ */
+ data: {
+ type: Array,
+ value: [],
+ observer: '_onDataChange'
+ },
+ /**
+ * A 2-element array containing the domain of values which
+ * maps to the color range defined by the `colors` array.
+ */
+ values: {
+ type: Array,
+ observer: '_onValuesChange'
+ },
+ _currentDataValid: {
+ type: Boolean,
+ value: false
+ },
+ _dataExtent: {
+ type: Array,
+ value: [0, 1]
+ },
+ _debug: {
+ type: Boolean,
+ value: false
+ },
+ _generatedColorScale: {
+ type: Object,
+ value: (function () {
+ var retFunction = d3.scaleLinear();
+ if (this.colors) {
+ retFunction.range(this.colors)
+ }
+
+ return retFunction;
+ })()
+ },
+ _isReady: {
+ type: Boolean,
+ value: false
+ },
+ _height: {
+ type: Number,
+ value: 0
+ },
+ _width: {
+ type: Number,
+ value: 0
+ },
+ _useUserColorPickerFunction: {
+ type: Boolean,
+ value: false
+ }
+ },
+ behaviors: [
+ Polymer.IronResizableBehavior
+ ],
+ listeners: {
+ 'iron-resize': '_onWidthChange'
+ },
+ _onWidthChange: function () {
+ // Re-render the chart if the data has already been set.
+ if (this._currentDataValid) {
+ this._renderData();
+ }
+ },
+ ready: function () {
+ this._isReady = true;
+ this._onWidthChange();
+ },
+ attached: function () {
+ this._onWidthChange();
+ },
+ /**
+ * Data change handler, if new data is valid, sets flag to indicate data
+ * now valid, updates the data extent and renders the new data.
+ */
+ _onDataChange: function () {
+ // Validate new data.
+ if (!this._isDataValid(this.data)) {
+ return;
+ }
+
+ // Set flag to indicate data is now valid.
+ this._currentDataValid = true;
+
+ // Calculate new data extent.
+ this._updateDataExtent();
+
+ if (this._isReady) {
+ this._renderData();
+ }
+ },
+ /**
+ * (Re-)Renders the heatmap. Called when data, color mapping, or width
+ * changes.
+ * @private
+ */
+ _renderData: function () {
+ // Ensure dimensions are up-to-date and valid before starting rendering.
+ this._updateDimensions();
+ if (!this._width || !this._height) return;
+
+ // Ensure the color function is up-to-date.
+ this._updateColorFunction();
+ var width = this._width;
+ var rows = this.data.length;
+ var columns = this.data[0].length;
+
+ // Calculate side length of each tile.
+ var sideLength = width / columns;
+
+ // Set domain and range of the axis.
+ var colorScale = (this._useUserColorPickerFunction) ?
+ this.colorFunction : this._generatedColorScale;
+
+ // Clear the canvas.
+ this.resetCanvas();
+
+ // Render heatmap.
+ var ctx = this.$.heatmap.getContext("2d");
+
+ for (var row = 0; row < rows; row++) {
+ for (var column = 0; column < columns; column++) {
+ var value = this.data[row][column];
+ // Set location and dimensions.
+ ctx.fillStyle = colorScale(value);
+ // Preferred way to set color of individual pixels.
+ ctx.fillRect(column, row, 1, 1);
+ }
+ }
+ },
+ /**
+ * Observer for this.colors.
+ * @private
+ */
+ _onColorsChange: function () {
+ if (Array.isArray(this.colors) && this.colors.length == 2) {
+ this._updateColorFunction();
+ }
+ this._useUserColorPickerFunction = false;
+ this._renderData();
+ },
+ /**
+ * Observer for this.values. Updates the
+ * internal color function. If data invalid, internal color scale uses
+ * the extent of the data as domain.
+ * @private
+ */
+ _onValuesChange: function () {
+ // Verify that validity of the new content of values.
+ // If data not valid, set this.values to undefined, as
+ // _updateColorFunction will then use the data's extent as the color
+ // scale's domain.
+ if (!(Array.isArray(this.values) && this.values.length == 2)) {
+ this.values = undefined;
+ }
+
+ this._updateColorFunction();
+ this._useUserColorPickerFunction = false;
+ this._renderData();
+ },
+ /**
+ * Observer for this.colorFunction. This field allows the user to
+ * provide a custom color scale. Updates to this value are ignored, if the new
+ * value is not a function object.
+ * @private
+ */
+ _onColorFunctionChange: function () {
+ if (typeof this.colorFunction === 'function') {
+ this._useUserColorPickerFunction = true;
+ this._renderData();
+ } else if (this._debug) {
+ console.log('The colorFunction provided is not of function type, and was not set as default.')
+ }
+ },
+ /**
+ * Calculates the extent of the data if it is required by the
+ * current color function. This function is called when
+ * properties change which would result in the color scale
+ * changing, or when the computed color scale is started to be used.
+ * @private
+ */
+ _updateDataExtent: function () {
+ if (this._currentDataValid) {
+ var rows = this.data.length;
+ var columns = this.data[0].length;
+
+ var min = this.data[0][0];
+ var max = this.data[0][0];
+
+ for (var row = 0; row < rows; row++) {
+ for (var col = 0; col < columns; col++) {
+ var currentElement = this.data[row][col];
+
+ if (currentElement < min) {
+ min = currentElement;
+ } else if (currentElement > max) {
+ max = currentElement;
+ }
+ }
+ }
+
+ this._dataExtent = [min, max];
+ } else if (!this._dataExtent) {
+ this._dataExtent = [0, 1];
+ }
+ },
+ /**
+ * Updates the internal color function only when the number of
+ * elements in this.colors is equal to the number of elements in
+ * this.values or if this.values is empty, in which case the
+ * extent of the data is used.
+ * @private
+ */
+ _updateColorFunction: function () {
+ if (Array.isArray(this.colors) && this.colors.length) {
+ this._generatedColorScale = d3.scaleLinear().range(this.colors);
+ }
+
+ if (this.values) {
+ // Check that the number of elements in this.values and
+ // this.colors have an identical number of elements.
+ if (this.colors.length === this.values.length) {
+ this._generatedColorScale.domain(this.values);
+ }
+
+ // If values reset, and data field contains valid data, set colour scale
+ // to use the data extent as domain.
+ } else if (this._currentDataValid) {
+ this._generatedColorScale.domain(this._dataExtent);
+ }
+ },
+ _getLinearInterpolation: function (domain, range) {
+ return d3.scaleLinear().domain(domain).range(range);
+ },
+ /**
+ * Find side length of each tile in heat map by dividing current width
+ * by number of columns
+ */
+ _findTileSideLength: function () {
+ if (this._currentDataValid) {
+ var numOfColumns = this.data[0].length;
+
+ // Make sure value is finite.
+ if (numOfColumns === 0) {
+ var returnValue = 0;
+ } else {
+ returnValue = this._width / numOfColumns;
+ }
+ return returnValue;
+
+ } else {
+ if (this._debug == true) {
+ console.log("WARNING: vz-heatmap is reverting to zero height as passed data is invalid.")
+ }
+ return 0; // Fall back to 0 height, if data is invalid.
+ }
+ },
+ /**
+ * Verifies whether input data is valid.
+ * @param newData Number[][]
+ * @private
+ */
+ _isDataValid: function (newData) {
+ return Array.isArray(newData) && newData.length &&
+ Array.isArray(newData[0]);
+ },
+ _updateDimensions: function () {
+ var canvasElement = this.$.heatmap;
+
+ var rows = 0;
+ var columns = 0;
+ if (this._currentDataValid) {
+ rows = this.data.length;
+ columns = this.data[0].length;
+ }
+
+ // Recalculate height and width, and ensure data can be rendered.
+ this._width = canvasElement.parentNode.clientWidth;
+ this._height = this._findTileSideLength() * rows;
+ // Update the attribute height and width.
+ canvasElement.setAttribute('height', rows);
+ canvasElement.setAttribute('width', columns);
+ },
+ /**
+ * Function which clears the canvas.
+ */
+ resetCanvas: function () {
+ // Reset the canvas.
+ var canvas = this.$.heatmap;
+ var ctx = canvas.getContext("2d");
+ ctx.clearRect(0, 0, canvas.width, canvas.height);
+ }
+ });
+ </script>
+</dom-module>