UNPKG

aframe-babia-components

Version:

A data visualization set of components for A-Frame.

348 lines (302 loc) 11.1 kB
let updateTitle = require('../others/common').updateTitle; const colors = require('../others/common').colors; let updateFunction = require('../others/common').updateFunction; const NotiBuffer = require("../../common/noti-buffer").NotiBuffer; /* global AFRAME */ if (typeof AFRAME === 'undefined') { throw new Error('Component attempted to register before AFRAME was available.'); } /** * A-Charts component for A-Frame. */ AFRAME.registerComponent('babia-bubbles', { schema: { data: { type: 'string' }, height: { type: 'string', default: 'height' }, radius: { type: 'string', default: 'radius' }, x_axis: { type: 'string', default: 'x_axis' }, z_axis: { type: 'string', default: 'z_axis' }, from: { type: 'string' }, legend: { type: 'boolean' }, legend_lookat: { type: 'string', default: "[camera]" }, legend_scale: { type: 'number', default: 1 }, axis: { type: 'boolean', default: true }, // Name for axis axis_name: {type: 'boolean', default: false}, animation: { type: 'boolean', default: false }, palette: { type: 'string', default: 'ubuntu' }, title: { type: 'string' }, titleFont: { type: 'string' }, titleColor: { type: 'string' }, titlePosition: { type: 'string', default: "0 0 0" }, scale: { type: 'number' }, heightMax: { type: 'number' }, radiusMax: { type: 'number' }, }, /** * List of visualization properties */ visProperties: ['height', 'radius', 'x_axis', 'z_axis'], /** * Set if component needs multiple instancing. */ multiple: false, /** * Called once when component is attached. Generally for initial setup. */ init: function () { this.notiBuffer = new NotiBuffer(); }, /** * Called when component is attached and when component data changes. * Generally modifies the entity based on the data. */ update: function (oldData) { updateFunction(this, oldData) }, /** * Querier component target */ prodComponent: undefined, /** * NotiBuffer identifier */ notiBufferId: undefined, /** * Where the data is gonna be stored */ newData: undefined, /** * Where the metadata is gonna be stored */ babiaMetadata: { id: 0 }, /* * Update title */ updateTitle: function(){ const titleRotation = { x: 0, y: 0, z: 0 } const titleEl = updateTitle(this.data, titleRotation); this.el.appendChild(titleEl); }, /* * Update chart */ updateChart: function () { const dataToPrint = this.newData; console.log("Data babia-bubbles:", dataToPrint) const data = this.data; const el = this.el; const animation = data.animation const palette = data.palette const scale = data.scale let heightMax = data.heightMax let radiusMax = data.radiusMax let xLabels = []; let xTicks = []; let zLabels = []; let zTicks = []; let colorId = 0 let maxColorId = 0 let stepX = 0 let stepZ = 0 let maxX = 0 let maxZ = 0 let keys_used = {} let z_axis = {} let valueMax = Math.max.apply(Math, dataToPrint.map(function (o) { return o[data.height]; })) let maxRadius = Math.max.apply(Math, dataToPrint.map(function (o) { return o[data.radius]; })) if (scale) { valueMax = valueMax / scale maxRadius = maxRadius / scale } if (!heightMax) { heightMax = valueMax } proportion = heightMax / valueMax if (!radiusMax) { radiusMax = maxRadius } radius_scale = radiusMax / maxRadius this.chartEl = document.createElement('a-entity'); this.chartEl.classList.add('babiaxrChart') el.appendChild(this.chartEl) if (scale) { maxX = maxRadius / scale; maxZ = maxRadius / scale; } else { maxX = maxRadius * radius_scale; maxZ = maxRadius * radius_scale; } for (let bubble of dataToPrint) { let xLabel = bubble[data.x_axis] let zLabel = bubble[data.z_axis] let height = bubble[data.height] let radius = bubble[data.radius] // Check if used in order to put the bubble in the parent row if (keys_used[xLabel]) { stepX = keys_used[xLabel].posX colorId = keys_used[xLabel].colorId } else { if (scale) { stepX += 2 * maxRadius / scale + 0.5 } else { stepX += 2 * maxRadius * radius_scale + 0.5 } colorId = maxColorId //Save in used keys_used[xLabel] = { "posX": stepX, "colorId": colorId } xLabels.push(xLabel) xTicks.push(stepX) maxColorId++ } // Get Z val if (z_axis[zLabel]) { stepZ = z_axis[zLabel].posZ } else { if (scale) { stepZ = maxZ + 2 * maxRadius / scale + 0.5 } else { stepZ = maxZ + 2 * maxRadius * radius_scale + 0.5 } //Save in used z_axis[zLabel] = { "posZ": stepZ } zLabels.push(zLabel) zTicks.push(stepZ) } if (stepX > maxX){ maxX = stepX } if (stepZ > maxZ){ maxZ = stepZ } let bubbleEntity = generateBubble(height, radius, colorId, palette, stepX, stepZ, animation, scale, proportion, radius_scale); bubbleEntity.classList.add("babiaxraycasterclass") this.chartEl.appendChild(bubbleEntity); //Prepare legend if (data.legend) { showLegend(data, bubbleEntity, bubble, el) } } //Print axis if (data.axis) { const lengthX = maxX const lengthZ = maxZ const lengthY = heightMax this.updateAxis(xLabels, xTicks, lengthX, zLabels, zTicks, lengthZ, valueMax, lengthY); } this.updateTitle(); }, /* * Update axis */ updateAxis: function(xLabels, xTicks, lengthX, zLabels, zTicks, lengthZ, valueMax, lengthY) { let xAxisEl = document.createElement('a-entity'); this.chartEl.appendChild(xAxisEl); xAxisEl.setAttribute('babia-axis-x',{'labels': xLabels, 'ticks': xTicks, 'length': lengthX,'palette': this.data.palette}); xAxisEl.setAttribute('position', {x: 0, y: 0, z: 0}); let yAxisEl = document.createElement('a-entity'); this.chartEl.appendChild(yAxisEl); yAxisEl.setAttribute('babia-axis-y',{'maxValue': valueMax, 'length': lengthY}); yAxisEl.setAttribute('position', {x: 0, y: 0, z: 0}); let zAxisEl = document.createElement('a-entity'); this.chartEl.appendChild(zAxisEl); zAxisEl.setAttribute('babia-axis-z',{'labels': zLabels, 'ticks': zTicks, 'length': lengthZ}); zAxisEl.setAttribute('position', {x: 0, y: 0, z: 0}); if (this.data.axis_name){ xAxisEl.setAttribute('babia-axis-x', 'name', this.data.x_axis); yAxisEl.setAttribute('babia-axis-y', 'name', this.data.height); zAxisEl.setAttribute('babia-axis-z', 'name', this.data.z_axis); } }, /* * Process data obtained from producer */ processData: function (data) { console.log("processData", this); this.newData = data; this.babiaMetadata = { id: this.babiaMetadata.id++ }; while (this.el.firstChild) this.el.firstChild.remove(); console.log("Generating bubbles...") this.updateChart() this.notiBuffer.set(this.newData) } }) function generateBubble(height, radius, colorId, palette, positionX, positionZ, animation, scale, proportion, radius_scale) { let color = colors.get(colorId, palette) console.log("Generating bubble...") if (scale) { height = height / scale radius = radius / scale } else if (proportion || radius_scale) { if (proportion) { height = proportion * height } if (radius_scale) { radius = radius_scale * radius } } let entity = document.createElement('a-sphere'); entity.setAttribute('color', color); entity.setAttribute('radius', radius); // Add Animation if (animation) { let from = positionX.toString() + " " + radius.toString() + " " + positionZ.toString() let to = positionX.toString() + " " + (radius + height).toString() + " " + positionZ.toString() entity.setAttribute('animation__position', { 'property': 'position', 'from': from, 'to': to, 'dur': '3000', 'easing': 'linear', }) } else { entity.setAttribute('position', { x: positionX, y: radius + height, z: positionZ }); } return entity; } function generateLegend(data, bubble, bubbleEntity) { let text = bubble[data.x_axis] + ': \n Radius:' + bubble[data.radius] + '\nHeight:' + bubble[data.height]; let width = 2; if (text.length > 16) width = text.length / 8; let bubblePosition = bubbleEntity.getAttribute('position') let bubbleRadius = parseFloat(bubbleEntity.getAttribute('radius')) let entity = document.createElement('a-plane'); entity.setAttribute('position', { x: bubblePosition.x, y: bubblePosition.y + bubbleRadius + 1, z: bubblePosition.z + 0.1 }); entity.setAttribute('rotation', { x: 0, y: 0, z: 0 }); entity.setAttribute('height', '1'); entity.setAttribute('width', width); entity.setAttribute('color', 'white'); entity.setAttribute('text', { 'value': text, 'align': 'center', 'width': 6, 'color': 'black' }); entity.classList.add("babiaxrLegend") entity.setAttribute('babia-lookat', data.legend_lookat); entity.setAttribute('scale',{x: data.legend_scale, y: data.legend_scale, z: data.legend_scale}); return entity; } function showLegend(data, bubbleEntity, bubble, element) { bubbleEntity.addEventListener('mouseenter', function () { this.setAttribute('scale', { x: 1.1, y: 1.1, z: 1.1 }); legend = generateLegend(data, bubble, bubbleEntity); element.appendChild(legend); }); bubbleEntity.addEventListener('mouseleave', function () { this.setAttribute('scale', { x: 1, y: 1, z: 1 }); element.removeChild(legend); }); }