UNPKG

aframe-babia-components

Version:

A data visualization set of components for A-Frame.

1,169 lines (1,030 loc) 81.4 kB
let findProdComponent = require('../others/common').findProdComponent; let updateFunction = require('../others/common').updateFunction; const colorsArray = require('../others/common').colors.palettes['categoric']; 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-boats', { schema: { data: { type: 'asset' }, from: { type: 'string' }, border: { type: 'number', default: 0.5 }, width: { type: 'string', default: 'width' }, depth: { type: 'string', default: 'depth' }, area: { type: 'string' }, color: { type: 'string' }, height: { type: 'string', default: 'height' }, maxBuildingHeight: { type: 'number', default: 2 }, minBuildingHeight: { type: 'number', default: 0.03 }, zone_elevation: { type: 'number', default: 0.01 }, building_separation: { type: 'number', default: 0.25 }, extra: { type: 'number', default: 1.0 }, levels: { type: 'number' }, building_color: { type: 'string', default: "#E6B9A1" }, buildingAlpha: { type: 'number', default: 1 }, gradientBaseColor: { type: 'boolean', default: false }, base_color: { type: 'color', default: '#98e690' }, baseAlpha: { type: 'number', default: 1 }, // To add into the doc height_quarter_legend_box: { type: 'number', default: 11 }, height_quarter_legend_title: { type: 'number', default: 12 }, height_building_legend: { type: 'number', default: 0 }, legend_scale: { type: 'number', default: 1 }, legend_lookat: { type: 'string', default: "[camera]" }, metricsInfoId: { type: 'string', default: "" }, highlightQuarter: { type: 'boolean', default: false }, field: { type: 'string', default: 'uid' }, // Numeric color legend entity to hide/show numericColorLegendId: { type: 'string' }, // Highlight building by field highlightBuildingByField: { type: 'string' }, highlightBuildingByFieldColor: { type: 'string', default: 'white' }, // Wireframe & Transparency by repeated IDs wireframeByRepeatedField: { type: 'string' }, transparent80ByRepeatedField: { type: 'string' }, transparent20ByRepeatedField: { type: 'string' }, // Autoscale when animating or starting autoscale: { type: 'boolean', default: false }, autoscaleSizeX: { type: 'number', default: 3 }, autoscaleSizeZ: { type: 'number', default: 3 }, //autoscaleSizeY: { type: 'number', default: 2 } // New layout treeLayout: { type: 'boolean', default: false }, treeQuartersLevelHeight: { type: 'number', default: 0.2 }, treeFixQuarterHeight: { type: 'boolean', default: false }, treeHideOneSonQuarters: { type: 'boolean', default: false } }, /** * Entities with legend activated */ entitiesWithLegend: [], legendsActive: [], /** * Querier component target */ dataComponent: undefined, /** * Where the data is gonna be stored */ newData: undefined, /** * Where the metaddata is gonna be stored */ babiaMetadata: { id: 0 }, /** * List of visualization properties */ visProperties: ['height', 'area', 'width', 'depth', 'color'], /** * Set if component needs multiple instancing. */ multiple: false, /** * */ duration: 2000, figures_del: [], figures_in: [], animation: 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) }, /** * Already autoscaled */ alreadyAutoscaled: false, autoscaleBoats: function () { const self = this // 2 for X and 2 for Y let bbox = new THREE.Box3().setFromObject(this.el.object3D) let finalSizeX = bbox.max.x - bbox.min.x let finalSizeY = bbox.max.y - bbox.min.y let finalSizeZ = bbox.max.z - bbox.min.z let currentScale = this.el.getAttribute("scale") if (!currentScale) { currentScale = { x: 1, y: 1, z: 1 } } this.el.setAttribute("scale", { x: eval(this.data.autoscaleSizeX / finalSizeX) * currentScale.x, y: currentScale.y, z: eval(this.data.autoscaleSizeZ / finalSizeZ) * currentScale.z }) }, /** * Called on each scene tick. */ tick: function (t, delta) { let self = this; // First time to autoscale if (this.data.autoscale && !this.alreadyAutoscaled) { let bbox = new THREE.Box3().setFromObject(this.el.object3D) if (bbox.min.x !== Infinity) { this.autoscaleBoats() this.alreadyAutoscaled = true } } if (this.animation) { let t = { x: 0, y: 0, z: 0 }; if ((Date.now() - this.start_time) > this.duration) { this.animation = false; this.setFigures(this.figures, t); // Animation finished, set autoscale if activated if (this.data.autoscale) { this.autoscaleBoats() } //Reactivate legends, check PERFORMANCE self.entitiesWithLegend = self.entitiesWithLegend.filter(item => document.getElementById(item.entity.id)) self.entitiesWithLegend.forEach(item => { let entity = item.entity let figure = item.figure if (figure.children) { // Quarter entity.legend = generateLegend(figure.name, self.data.legend_scale, self.data.legend_lookat, 'black', 'white'); let worldPos = new THREE.Vector3(); let coordinates = worldPos.setFromMatrixPosition(entity.object3D.matrixWorld); let coordinatesFinal = { x: coordinates.x, y: self.data.height_quarter_legend_title, z: coordinates.z } entity.legend.setAttribute('position', coordinatesFinal) entity.legend.setAttribute('visible', true); self.el.parentElement.appendChild(entity.legend) self.legendsActive.push(entity.legend) entity.alreadyActive = true } else { // Building entity.legend = generateLegend(figure.name, self.data.legend_scale, self.data.legend_lookat, 'white', 'black', entity.babiaRawData, self.data.height, self.data.area, self.data.depth, self.data.width, self.data.color); let worldPos = new THREE.Vector3(); let coordinates = worldPos.setFromMatrixPosition(entity.object3D.matrixWorld); let height_real = new THREE.Box3().setFromObject(entity.object3D) let coordinatesFinal = { x: coordinates.x, y: height_real.max.y + 1 + self.data.height_building_legend, z: coordinates.z } entity.legend.setAttribute('position', coordinatesFinal) entity.legend.setAttribute('visible', true); self.el.parentElement.appendChild(entity.legend); self.legendsActive.push(entity.legend) entity.alreadyActive = true } }); } else { this.Animation(this.el, this.figures, this.figures_old, delta, t, t); } } }, updateChart: function (items) { console.log('Data Loaded.'); let t = { x: 0, y: 0, z: 0 }; this.idsToNotRepeatWireframe = [] this.idsToNotRepeat20Transparent = [] this.idsToNotRepeat80Transparent = [] // when animation not finished, delete figures and opa 1 to inserted if (this.animation) { this.figures_del.forEach(figure => { let entity = document.getElementById(figure.id); if (entity) { entity.remove(); } }) this.figures_in.forEach(figure => { let entity = document.getElementById(figure.id); if (entity) { setOpacity(entity, 1.0); } }) this.setFigures(this.figures, t); this.animation = false; this.figures_del = []; this.figures_in = []; } this.figures_old = this.figures; this.figures = []; let el = this.el; let elements = items // Calculate Increment let increment; if (this.data.levels) { increment = this.data.border * this.data.extra * this.data.levels; } else { // Find last level let levels = getLevels(elements, 0); //console.log("Levels:" + levels); increment = this.data.border * this.data.extra * (levels + 1); } // Register all figures before drawing [x, y, t, this.figures] = this.generateElements(elements, this.figures, t, increment); // Draw figures t.x = 0; t.z = 0; if (this.figures_old.length == 0) { // First, delete all elements of the component while (this.el.firstChild) this.el.firstChild.remove(); this.drawElements(el, this.figures, t); } else { if (this.figures_old !== this.figures) { this.animation = true; this.start_time = Date.now(); } } }, /** * For the categoric colors */ categoricColorIndex: 0, categoricColorMaps: {}, generateElements: function (elements, figures, translate, inc) { const self = this; var increment = inc; // Vertical Limits var limit_up = 0; var limit_down = 0; // Horizontal Limits var limit_right = 0; var limit_left = 0; //Position Figure var posX = 0; var posY = 0; // Aux to update the limits // Save max limit to update last limit in the next step var max_right = 0; var max_left = 0; var max_down = 0; var max_up = 0; // control points var current_vertical = 0; var current_horizontal = 0; // Controllers var up = false; var down = false; var left = false; var right = true; /** * Get each element and set its position respectly * Then save all data in figures array */ for (let i = 0; i < elements.length; i++) { // To do not overwrite newData let element = {} if (this.data.width) { element.width = elements[i][this.data.width] || 0.5 } if (this.data.height) { element.height = self.normalizeValues(self.babiaMetadata['heightMin'], self.babiaMetadata['heightMax'], self.data.minBuildingHeight, self.data.maxBuildingHeight, elements[i][this.data.height]) || this.data.minBuildingHeight } if (this.data.depth) { element.depth = elements[i][this.data.depth] || 0.5 } if (this.data.area) { element.area = elements[i][this.data.area] || 0.5 } if (elements[i].children) { element.children = elements[i].children this.quarter = true; var children = []; var translate_matrix; // Save Zone's parameters // Check scale because it should be abosulte, not relative let scale = self.el.getAttribute("scale") if (!scale) { scale = { x: 1, y: 1, z: 1 } } element.height = this.data.zone_elevation / scale.y; increment -= this.data.border * this.data.extra; // TEST TREE if (self.data.treeLayout) { element.children.forEach(el => { if (!el.children) { element.treeMetaphorHeight = el[self.data.height] return } }); } [element.width, element.depth, translate_matrix, children] = this.generateElements(element.children, children, translate_matrix, increment); translate_matrix.y = element.height; increment = inc; } if (i == 0) { if (this.data.area && !elements[i].children) { limit_up += Math.sqrt(element.area) / 2; limit_down -= Math.sqrt(element.area) / 2; limit_right += Math.sqrt(element.area) / 2; limit_left -= Math.sqrt(element.area) / 2; } else { limit_up += element.depth / 2; limit_down -= element.depth / 2; limit_right += element.width / 2; limit_left -= element.width / 2; } //console.log("==== RIGHT SIDE ===="); current_horizontal = limit_up + this.data.building_separation / 2; } else if (element.height > 0) { if (up) { [current_vertical, posX, posY, max_up] = this.UpSide(element, limit_up, current_vertical, max_up); if (current_vertical > limit_right) { current_vertical += this.data.building_separation / 2; max_right = current_vertical; up = false; right = true; if (max_left < limit_left) { limit_left = max_left; } current_horizontal = limit_up + this.data.building_separation / 2; } } else if (right) { [current_horizontal, posX, posY, max_right] = this.RightSide(element, limit_right, current_horizontal, max_right); if (current_horizontal < limit_down) { current_horizontal += this.data.building_separation / 2; max_down = current_horizontal; right = false; down = true; if (max_up > limit_up) { limit_up = max_up; } current_vertical = limit_right + this.data.building_separation / 2; } } else if (down) { [current_vertical, posX, posY, max_down] = this.DownSide(element, limit_down, current_vertical, max_down); if (current_vertical < limit_left) { current_vertical -= this.data.building_separation / 2; max_left = current_vertical; down = false; left = true; if (max_right > limit_right) { limit_right = max_right; } current_horizontal = limit_down - this.data.building_separation / 2; } } else if (left) { [current_horizontal, posX, posY, max_left] = this.LeftSide(element, limit_left, current_horizontal, max_left); if (current_horizontal > limit_up) { current_horizontal -= this.data.building_separation / 2; max_up = current_horizontal; left = false; up = true; if (max_down < limit_down) { limit_down = max_down; } current_vertical = limit_left - this.data.building_separation / 2; } } } // Save information about the figure let figure if (elements[i].children) { figure = { id: "boat-" + elements[i][this.data.field], name: elements[i][this.data.field], posX: posX, posY: posY, width: element.width, height: element.height, depth: element.depth, children: children, alpha: self.data.baseAlpha, translate_matrix: translate_matrix } // TEST TREE if (self.data.treeLayout) { figure.treeMetaphorHeight = element.treeMetaphorHeight } // Gradient color let hierarchyLevel = figure.name.split("/").length - 1 figure.hierarchyLevel = hierarchyLevel figure.renderOrder = hierarchyLevel if (self.data.gradientBaseColor) { figure.color = greenColorsurface(Math.round(self.normalizeValues(1, self.babiaMetadata['maxLevels'], 15, 100, hierarchyLevel))); } else { figure.color = self.data.base_color; } } else { if (this.data.area) { figure = { id: "boat-" + elements[i][this.data.field], name: (elements[i].name) ? elements[i].name : elements[i][this.data.field], posX: posX, posY: posY, width: Math.sqrt(element.area), height: element.height, alpha: self.data.buildingAlpha, depth: Math.sqrt(element.area) } } else { figure = { id: "boat-" + elements[i][this.data.field], name: (elements[i].name) ? elements[i].name : elements[i][this.data.field], posX: posX, posY: posY, width: element.width, height: element.height, alpha: self.data.buildingAlpha, depth: element.depth } } if (typeof elements[i][self.data.color] === 'number') { figure.color = heatMapColorforValue(elements[i][self.data.color], self.babiaMetadata['color_max'], self.babiaMetadata['color_min']) } else if (typeof elements[i][self.data.color] === 'string') { // Categoric color if (elements[i][self.data.color] in self.categoricColorMaps) { figure.color = self.categoricColorMaps[elements[i][self.data.color]] } else { self.categoricColorMaps[elements[i][self.data.color]] = colorsArray[self.categoricColorIndex] figure.color = colorsArray[self.categoricColorIndex] self.categoricColorIndex = self.categoricColorIndex + 1 if (self.categoricColorIndex >= colorsArray.length) { self.categoricColorIndex = 0 } } } } figure.rawData = elements[i] // If transparency by a field on buildings // Put transparent 80% to those buildings that share same value for a field selected if (self.data.transparent80ByRepeatedField && (!figure.children && self.idsToNotRepeat80Transparent.includes(figure.rawData[self.data.transparent80ByRepeatedField]))) { figure.alpha = 0.8 figure.renderOrder = 2000 } else { self.idsToNotRepeat80Transparent.push(figure.rawData[self.data.transparent80ByRepeatedField]) } // Put transparent 20% to those buildings that share same value for a field selected if (self.data.transparent20ByRepeatedField && (!figure.children && self.idsToNotRepeat20Transparent.includes(figure.rawData[self.data.transparent20ByRepeatedField]))) { figure.alpha = 0.45 figure.renderOrder = 2000 } else { self.idsToNotRepeat20Transparent.push(figure.rawData[self.data.transparent20ByRepeatedField]) } // Put wireframe to those buildings that share same value for a field selected if (self.data.wireframeByRepeatedField && (!figure.children && self.idsToNotRepeatWireframe.includes(figure.rawData[self.data.wireframeByRepeatedField]))) { figure.wireframe = true } else { figure.wireframe = false self.idsToNotRepeatWireframe.push(figure.rawData[self.data.wireframeByRepeatedField]) } figures.push(figure); } // Check and update last limits if (max_down < limit_down) { limit_down = max_down; } if (max_left < limit_left) { limit_left = max_left; } if (max_up > limit_up) { limit_up = max_up; } if (max_right > limit_right) { limit_right = max_right; } if (current_vertical < limit_left) { limit_left = current_vertical + this.data.building_separation / 2; } if (current_vertical > limit_right) { limit_right = current_vertical - this.data.building_separation / 2;; } if (current_horizontal > limit_up) { limit_up = current_horizontal - this.data.building_separation / 2;; } if (current_horizontal < limit_down) { limit_down = current_horizontal + this.data.building_separation / 2;; } // Calculate translate of the center, width and depth of the zone var width = Math.abs(limit_left) + Math.abs(limit_right); var depth = Math.abs(limit_down) + Math.abs(limit_up); width += 2 * increment; depth += 2 * increment; var translate_x = limit_left + width / 2 - increment; var translate_z = limit_down + depth / 2 - increment; translate = { x: translate_x, y: 0, z: translate_z, }; return [width, depth, translate, figures]; }, // If wireframerepeated activated idsToNotRepeatWireframe: [], idsToNotRepeat80Transparent: [], idsToNotRepeat20Transparent: [], drawElements: function (element, figures, translate) { const self = this for (let i in figures) { let height = figures[i].height; let x = figures[i].posX; let y = figures[i].posY; let position = { x: x - translate.x, y: (height / 2 + translate.y / 2), z: -y + translate.z } // TEST TREE if (self.data.treeLayout) { // // Hide quarters that has not children if (self.data.treeHideOneSonQuarters && (figures[i].children && figures[i].children.length === 1)) { figures[i].alphaTest = 1 figures[i].dontAddEvents = true figures[i].children[0].dontAddLine = true } if (self.data.treeFixQuarterHeight) { // Fix position y for quarters // Check if not undefined because if it is 0, it returns false (thx JS) if (figures[i].treeMetaphorHeight !== undefined) { position.y = (height / 2 + translate.y / 2) + self.data.treeQuartersLevelHeight } else { position.y = ((height / 2 + translate.y / 2)) - self.data.treeQuartersLevelHeight - self.data.zone_elevation if (figures[i].height < self.data.treeQuartersLevelHeight) { // If flag to hide activated and the parent has only one son, not needed to draw the line if (!(self.data.treeHideOneSonQuarters && figures[i].dontAddLine)) { // First get top let topBuildingY = position.y // Get where the quarter is let quarterPosY = position.y + (self.data.treeQuartersLevelHeight - (height / 2)) // Draw the line in the space let line = document.createElement('a-entity') line.setAttribute('class', 'babiaboatstreelines') line.setAttribute('line', { start: { x: position.x, y: topBuildingY, z: position.z }, end: { x: position.x, y: quarterPosY, z: position.z }, color: 'yellow' }) element.appendChild(line) } } } } else { // Quarters on top of the buildings if (figures[i].treeMetaphorHeight !== undefined) { position.y = ((self.normalizeValues(self.babiaMetadata['heightMin'], self.babiaMetadata['heightMax'], self.data.minBuildingHeight, self.data.maxBuildingHeight, figures[i].treeMetaphorHeight))) } else { position.y = ((height / 2 + translate.y / 2)) - height - 0.001 } } } let entity = this.createElement(figures[i], position); this.addEvents(entity, figures[i]); element.appendChild(entity); } }, RightSide: function (element, limit_right, current_horizontal, max_right) { let separation = parseFloat(this.data.building_separation); let width, depth; if (this.data.area && !element.children) { width = Math.sqrt(element.area); depth = Math.sqrt(element.area) + separation; } else { width = parseFloat(element.width); depth = parseFloat(element.depth) + separation; } // Calculate position let posX = limit_right + (width / 2) + separation; let posY = current_horizontal - (depth / 2); // Calculate states current_horizontal -= depth; let total_x = limit_right + width + separation; if (total_x > max_right) { max_right = total_x; } return [current_horizontal, posX, posY, max_right]; }, DownSide: function (element, limit_down, current_vertical, max_down) { let separation = parseFloat(this.data.building_separation); let width, depth; if (this.data.area && !element.children) { width = Math.sqrt(element.area) + separation; depth = Math.sqrt(element.area); } else { width = parseFloat(element.width) + separation; depth = parseFloat(element.depth); } // Calculate position let posX = current_vertical - (width / 2); let posY = limit_down - (depth / 2) - separation; // Calculate state current_vertical -= width; let total_y = limit_down - depth - separation; if (total_y < max_down) { max_down = total_y; } return [current_vertical, posX, posY, max_down]; }, LeftSide: function (element, limit_left, current_horizontal, max_left) { let separation = parseFloat(this.data.building_separation); let width, depth; if (this.data.area && !element.children) { width = Math.sqrt(element.area); depth = Math.sqrt(element.area) + separation; } else { width = parseFloat(element.width); depth = parseFloat(element.depth) + separation; } // Calculate position let posX = limit_left - (width / 2) - separation; let posY = current_horizontal + (depth / 2); // Calculate state current_horizontal += depth; let total_x = limit_left - width - separation; if (total_x < max_left) { max_left = total_x; } return [current_horizontal, posX, posY, max_left]; }, UpSide: function (element, limit_up, current_vertical, max_up) { let separation = parseFloat(this.data.building_separation); let width, depth; if (this.data.area && !element.children) { width = Math.sqrt(element.area) + separation; depth = Math.sqrt(element.area); } else { width = parseFloat(element.width) + separation; depth = parseFloat(element.depth); } // Calculate position let posX = current_vertical + (width / 2); let posY = limit_up + (depth / 2) + separation; // Calculate state current_vertical += width; let total_y = limit_up + depth + separation; if (total_y > max_up) { max_up = total_y; } return [current_vertical, posX, posY, max_up]; }, Animation: function (element, figures, figures_old, delta, translate, translate_old) { let self = this let new_time = Date.now(); let entity; // First, remove all the legends that are active self.legendsActive.forEach(legend => { legend.remove() }); self.legendsActive = [] for (let i in figures) { if (document.getElementById(figures[i].id)) { // If exists entity = document.getElementById(figures[i].id); // Creating... (next ticks) if (figures[i].inserted) { //TODO: This code increments the opacity in the animation part, but it adds several performance issues //Increment opacity // let opa_inc = delta / this.duration; // let opacity = parseFloat(entity.getAttribute('material').opacity); // if (opacity + opa_inc < 1) { // opacity += opa_inc; // } else { // opacity = 1.0; // figures[i].inserted = false; // } //setOpacity(entity, opacity); // If animation stops before finish let cond = 'id=' + figures[i].id if (findIndex(self.figures_in, cond) < 0) { self.figures_in.push(figures[i]) } } else { // find index in old_figures let cond = 'id=' + figures[i].id let index = findIndex(figures_old, cond) if (index < 0) { // encontrar el elemento que no esta en la escena con id y darle opacidad // y cambiar sus propiedades a la nueva si es necesario figures[i].inserted = true; if (entity.getAttribute('width') != figures[i].width) { entity.setAttribute('width', figures[i].width); } if (entity.getAttribute('height') != figures[i].height) { entity.setAttribute('height', figures[i].height); } if (entity.getAttribute('depth') != figures[i].depth) { entity.setAttribute('depth', figures[i].depth); } if (entity.getAttribute('color') != figures[i].color) { entity.setAttribute('color', figures[i].color); } if (entity.getAttribute('material').wireframe != figures[i].wireframe) { entity.setAttribute('material', 'wireframe', figures[i].wireframe); entity.setAttribute('material', 'wireframeLinewidth', 0.1); } } else { // RESIZE this.resize(entity, new_time, delta, figures[i], figures_old[index]); // TRASLATE this.traslate(entity, new_time, delta, figures[i], figures_old[index], translate, translate_old); // COLOR if (entity.getAttribute('color') != figures[i].color && !self.inEntitiesWithLegend(entity)) { entity.setAttribute('color', figures[i].color); } if (entity.getAttribute('material').wireframe != figures[i].wireframe) { entity.setAttribute('material', 'wireframe', figures[i].wireframe); entity.setAttribute('material', 'wireframeLinewidth', 0.1); } if (entity.getAttribute('material').opacity != figures[i].alpha) { entity.setAttribute('material', 'opacity', figures[i].alpha); entity.object3D.renderOrder = figures[i].renderOrder } if (figures[i].children) { this.Animation(entity, figures[i].children, figures_old[index].children, delta, figures[i].translate_matrix, figures_old[index].translate_matrix); } else { // Building color if changed, force to the new one if (figures[i].rawData[self.data.color] !== figures_old[index].rawData[self.data.color]) { // Color numeric or categoric let color if (typeof figures[i].rawData[self.data.color] === 'number') { color = heatMapColorforValue(figures[i].rawData[self.data.color], self.babiaMetadata['color_max'], self.babiaMetadata['color_min']) } else if (typeof figures[i].rawData[self.data.color] === 'string') { // Categoric color if (figures[i].rawData[self.data.color] in self.categoricColorMaps) { color = self.categoricColorMaps[figures[i].rawData[self.data.color]] } else { self.categoricColorMaps[figures[i].rawData[self.data.color]] = colorsArray[self.categoricColorIndex] color = colorsArray[self.categoricColorIndex] self.categoricColorIndex = self.categoricColorIndex + 1 if (self.categoricColorIndex >= colorsArray.length) { self.categoricColorIndex = 0 } } } let oldColor = entity.getAttribute('color') if (color !== oldColor) { entity.setAttribute('color', color) } } } } } } else { // CREATE NEW position = { x: figures[i].posX - translate.x, y: (figures[i].height / 2 + translate.y / 2), z: -figures[i].posY + translate.z } let new_entity = this.createElement(figures[i], position); this.addEvents(new_entity, figures[i]); //TODO: bad perfomance when Opacity 0 at the beginning //setOpacity(new_entity, 0); // New building and quarter, it appears with 0.5 opacity setOpacity(new_entity, 0.5); element.appendChild(new_entity); figures[i].inserted = true; } } // Delete figures let opa_dec = delta / this.duration; for (let i in figures_old) { let deleted = false; let cond = 'id=' + figures_old[i].id let index = findIndex(figures, cond) if (index < 0) { if (findIndex(self.figures_del, cond) < 0) { self.figures_del.push(figures_old[i]) } let entity_del = document.getElementById(figures_old[i].id); if (entity_del) { //TODO: Bad perfomance issue when changing dinamically the opacity, better to just make it non vissible entity_del.object3D.visible = false entity_del.remove(); // let opacity = parseFloat(entity_del.components.material.opacity); // if (opacity - opa_dec > 0) { // opacity -= opa_dec; // } else { // opacity = 0.0; // deleted = true; // self.figures_del.pop(figures_old[i]); // } // setOpacity(entity_del, opacity); // if (deleted) { // entity_del.remove(); // } } } } }, inEntitiesWithLegend(obj) { let i; for (i = 0; i < this.entitiesWithLegend.length; i++) { if (this.entitiesWithLegend[i].entity === obj) { return true; } } return false; }, setFigures: function (figures, translate) { const self = this figures.forEach(figure => { let entity = document.getElementById(figure.id); if (entity) { if (entity.getAttribute('width') != figure.width) { entity.setAttribute('width', figure.width); } if (entity.getAttribute('height') != figure.height) { entity.setAttribute('height', figure.height); } if (entity.getAttribute('depth') != figure.depth) { entity.setAttribute('depth', figure.depth); } // If legend active, don't force change color, put it as the color before if (self.inEntitiesWithLegend(entity)) { entity.setAttribute('babiaxrFirstColor', figure.color) } else { if (entity.getAttribute('color') != figure.color) { entity.setAttribute('color', figure.color); } } //TODO: Full opacity because it was in 0.5 if new building/quarter if (entity.components.material.data.opacity < 1) { setOpacity(entity, 1) } // Quarters to alpha if tree fix position if (entity.getAttribute('material').opacity != figure.alpha) { entity.setAttribute('material', 'opacity', figure.alpha); } // TEST TREE if (self.data.treeLayout) { // Hide quarters that has not children if (self.data.treeHideOneSonQuarters && (figure.children && figure.children.length === 1)) { figure.alphaTest = 1 figure.dontAddEvents = true figure.children[0].dontAddLine = true } if (self.data.treeFixQuarterHeight) { // Fix position y for quarters if (figure.treeMetaphorHeight !== undefined) { entity.object3D.position.set( figure.posX - translate.x, (figure.height / 2 + translate.y / 2) + self.data.treeQuartersLevelHeight, (- figure.posY + translate.z), ) } else { let positionYFixed = ((figure.height / 2 + translate.y / 2)) - self.data.treeQuartersLevelHeight - self.data.zone_elevation let positionX = figure.posX - translate.x let positionZ = (- figure.posY + translate.z) entity.object3D.position.set( positionX, positionYFixed, positionZ, ) // DRAW LINE WHEN DOES NOT ACHIEVE THE QUARTER (TOO LOW) if (figure.height < self.data.treeQuartersLevelHeight) { // If flag to hide activated and the parent has only one son, not needed to draw the line if (!(self.data.treeHideOneSonQuarters && figure.dontAddLine)) { // First get top let topBuildingY = positionYFixed // Get where the quarter is let quarterPosY = positionYFixed + (self.treeQuartersLevelHeight - figure.height / 2) // Draw the line in the space let line = document.createElement('a-entity') line.setAttribute('class', 'babiaboatstreelines') line.setAttribute('line', { start: { x: positionX, y: topBuildingY, z: positionZ }, end: { x: positionX, y: quarterPosY, z: positionZ }, color: 'yellow' }) entity.parentElement.appendChild(line) } } } } else { if (figure.treeMetaphorHeight !== undefined) { entity.object3D.position.set( figure.posX - translate.x, ((self.normalizeValues(self.babiaMetadata['heightMin'], self.babiaMetadata['heightMax'], self.data.minBuildingHeight, self.data.maxBuildingHeight, figure.treeMetaphorHeight))), (- figure.posY + translate.z), ) } else { entity.object3D.position.set( figure.posX - translate.x, ((figure.height / 2 + translate.y / 2)) - figure.height - 0.001, (- figure.posY + translate.z), ) } } } else { // Lo de no tree, lo que estaba antes entity.object3D.position.set( figure.posX - translate.x, ((parseFloat(figure.height) + translate.y) / 2), (- figure.posY + translate.z), ) } if (figure.children) { this.setFigures(figure.children, figure.translate_matrix); } } }); }, resize: function (entity, new_time, delta, figure, figure_old) { const self = this if (((new_time - this.start_time) < this.duration) && ((figure.width != figure_old.width) || (figure.height != figure_old.height) || (figure.depth != figure_old.depth))) { // Calulate increment let diff_width = Math.abs(figure.width - figure_old.width); let diff_height = Math.abs(figure.height - figure_old.height); let diff_depth = Math.abs(figure.depth - figure_old.depth); let inc_width = (delta * diff_width) / this.duration; let inc_height = (delta * diff_height) / this.duration; let inc_depth = (delta * diff_depth) / this.duration; let last_width = parseFloat(entity.getAttribute('width')); let last_height = parseFloat(entity.getAttribute('height')); let last_depth = parseFloat(entity.getAttribute('depth')); let new_width; if (figure.width - figure_old.width < 0) { new_width = last_width - inc_width; } else { new_width = last_width + inc_width; } let new_height; if (figure.height - figure_old.height < 0) { new_height = last_height - inc_height; } else { new_height = last_height + inc_height; } let new_depth; if (figure.depth - figure_old.depth < 0) { new_depth = last_depth - inc_depth; } else { new_depth = last_depth + inc_depth; } // Update size entity.setAttribute('width', new_width); entity.setAttribute('height', new_height); entity.setAttribute('depth', new_depth); entity.babiaRawData = figure.rawData //Check if has transparent box as a quarter if (entity.classList.contains('babiaquarterboxactivated')) { entity.childNodes.forEach(child => { if (child.classList.contains('babiaquarterlegendbox')) { child.setAttribute('geometry', 'width', new_width); child.setAttribute('geometry', 'depth', new_depth); child.setAttribute('material', 'opacity', 0.4); } }); } } else if (((new_time - this.start_time) > this.duration) && ((figure.width != figure_old.width) || (figure.height != figure_old.height) || (figure.depth != figure_old.depth))) { entity.setAttribute('width', figure.width); entity.setAttribute('height', figure.height); entity.setAttribute('depth', figure.depth); entity.babiaRawData = figure.rawData //Check if has transparent box as a quarter if (entity.classList.contains('babiaquarterboxactivated')) { entity.childNodes.forEach(child => { if (child.classList.contains('babiaquarterlegendbox')) { child.setAttribute('geometry', 'width', new_width); child.setAttribute('geometry', 'depth', new_depth); child.setAttribute('material', 'opacity', 0.4); } }); } } }, traslate: function (entity, new_time, delta, figure, figure_old, translate, translate_old) { let dist_x = (figure_old.posX - translate_old.x) - (figure.posX - translate.x); let dist_y = (figure.height - figure_old.height); let dist_z = (figure_old.posY - translate_old.z) - (figure.posY - translate.z); if (dist_x != 0 || dist_y != 0 || dist_z != 0) { if ((new_time - this.start_time) < this.duration) { // Calculate increment positions let inc_x = (delta * dist_x) / this.duration; let inc_y = (delta * dist_y) / (2 * this.duration); let inc_z = (delta * dist_z) / this.duration; let last_x = parseFloat(entity.object3D.position.x); let last_y = parseFloat(entity.object3D.position.y); let last_z = parseFloat(entity.object3D.position.z); let new_x = last_x - inc_x; let new_y = last_y + inc_y; let new_z = last_z + inc_z; // Update entity entity.object3D.position.set( new_x, new_y, new_z ) } else if ((new_time - this.start_time) > this.duration) { entity.object3D.position.set( (figure.posX - translate.x), ((parseFloat(figure.height) + translate.y) / 2), (- figure.posY + translate.z), ) } } }, createElement: function (figure, pos