/**
* @class Ext.chart.sprite.Bar3D
* @extends Ext.draw.sprite.Sprite
*
* A sprite that represents a 3D bar or column.
* Used as an item template by the {@link Ext.chart.series.sprite.Bar3D} marker holder.
*
*/
Ext.define('Ext.chart.sprite.Bar3D', {
extend: 'Ext.draw.sprite.Sprite',
alias: 'sprite.bar3d',
type: 'bar3d',
inheritableStatics: {
def: {
processors: {
/**
* @cfg {Number} [x=0]
* The position of the sprite on the x-axis.
* Corresponds to the center of the front face of the box.
*/
x: 'number',
/**
* @cfg {Number} [y=0]
* The position of the sprite on the y-axis.
* Corresponds to the top of the front face of the box.
*/
y: 'number',
/**
* @cfg {Number} [width=8] The width of the box.
*/
width: 'number',
/**
* @cfg {Number} [height=8] The height of the box.
*/
height: 'number',
/**
* @cfg {Number} [depth=8] The depth of the box.
*/
depth: 'number',
/**
* @cfg {String} [orientation='vertical'] The orientation of the box.
*/
orientation: 'enums(vertical,horizontal)',
/**
* @cfg {Boolean} [showStroke=false]
* Whether to render the stroke or not.
*/
showStroke: 'bool',
/**
* @cfg {Number} [saturationFactor=1]
* The factor applied to the saturation of the box.
*/
saturationFactor: 'number',
/**
* @cfg {Number} [brightnessFactor=1]
* The factor applied to the brightness of the box.
*/
brightnessFactor: 'number',
/**
* @cfg {Number} [colorSpread=1]
* An attribute used to control how flat the bar gradient looks.
* A value of 0 essentially means no gradient (flat color).
*/
colorSpread: 'number'
},
triggers: {
x: 'bbox',
y: 'bbox',
width: 'bbox',
height: 'bbox',
depth: 'bbox',
orientation: 'bbox'
},
defaults: {
x: 0,
y: 0,
width: 8,
height: 8,
depth: 8,
orientation: 'vertical',
showStroke: false,
saturationFactor: 1,
brightnessFactor: 1,
colorSpread: 1,
lineJoin: 'bevel'
}
}
},
constructor: function(config) {
this.callParent([config]);
this.topGradient = new Ext.draw.gradient.Linear({});
this.rightGradient = new Ext.draw.gradient.Linear({});
this.frontGradient = new Ext.draw.gradient.Linear({});
},
updatePlainBBox: function(plain) {
var attr = this.attr,
x = attr.x,
y = attr.y,
width = attr.width,
height = attr.height,
depth = attr.depth;
plain.x = x - width * 0.5;
plain.width = width + depth;
if (height > 0) {
plain.y = y;
plain.height = height + depth;
}
else {
plain.y = y + depth;
plain.height = height - depth;
}
},
render: function(surface, ctx) {
var me = this,
attr = me.attr,
center = attr.x,
top = attr.y,
bottom = top + attr.height,
isNegative = top < bottom,
halfWidth = attr.width * 0.5,
depth = attr.depth,
isHorizontal = attr.orientation === 'horizontal',
isTransparent = attr.globalAlpha < 1,
fillStyle = attr.fillStyle,
color = Ext.util.Color.create(
fillStyle.isGradient
? fillStyle.getStops()[0].color
: fillStyle
),
saturationFactor = attr.saturationFactor,
brightnessFactor = attr.brightnessFactor,
colorSpread = attr.colorSpread,
hsv = color.getHSV(),
bbox = {},
roundX, roundY,
temp;
if (!attr.showStroke) {
ctx.strokeStyle = Ext.util.Color.RGBA_NONE;
}
if (isNegative) {
temp = top;
top = bottom;
bottom = temp;
}
// Refresh gradients based on sprite's fillStyle and other attributes.
me.topGradient.setDegrees(isHorizontal ? 0 : 80);
me.topGradient.setStops([
{
offset: 0,
color: Ext.util.Color.fromHSV(
hsv[0],
Ext.Number.constrain(hsv[1] * saturationFactor, 0, 1),
Ext.Number.constrain((0.5 + colorSpread * 0.10) * brightnessFactor, 0, 1)
)
},
{
offset: 1,
color: Ext.util.Color.fromHSV(
hsv[0],
Ext.Number.constrain(hsv[1] * saturationFactor, 0, 1),
Ext.Number.constrain((0.5 - colorSpread * 0.11) * brightnessFactor, 0, 1)
)
}
]);
me.rightGradient.setDegrees(isHorizontal ? 45 : 90);
me.rightGradient.setStops([
{
offset: 0,
color: Ext.util.Color.fromHSV(
hsv[0],
Ext.Number.constrain(hsv[1] * saturationFactor, 0, 1),
Ext.Number.constrain((0.5 - colorSpread * 0.14) * brightnessFactor, 0, 1)
)
},
{
offset: 1,
color: Ext.util.Color.fromHSV(
hsv[0],
Ext.Number.constrain(hsv[1] * (1.0 + colorSpread * 0.4) * saturationFactor,
0, 1),
Ext.Number.constrain((0.5 - colorSpread * 0.32) * brightnessFactor, 0, 1)
)
}
]);
if (isHorizontal) {
// 0° angle looks like 90° angle because the chart is flipped
me.frontGradient.setDegrees(0);
}
else {
me.frontGradient.setRadians(Math.atan2(top - bottom, halfWidth * 2));
}
me.frontGradient.setStops([
{
offset: 0,
color: Ext.util.Color.fromHSV(
hsv[0],
Ext.Number.constrain(hsv[1] * (1.0 - colorSpread * 0.1) * saturationFactor,
0, 1),
Ext.Number.constrain((0.5 + colorSpread * 0.1) * brightnessFactor, 0, 1)
)
},
{
offset: 1,
color: Ext.util.Color.fromHSV(
hsv[0],
Ext.Number.constrain(hsv[1] * (1.0 + colorSpread * 0.1) * saturationFactor,
0, 1),
Ext.Number.constrain((0.5 - colorSpread * 0.23) * brightnessFactor, 0, 1)
)
}
]);
if (isTransparent || isNegative) {
// Bottom side.
ctx.beginPath();
ctx.moveTo(center - halfWidth, bottom);
ctx.lineTo(center - halfWidth + depth, bottom + depth);
ctx.lineTo(center + halfWidth + depth, bottom + depth);
ctx.lineTo(center + halfWidth, bottom);
ctx.closePath();
bbox.x = center - halfWidth;
bbox.y = top;
bbox.width = halfWidth + depth;
bbox.height = depth;
// eslint-disable-next-line max-len
ctx.fillStyle = (isHorizontal ? me.rightGradient : me.topGradient).generateGradient(ctx, bbox);
ctx.fillStroke(attr);
}
if (isTransparent) {
// Left side.
ctx.beginPath();
ctx.moveTo(center - halfWidth, top);
ctx.lineTo(center - halfWidth + depth, top + depth);
ctx.lineTo(center - halfWidth + depth, bottom + depth);
ctx.lineTo(center - halfWidth, bottom);
ctx.closePath();
bbox.x = center + halfWidth;
bbox.y = bottom;
bbox.width = depth;
bbox.height = top + depth - bottom;
// eslint-disable-next-line max-len
ctx.fillStyle = (isHorizontal ? me.topGradient : me.rightGradient).generateGradient(ctx, bbox);
ctx.fillStroke(attr);
}
// Top side.
roundY = surface.roundPixel(top);
ctx.beginPath();
ctx.moveTo(center - halfWidth, roundY);
ctx.lineTo(center - halfWidth + depth, top + depth);
ctx.lineTo(center + halfWidth + depth, top + depth);
ctx.lineTo(center + halfWidth, roundY);
ctx.closePath();
bbox.x = center - halfWidth;
bbox.y = top;
bbox.width = halfWidth + depth;
bbox.height = depth;
// eslint-disable-next-line max-len
ctx.fillStyle = (isHorizontal ? me.rightGradient : me.topGradient).generateGradient(ctx, bbox);
ctx.fillStroke(attr);
// Right side.
roundX = surface.roundPixel(center + halfWidth);
ctx.beginPath();
ctx.moveTo(roundX, surface.roundPixel(top));
ctx.lineTo(center + halfWidth + depth, top + depth);
ctx.lineTo(center + halfWidth + depth, bottom + depth);
ctx.lineTo(roundX, bottom);
ctx.closePath();
bbox.x = center + halfWidth;
bbox.y = bottom;
bbox.width = depth;
bbox.height = top + depth - bottom;
// eslint-disable-next-line max-len
ctx.fillStyle = (isHorizontal ? me.topGradient : me.rightGradient).generateGradient(ctx, bbox);
ctx.fillStroke(attr);
// Front side.
roundX = surface.roundPixel(center + halfWidth);
roundY = surface.roundPixel(top);
ctx.beginPath();
ctx.moveTo(center - halfWidth, bottom);
ctx.lineTo(center - halfWidth, roundY);
ctx.lineTo(roundX, roundY);
ctx.lineTo(roundX, bottom);
ctx.closePath();
bbox.x = center - halfWidth;
bbox.y = bottom;
bbox.width = halfWidth * 2;
bbox.height = top - bottom;
ctx.fillStyle = me.frontGradient.generateGradient(ctx, bbox);
ctx.fillStroke(attr);
}
});