import * as ECS from '../../../packages/ecs-js/index'
import {x} from '../../xapp/xworld'
import {XError} from '../../xutils/xcommon'
import XSys from '../../sys/xsys'
import {MorphingAnim} from '../../sys/tween/animizer'
import {CoordsGrid} from '../../xmath/chartgrid'
import {vec3} from '../../xmath/vec'
import xgeom from '../../xmath/geom'
import {Obj3Type} from '../../component/obj3'
import {ElemType, GridValue, Bar} from '../../component/ext/chart'
import {AssetType, ShaderFlag} from '../../component/visual'
/**
* Subsystem rendering 3d bar chart
*
* @class XBar
*/
export default class XBar extends XSys {
/**
* create bar chart objects
* @param {ECS} ecs
* @param {object} options
* options.chart: the json chart section defining chart grid space, {domain, range, grid, grid-space}
* @param {object} jbar the json bar section definint bar chart.<br>
* @param {array} vectors the high dimensional vectors.<br>
* assumes the last dimension as the y scale value
* @constructor XSankey
*/
constructor(ecs, options, jbar, vectors) {
super(ecs);
ecs.registerComponent('Bar', Bar);
ecs.registerComponent('GridValue', GridValue);
if (!x.chart || !x.chart.grid) {
this.grid = new CoordsGrid(json.chart);
x.chart = Object.assign(x.chart || new Object(), {grid: this.grid});
}
else this.grid = x.chart.grid;
this.bars(ecs, this.grid, jbar, vectors, options);
}
bars(ecs, grid, chart, vectors, options = {}) {
var vecs = vectors;
if (!chart || !vecs || !Array.isArray(vecs))
throw new XError("XBar(chart, vectors): invalid data: ", chart, vecs);
var dok; // docking
if (chart.serials && chart.serials.docking && chart.serials.docking.length > 0)
dok = chart.serials.docking;
var offset = chart.serials ? chart.serials.pos0 || [1, 0, 1] : [1, 0, 1];
var geom = chart.Obj3.geom === undefined ?
Obj3Type.Cylinder : eval(chart.Obj3.geom);
var asset = chart.texture || options.texture;
var animSeqs = [];
animSeqs.push( [{
mtype: xv.XComponent.AnimType.U_MORPHi,
paras: {start: 5,
duration: 0.71,
uniforms: {
u_morph0: [0, 1],
u_morph1: [0, 0],
u_morph2: [0, 0]
},
ease: xv.XEasing.Elastic.Elastic} }] );
animSeqs.push( [{
mtype: xv.XComponent.AnimType.U_MORPHi,
paras: {start: Infinity,
duration: 0.62,
uniforms: {
u_morph0: [0, 0],
u_morph1: [0, 1],
u_morph2: [0, 0]
},
ease: undefined} }] );
animSeqs.push( [{
mtype: xv.XComponent.AnimType.U_MORPHi,
paras: {start: Infinity,
duration: 0.55,
uniforms: {
u_morph0: [0, 0],
u_morph1: [0, 0],
u_morph2: [0, 1] },
ease: undefined} }] );
this.vecs = new Array(vecs.length);
for (var brx = 0; brx < vecs.length; brx++) {
var bar = vecs[brx];
var h = grid.barHeight(bar[2]);
// init pos: x, y, z=0
// x, y, z = bar[0], 0, bar[1]
// x, y, z = offset[0], offset[1], offset[2]
// var pos0 = grid.worldPos( [bar[0] + offset[0], offset[1], bar[1] + offset[2]] );
var pos0;
var gridx; // position of bar's value lines
if (dok) {
pos0 = grid.worldPos( undefined, dok[brx], offset );
gridx = [dok[brx][0] + offset[0], dok[brx][1] + offset[1], dok[brx][2] + offset[2]];
}
else {
pos0 = grid.worldPos( undefined, [bar[0], 0, bar[1]], offset );
gridx = [bar[0] + offset[0], offset[1], bar[1] + offset[2]];
}
pos0[1] += h * 0.5;
// Cylinder:
// radiusTop, radiusBottom, height, radialSegments, heightSegments, openEnded, thetaStart, thetaLength
// Box: x, y, z
var box = [...chart.Obj3.box]; // copy
for (var xl = 3; xl < box.length; xl++ )
box[xl] = eval(box[xl]);
box = XBar.threeGeomParas(grid, geom, box, h);
var bar = ecs.createEntity({
id: barUId(),
Obj3: { geom,
// transform: [{translate: [-1 * scl * 4, y11, 0]}],
transform: [{ translate: pos0 }],
box,
uniforms: {opacity: 1.0}},
Visual:{vtype: AssetType.mesh,
shader: xv.XComponent.ShaderFlag.colorArray,
// colors' morphing weight variable name: u_morph0, u_morph1, ...
paras:{ colors:[[1, 0, 0],
[0, 0, 1],
[0, 1, 0]] },
asset},
Bar: { vecIx: brx },
// GridValue: {gridx: [bar[0] + offset[0], offset[1], bar[1] + offset[2]],
GridValue: {gridx,
val: bar[2]},
GpuPickable: {},
ModelSeqs: { script: animSeqs },
CmpTweens: {}
});
}
this.serials(ecs, grid, chart.serials, options);
}
/**
* Create label serials.
* @param {ECS} ecs
* @param {CoordsGrid} grid
* @param {object} serials labels configuration, e.g.<pre>
{
"pos0": [1, 0, 1],
"label-offsetssss": [[0, -0.1, 0], [-0.1, 0.5, -0.1], [0, -0.1, 0]],
"label-desc": "in bar serials, labels always a 2d array of stirngs, with rows' cardinality of 3",
"labels": [ ["A", "B", "C"],
["X"],
["- I -", "- II -", "- III -"] ],
"label-colors": ["red", "green", "#24f"],
"label-font": "4em Arial",
"label-canvas": {"x": 24, "y": 144, "w": 256, "h": 256},
"label-box": [20, 20],
...
}</pre>
* @param {object.<{font}>} options
* @member XBar#serials
* @function
*/
serials(ecs, grid, serials, options) {
// var serials = chart.serials;
var off = serials.pos0;
var offsets = serials["label-offsets"];
var l = serials.labels;
if (!l || l.length === 0) return;
var colors = serials["label-colors"] || ['white', 'white', 'white'];
var boxes = serials["label-boxes"];
var bg_color = serials["label-bg"] || '#003';
// label at [x, 0, 0]
var posx = grid.lerposes( [l[0].length, 1, 1],
[off[0], 0, 0],
offsets ? offsets[0] : undefined );
var xywh = boxes && boxes[0] ? boxes[0]
: serials["label-box"] || {x: 0, y: 0, w: 64, y: 24, size: 16};
for (var x = 0; x < posx[0][0].length; x++) {
createLabels(ecs, l[0][x], posx[0][0][x], {
xywh, bg_color,
style: colors[0],
font: options.font });
}
// label at [0, y, 0]
var posy = grid.lerposes( [1, l[1].length, 1],
[0, off[1], 0],
offsets ? offsets[1] : undefined );
xywh = boxes && boxes[1] ? boxes[1]
: serials["label-box"] || {x: 0, y: 0, w: 64, y: 24, size: 16};
for (var y = 0; y < posy.length; y++) {
createLabels(ecs, l[1][y], posy[y][0][0], {
xywh, bg_color,
style: colors[1],
font: options.font });
}
// label at [0, 0, z]
var posz = grid.lerposes( [1, 1, l[2].length],
[0, 0, off[2]],
offsets ? offsets[2] : undefined );
xywh = boxes && boxes[2] ? boxes[2]
: serials["label-box"] || {x: 0, y: 0, w: 64, y: 24, size: 16};
for (var z = 0; z < posz[0].length; z++) {
createLabels(ecs, l[2][z], posz[0][z][0], {
xywh, bg_color,
style: colors[2],
font: options.font });
}
/**
* Create label entity.
* @param {ECS} ecs
* @param {string} lb label text
* @param {object} pos position in world
* @param {object.<{font}>} options
* @return {Entity} label entity
* @member XBar#createLabels
* @function
*/
function createLabels(ecs, lb, pos, options) {
var xywh = options.xywh;
var box = [xywh.w + ( xywh.margin || 0 ) * 2, xywh.h + ( xywh.margin || 0 ) * 2];
var lablx = ecs.createEntity({
id: `lb-${pos}`,
Obj3: { geom: xv.XComponent.Obj3Type.PLANE,
box,
transform: [ {translate: pos}, {scale: [1, 1, 1]} ],
mesh: undefined },
Visual:{vtype: xv.AssetType.mesh, // makes GpuPickable working
shader: xv.ShaderFlag.discard // Dynatex is actually a child
},
Dynatex: {text: lb,
xywh, //: {x: 0, y: 0, w: 24, size: 32},
dirty: true,
'bg-color': options.bg_color,
'v-align': 'middle',
font: options.font,
style: options.style
},
GpuPickable: {},
GridElem: {etype: ElemType.text}
});
return lablx;
}
}
/**Update x.chart.valuePos
* @param {int} tick
* @param {array<Entity>} entites
* @member XBar#update
* @function
*/
update(tick, entities) { }
///////////////////////////////////////////////////////////////////////////
// helpers
//
/**Get bars' geometry paras (box) for different geometries.
* @param {Obj3Type} geom
* @param {array} box [number] parameters
* @param {number} valHeight bar, cylinder height
* @return {array} parsed geometry paras.
* @member XBar.threeGeomParas
* @function
*/
static threeGeomParas(grid, geom, box, valHeight) {
switch (geom) {
case Obj3Type.BOX:
box[0] = grid.space(box[0]);
box[1] = valHeight;
box[2] = grid.space(box[2]);
break;
case Obj3Type.Cylinder:
box[0] = grid.space(box[0]);
box[1] = grid.space(box[1]);
box[2] = valHeight;
break;
default:
throw new XError(`Geometry not supported by XBar: ${geom}`);
}
return box;
}
}
/**
* @property {object.<{iffall: array}>} query - query condition: {iffall: ['Bar']}
* @member XBar#query
*/
XBar.query = {iffall: ['Bar']};
/**For generating XBar element uuid.
* @memberof XBar
*/
var baruuid = 0;
/**Get a uuid.
* @memberof XBar */
function barUId() {
return `br-${++baruuid}`;
}